BugForge - Weekly - Fur Hire
Weekly - FurHire
Vulnerabilities Covered:
WAF Bypass via Obscure Event Handler (XSS)
Cross-Site Request Forgery (CSRF) - Password Takeover
Summary:
This walkthrough demonstrates a chained attack against a job recruitment application protected by a WAF. The application reflects unsanitised input in the application status field, but the WAF blocks common XSS payloads and event handlers such asonerrorandalert. By switching to the lesser-knownoncontentvisibilityautostatechangeevent handler on an<img>tag, the WAF filter is bypassed entirely. With XSS execution confirmed, cookie theft is ruled out because the session cookie carries theHttpOnlyflag. Instead, the password change endpoint is found to require neither a CSRF token nor the current password, and it honours same-origin fetch requests. A base64-encoded fetch payload is injected into the status field, which silently changes the job seeker’s password when they view an accepted application notification. Logging in with the new password retrieves the flag.
Reference:
Bugforge.io
PortSwigger - XSS Cheat Sheet
Solution
Step 1 - Application Analysis
The application is a job recruitment platform with two distinct roles: job seeker and recruiter. To map the attack surface, two accounts were created - one job seeker and one recruiter. The recruiter posts a job listing and the job seeker applies for it.
After the recruiter accepts the application, a notification is sent to the job seeker. This notification path is the key: whatever the recruiter writes in the status field when accepting an application is reflected back to the job seeker. This is the candidate injection point.
Step 2 - Finding the Injection Point
The application status is set via a PUT request to /api/applications/:id/status. Testing with a basic HTML tag confirms that the value is rendered in the browser without sanitisation - the response reflects the raw HTML.
Initial XSS attempts using common event handlers are immediately blocked.
Step 3 - WAF Analysis
With basic XSS confirmed as blocked, the WAF rules were probed systematically. Common event handlers such as onerror, onload, and onclick are all blocked. JavaScript function calls like alert() are also filtered.
The WAF appears to maintain a blocklist of well-known event handlers. The strategy is to find an event handler that is valid in modern browsers but not present in the WAF’s blocklist.
Step 4 - WAF Bypass via Obscure Event Handler
Consulting the PortSwigger XSS cheat sheet surfaced oncontentvisibilityautostatechange, a relatively new event that fires when an element’s content visibility state changes. This event handler is not in the WAF’s blocklist.
The handler requires style=display:block;content-visibility:auto on the element to trigger automatically. Since alert is blocked by the WAF, execution was confirmed using both print() and a blind XSS payload pointing to an out-of-band callback server.
1
<img oncontentvisibilityautostatechange=print() style=display:block;content-visibility:auto>
The print dialog fires when the job seeker views the application, confirming client-side JavaScript execution through the WAF.
With bypass confirmed, a blind XSS payload was injected into the application status field to verify execution in the job seeker’s browser context via an out-of-band callback.
Step 5 - Ruling Out Cookie Theft
The natural next step after confirming XSS is to attempt to steal the session cookie. Inspecting the job seeker’s session cookie reveals it is flagged as HttpOnly, meaning it is not accessible to JavaScript.
Cookie theft is not viable. A different impact vector is needed.
Step 6 - CSRF via Password Change
Inspecting the application for sensitive state-changing endpoints, the password update endpoint was identified: PUT /api/profile/password. Testing the endpoint revealed:
- No CSRF token is required
- The current password is not required
- The endpoint accepts a
newPasswordfield in the JSON body - Same-origin
fetchrequests from XSS are honoured
This means the XSS payload can silently call the password change endpoint on behalf of the job seeker, setting a known password and enabling account takeover.
To bypass the WAF’s filtering of the JSON body, the payload is base64-encoded and decoded at runtime using atob().
The JSON payload {"newPassword":"Zwarts"} encodes to eyJuZXdQYXNzd29yZCI6Ilp3YXJ0cyJ9.
The final exploit payload:
1
<img oncontentvisibilityautostatechange=fetch('/api/profile/password',{'method':'PUT','headers':{'Content-Type':'application/json'},'body':atob('eyJuZXdQYXNzd29yZCI6Ilp3YXJ0cyJ9')}) style=display:block;content-visibility:auto>
Step 7 - Triggering the Attack
The malicious payload is submitted as the application status when accepting Jeremy’s (the job seeker’s) application. When Jeremy views the accepted application notification, the XSS fires in their browser context and silently issues the password change request.
Logging in as Jeremy with the new password password2 succeeds, confirming the account takeover and retrieving the flag.
Impact
- Full account takeover of a job seeker account through a blind XSS triggered by a job application, with no interaction required beyond the job seeker viewing a notification
- The WAF provides a false sense of security - a single unlisted event handler is sufficient to bypass it entirely
- The absence of CSRF tokens and current-password verification on the password change endpoint turns reflected XSS into an account takeover primitive
HttpOnlycookies alone are insufficient protection when CSRF on sensitive endpoints is left unaddressed- An attacker with job seeker access gains visibility over all applications, candidate data, and any privileged actions available to that role
Vulnerability Classification
WAF Bypass (XSS)
- OWASP Top 10: A03:2021 - Injection
- Vulnerability Type: Stored XSS with WAF Bypass via Obscure Event Handler
- Attack Surface:
PUT /api/applications/:id/status- status field reflected to job seeker and job seeker without sanitisation - CWE:
- CWE-79 - Improper Neutralisation of Input During Web Page Generation (XSS)
- CWE-693 - Protection Mechanism Failure
CSRF - Password Takeover
- OWASP Top 10: A01:2021 - Broken Access Control
- Vulnerability Type: Cross-Site Request Forgery on Password Change Endpoint
- Attack Surface:
PUT /api/profile/password- no CSRF token, no current password verification - CWE:
- CWE-352 - Cross-Site Request Forgery
- CWE-620 - Unverified Password Change
Root Cause
WAF Bypass: The WAF relies on a blocklist of known-bad patterns. Blocklist-based filters are fundamentally incomplete - any event handler or JavaScript construct not present in the list passes through unchecked. The root cause is trusting a perimeter control rather than fixing the underlying output encoding. The status field is stored and rendered as raw HTML, which is the real vulnerability. The WAF only delays exploitation.
CSRF: The password change endpoint performs no origin verification beyond relying on the browser’s same-origin policy for cookies. Because there is no CSRF token and no requirement to supply the current password, any JavaScript running in the user’s browser context can issue an authenticated password change. When combined with XSS, this collapses account security to a single injection point.
Remediation
XSS / WAF Bypass:
- Fix the root cause: HTML-encode all user-supplied output before rendering it in the browser; do not rely on the WAF as the primary defence
- Use a Content Security Policy (CSP) to restrict which scripts and event handlers can execute, reducing the impact of any bypass
- Transition the WAF from a blocklist to an allowlist model where only explicitly permitted patterns are accepted
- Validate and sanitise the
statusfield server-side to a set of known safe values (e.g.,accepted,rejected) rather than accepting freeform HTML
CSRF - Password Change:
- Require the user’s current password as part of any password change request, ensuring the requester possesses the credential
- Add a per-session CSRF token to all state-changing endpoints and validate it server-side on every request
- Set the session cookie’s
SameSiteattribute toStrictorLaxto prevent cross-origin requests from carrying credentials - Audit all sensitive endpoints (email change, password reset, role update) for the same missing CSRF protection














