# Potential unprivileged Stored XSS through wp_targeted_link_rel

wp_targeted_link_rel() regex parses rel regardless of attribute position, injecting a quote into a double-quoted title attribute to achieve stored XSS via user bio.

- Payout: hidden
- Severity: high
- Program: WordPress
- Found by: simonscannell
- Disclosed: 2020-01-08
- CVE: CVE-2019-16773
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/509930
- Library page: https://bugbountylibrary.com/r/h1%3A509930
- Tags: area:web domain:web impact:integrity kind:bounty platform:hackerone sev:high tech:php technique:parser-differential technique:source-review vuln:xss

**Target:** WordPress core wp_targeted_link_rel() filter / BuddyPress user bio
**Root cause:** wp_targeted_link_rel_callback() regex-matches rel anywhere in the attribute string, ignoring whether it sits inside another quoted attribute; when rel has no delimiter it defaults to a double quote and str_replace injects it, breaking out of the enclosing attribute.
**Steps:**
1. Enable BuddyPress member bio display (Appearance -> Customize -> BuddyPress Nouveau -> Member front page).
2. Create a normal forum user and edit profile.
3. Set user description to `<a href="#" title=" target='abc' rel= onmouseover=alert(/XSS/) ">This is a PoC for a Stored XSS</a>`.
4. Visit profile and hover the link to trigger the injected onmouseover handler.
**How found:** Code review of wp_targeted_link_rel() regex: noticed the rel regex ignores attribute position and that the empty-delimiter fallback to a double quote would inject a quote when rel sits inside another quoted attribute; also noted kses ordering makes user description the exploitable sink.
**Exploitation:** The rel=abc without delimiter inside a double-quoted title causes the callback to substitute rel="abc", injecting a stray double quote that closes title and lets the trailing onmouseover attribute execute on hover. User description is the only path where kses runs before wp_targeted_link_rel, so the injected attribute survives sanitization.
**Escalation:** Injecting a style attribute to make the link span the whole page turns it into a wormable stored XSS in BuddyPress; any plugin/forum rendering user description is affected.
**Insight:** When a sanitizer re-serializes a single attribute via regex+str_replace, test placing that attribute inside another quoted attribute's value; the delimiter-defaulting fallback can inject a quote and break out. Also check filter ordering (kses before vs after) to find which sinks survive sanitization.

