# Content injection in Jira issue title enabling sending arbitrary POST request as victim

Unencoded Jira issue title in GitLab's Jira integration page allowed HTML/CSS injection that, via a DOMPurify-bypassing gadget and history-cache quirk, sent arbitrary POST requests as the victim.

- Payout: hidden
- Severity: high
- Program: GitLab
- Found by (vendor credit): joaxcar
- Found by: joaxcar
- Disclosed: 2022-09-22
- CVE: CVE-2022-1940
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/1533976
- Library page: https://bugbountylibrary.com/r/h1%3A1533976
- Tags: area:web domain:web impact:ato impact:privesc kind:bounty platform:hackerone sev:high tech:jwt technique:chained technique:race-window vuln:account-takeover vuln:csrf vuln:privilege-escalation vuln:xss

**Target:** GitLab Jira integration issue detail page (title field)
**Root cause:** The Jira issue title is rendered via v-safe-html without proper HTML encoding, and DOMPurify's default config lets a js-feature-highlight gadget with data-dismiss-endpoint through, enabling arbitrary POST requests.
**Steps:**
1. Set up a premium GitLab project with Jira integration enabled.
2. Create a Jira task titled `<img src=#>` and confirm it renders as broken image on the issue detail page (injection proof).
3. Create a Jira task titled `<a href=http:j15.se class=js-feature-highlight data-dismiss-endpoint='/-/profile/password?_method=put&user%5Bnew_password%5D=12345678&user%5Bpassword_confirmation%5D=12345678'>.</a><style>@import '/api/v4/projects/30205462/jobs/2304158115/artifacts/a.css`.
4. Victim opens the issue detail page, clicks the full-screen overlay link to http:j15.se which calls History.back(), returning to the page with cached data so main.js deferred gadgets fire.
5. Victim clicks again to trigger the gadget, sending the PUT to /-/profile/password setting password 12345678.
6. Attacker logs in with the OAuth email and new password (account takeover).
7. Admin variant: same gadget with data-dismiss-endpoint='/api/v4/users?admin=true&email=j@j15.se&name=h&username=hack&password=12345678&skip_confirmation=true' creates an admin when an admin visits.
**How found:** Prior research on GitLab DOMPurify gadgets (reports 1409788, 1481207) showed a full-XSS gadget requiring data present on initial load. The researcher noticed Jira issue data loads in a subsequent request, missing main.js's first run, then discovered that History.back() serves cached data fast enough to hit the deferred_execution gadgets.
**Exploitation:** Attacker with premium project and Jira integration injects HTML into a Jira issue title. Victim visits the issue page; a CSS-styled full-screen anchor redirects to an attacker page that calls History.back(), causing cached data to hit main.js deferred gadgets. A second click fires the js-feature-highlight gadget which POSTs to the data-dismiss-endpoint, either resetting an OAuth user's password (ATO) or creating an admin account.
**Escalation:** Chained HTML/CSS injection with a DOMPurify-bypassing JS gadget and a browser history-cache quirk to convert a non-initial-load injection into a working XSS-like arbitrary POST; payloads escalate to account takeover and admin account creation.
**Insight:** When a gadget requires data present at initial page load but the target fetches data asynchronously, try navigating away and using History.back() so the browser serves cached responses fast enough to race the deferred script execution.
