5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / vulnerable-server.js JS
/**
 * Vulnerable PDF Generation Server
 *
 * This server demonstrates a realistic scenario where pdfmake
 * is used server-side with user-controlled document definitions.
 *
 * VULNERABILITY: User-controlled URLs in docDefinition trigger SSRF
 */

import express from 'express';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const pdfmake = require('pdfmake');

const app = express();
app.use(express.json({ limit: '10mb' }));

// Configure fonts - use standard PDF fonts (no external TTF needed)
pdfmake.setFonts({
  Helvetica: {
    normal: 'Helvetica',
    bold: 'Helvetica-Bold',
    italics: 'Helvetica-Oblique',
    bolditalics: 'Helvetica-BoldOblique'
  }
});

/**
 * VULNERABLE ENDPOINT
 *
 * Accepts user-controlled docDefinition which can contain
 * arbitrary URLs in images, attachments, or files fields.
 *
 * pdfmake will fetch these URLs without any validation!
 */
app.post('/api/generate-pdf', async (req, res) => {
  console.log('[SERVER] Received PDF generation request');

  try {
    const docDefinition = req.body;

    // Log what we received (for demo purposes)
    if (docDefinition.images) {
      console.log('[SERVER] Document contains images:', Object.keys(docDefinition.images));
    }
    if (docDefinition.attachments) {
      console.log('[SERVER] Document contains attachments:', Object.keys(docDefinition.attachments));
    }

    console.log('[SERVER] Creating PDF...');

    // Add default font to docDefinition if not specified
    if (!docDefinition.defaultStyle) {
      docDefinition.defaultStyle = { font: 'Helvetica' };
    } else if (!docDefinition.defaultStyle.font) {
      docDefinition.defaultStyle.font = 'Helvetica';
    }

    // THIS IS WHERE THE VULNERABILITY IS TRIGGERED
    // pdfmake.createPdf() will call URLResolver.resolve() for any URLs
    // in images, attachments, or files - NO VALIDATION
    const pdfDoc = pdfmake.createPdf(docDefinition);

    // Get the PDF as buffer (pdfmake 0.3.x API)
    const buffer = await pdfDoc.getBuffer();

    console.log('[SERVER] PDF generated successfully');
    res.contentType('application/pdf');
    res.send(buffer);

  } catch (error) {
    console.error('[SERVER] Error:', error.message);
    // Note: Even if PDF generation fails, the SSRF has already occurred!
    res.status(500).json({
      error: error.message,
      note: 'SSRF may have still been triggered before this error'
    });
  }
});

// Health check
app.get('/health', (req, res) => {
  res.json({ status: 'ok', vulnerable: true });
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log('='.repeat(50));
  console.log('VULNERABLE PDF GENERATION SERVER');
  console.log('='.repeat(50));
  console.log(`[*] Listening on http://127.0.0.1:${PORT}`);
  console.log('[*] POST /api/generate-pdf - Accepts docDefinition');
  console.log('[*] WARNING: This server is intentionally vulnerable!');
  console.log('');
});