What Happened
A Java application using snakeyaml before version 1.26 processed a malicious YAML file, triggering a Denial of Service vulnerability. The attack exploited YAML's entity expansion feature, specifically the "billion laughs" pattern, where nested entity references multiply exponentially during parsing. This caused the application to consume all available memory and CPU, leading to service unavailability.
The vulnerability arose from how older snakeyaml versions handled recursive entity definitions. The parser expanded these nested structures fully before validation, allowing a small payload to inflate into gigabytes of in-memory data.
Timeline
- T+0: Application receives YAML input with nested entity references.
- T+0.3s: Parser begins expanding entities recursively.
- T+2s: Memory consumption spikes from 2GB to 14GB.
- T+4s: JVM triggers garbage collection, CPU utilization hits 100%.
- T+8s: Application becomes unresponsive to health checks.
- T+12s: Load balancer marks instance unhealthy, traffic fails over.
- T+15s: Remaining instances receive redirected traffic, exhibiting the same behavior.
- T+45s: All application instances down, service outage declared.
The attack required no authentication or prior access—just the ability to submit YAML input through any exposed endpoint.
Failed or Missing Controls
Dependency Management: The team used snakeyaml 1.23, released in 2018. The fix for the billion laughs vulnerability was in version 1.26, released in 2020. Without a process to track or prioritize library updates, the vulnerable version remained in production for over three years after the patch was available.
Input Validation: The application parsed YAML without size or complexity limits. There were no checks for entity depth, expansion ratio, or total memory consumption during parsing. The code assumed well-formed input and relied entirely on the library for validation.
Resource Constraints: The JVM ran with -Xmx16g but lacked parsing-specific memory limits. A single malicious request could consume the entire heap. No timeout existed for YAML parsing operations, allowing the parser to run until completion or crash.
Monitoring and Alerting: Memory alerts triggered only at 90% utilization. By the time the first alert fired, the application was already unresponsive. No metrics tracked parsing duration, entity expansion depth, or abnormal YAML characteristics.
Blast Radius Containment: All instances shared the same vulnerable code path. No circuit breaker prevented cascading failures. The load balancer's health check interval (10 seconds) meant unhealthy instances continued receiving traffic for several seconds after becoming unresponsive.
Relevant Standards
OWASP ASVS v4.0.3 Requirement 5.1.3 mandates that applications verify input conforms to a positive specification before processing. This includes structural validation—checking nesting depth, collection sizes, and reference counts before parsing. The application violated this by parsing first and validating (if at all) second.
OWASP ASVS Requirement 1.14.2 requires dependency checking as part of the build process. You must identify components with known vulnerabilities and either update them or document accepted risk. Running a three-year-old library with a published CVE fails this requirement entirely.
PCI DSS v4.0.1 Requirement 6.3.2 states that software components must be inventoried and kept up to date to address vulnerabilities. For in-scope systems processing payment data, this isn't optional—it's a validation failure.
NIST 800-53 Rev 5 Control SI-10 (Information Input Validation) requires checking information inputs for accuracy, completeness, and validity. Accepting unbounded entity expansion violates the control's intent.
ISO/IEC 27001:2022 Control 8.24 (Use of Cryptography) doesn't directly address parsing, but its principle applies: validate data before processing it through complex operations. YAML parsing is complex. Entity expansion is complex. You validate first.
Lessons and Action Items for Your Team
Implement Automated Dependency Scanning in CI/CD: Use tools like Snyk, Dependabot, or OWASP Dependency-Check on every commit. Configure them to fail builds when high-severity vulnerabilities appear in direct dependencies. Set a policy: critical CVEs get patched within 7 days, high-severity within 30 days. Track your remediation time as a team metric.
Update snakeyaml to Version 1.26 or Higher Immediately: If you're on 1.33 (current as of this writing), you're good. If you're on anything older, you're exposed. Run mvn dependency:tree | grep snakeyaml or gradle dependencies | grep snakeyaml now. Check transitive dependencies too—another library might pull in the vulnerable version.
Add Pre-parsing Validation for All YAML Inputs: Before calling yaml.load(), check file size (reject anything over 1MB unless you have a specific reason), count line breaks (reject files with >10,000 lines), and scan for suspicious patterns like repeated & or * characters that indicate entity references. This catches attacks before they hit the parser.
Set Resource Limits on Parsing Operations: Wrap YAML parsing in a try-with-resources block with a timeout (2 seconds is generous for legitimate YAML). Configure a separate thread pool with limited heap allocation for parsing tasks. If parsing exceeds limits, kill the operation and return a 400 error.
Create a Dependency Review Process for New Libraries: Before adding any dependency, check its age, maintenance status, and vulnerability history. Prefer libraries with active maintainers and recent commits. Document why you chose each library and what alternatives you considered. Review this documentation during security assessments.
Monitor Parsing Operations in Production: Add metrics for YAML parsing duration (p50, p95, p99), input size distribution, and parsing failures. Alert when p99 exceeds 500ms or when failure rate exceeds 1%. These signals indicate either attack attempts or legitimate traffic patterns you need to optimize for.
Test Your YAML Endpoints with Malicious Payloads: Add the billion laughs pattern to your security test suite. Include deeply nested structures, circular references, and oversized inputs. If your application accepts YAML from untrusted sources, these tests belong in your regression suite—run them on every deploy.
The fix for this specific vulnerability is a one-line version bump in your build file. The fix for the class of problems it represents—outdated dependencies creating exploitable attack surfaces—requires process changes that most teams still haven't implemented. Which kind of fix are you implementing today?



