How I Found 2 Critical Vulnerabilities in an AI-Driven SaaS
A deep dive into Prompt Injection and Sensitive Information Disclosure within an AI-powered resume optimizer.
1. The Friday Night “Recon”
It was a Friday night. I was exhausted after an unsuccessful Bug Bounty hunt on Intigriti, scrolling through X (formerly Twitter) to wind down. I came across a developer sharing his SaaS results and interacting with the “Build In Public” community—a movement where devs share their progress, challenges, and metrics openly.
The project was CvPorVaga, an AI-driven solution designed to optimize resumes using ATS (Applicant Tracking System) filters.
As a researcher, my “hacker 101” instincts immediately kicked in when I saw the PDF upload feature. However, since every professional pentest requires authorization, I reached out to Sérgio Banhos (the founder). I asked for permission to conduct a quick, pro-bono security audit to apply some TTPs (Tactics, Techniques, and Procedures) I’d been studying. Sérgio was incredibly supportive and even set up a staging environment so I could have full freedom to test without impacting production.
2. Reconnaissance & Enumeration
Since I had the green light to be noisy in a controlled environment, I started by identifying the tech stack. The SaaS runs on Next.js and React. This immediately told me that basic, “low-hanging fruit” XSS (Cross-Site Scripting) payloads likely wouldn’t work due to the way Next.js handles component rendering and routing.
My focus shifted: I needed to find flaws in the business logic.
I fired up Caido and began intercepting every request. When auditing web platforms, I always look closely at POST requests—specifically checking endpoints, ensuring Content-Type: application/json is enforced, and testing for hidden parameters in the request body.
I started manual testing on the /dash/cv/new endpoint. The request sent a JSON body with a step field set to welcome. I attempted to use this as an attack surface for directory traversal or server-side exfiltration. However, the presence of the Vary: rsc, next-router-state-tree... headers confirmed that the application was heavily reliant on Next.js internal routing, which is generally resilient against OS-level interactions unless there’s a massive misconfiguration.
3. Exploiting the AI Engine (The Turning Point)
After an hour of hitting 404s and seeing consistent content-length responses (a sign of a well-secured perimeter), I was getting frustrated. But I remembered a principle often discussed in the Brazilian bug-hunting community: “When a system is technically hardened, business logic flaws become the path of least resistance.”
I stepped back to analyze the core value proposition of CvPorVaga: it processes PDFs using AI to evaluate resumes. That was my “Aha!” moment. I had recently researched vulnerabilities involving LLM (Large Language Model) integration, and this was the perfect sandbox to test them.
Where the Fun Begins 😈
The site takes a PDF and uses an LLM to extract keywords like “Name,” “Professional Experience,” and “Education.” I inferred that if the frontend was protected by Next.js, the backend might still be vulnerable when it processes the content of the PDF and renders the AI’s output back to the screen.
I created a malicious PDF blending fake resume data with an XSS payload. My hypothesis: the backend would process the PDF, the AI would extract the “name” (my payload), and the application would render it in the ATS result screen without proper sanitization.
I was wrong—but in a way that led to something better.
The AI model wasn’t “dumb” enough to just spit out the code. It actually flagged the attempt as Code Injection. While the XSS failed, this confirmed a vital piece of information: the processed output was being printed directly into the summary field of the response endpoint. I now had a direct line of communication with the LLM.
Exploiting the Logic
I moved to LLM01:2025 Prompt Injection. I tried standard context splitting and l33t speak, but the model held firm.
Then, I turned to Social Engineering for AI. Models are often sensitive to “urgency” and “authority” within their context window. I modified the PDF to include legitimate-looking data followed by a forceful instruction, assuming the model might interpret everything after a certain point as a developer command.
Bingo. I forced the model to ignore the resume content and return a perfect “100” score, confirmed by the presence of my “TECH-GOLD-2026” string in the summary. This worked because the model interpreted the instructions following the user data as a system-level override.
Escalating to Information Disclosure
Adrenaline kicked in. If I could hijack the logic, could I leak the “brain” of the operation? My next goal was to extract the System Prompt—the hidden instructions Sérgio wrote to tell the AI how to behave.
I added a layer of “critical scenario” fiction to the PDF, commanding the LLM to reveal its original instructions for “debugging purposes.”
Bingo again. The model leaked the system prompt. I had successfully achieved:
-
LLM01: Prompt Injection (Business Logic Hijack)
-
LLM02: Sensitive Information Disclosure (System Prompt Leakage)
I decided to stop there. It was 11 PM on a Friday, and I had two critical findings to report. I attempted to exfiltrate API keys or backend tech details, but those were properly secured.
Conclusion
As highlighted in the latest OWASP Top 10 for LLM, the most interesting attack surface in modern apps is the interaction with AI. That is where the business logic resides today. Even if there are no obvious IDORs or SQLis, the way an application handles AI-generated data is often the weakest link.
I want to thank Sérgio for the opportunity. He acted quickly, and both vulnerabilities have been patched. CvPorVaga remains a top-tier tool for resume optimization, now with a significantly hardened security posture.
Until the next hunt. :)








