VNX-345: Insufficient Verification of Data Authenticity
Python code loads request, socket or downloaded bytes with marshal, executes code fetched over the network, or runs a downloaded file, with no signature or digest check in between.
| Field | Value |
|---|---|
| Rule ID | VNX-345 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Python |
| CWE | CWE-345 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | data-authenticity cwe-345 deserialization python |
| Source | internal/sast/rules/vnx-345.rego |
Overview
VNX-345 maps to CWE-345: Insufficient Verification of Data Authenticity. It reports Python code that acts on data from an untrusted channel as though it were authentic:
marshal.load()/loads()(also through animport marshal as malias orfrom marshal import loads) applied to request data (body, form, args, cookies, files, headers), bytes read from a socket, or a body downloaded with requests, httpx orurlopen(), inline or through a variable assigned from it (one further assignment, such as a base64 decode, is followed) within 12 lines;exec()/eval()of content fetched with requests, httpx orurlopen();- a file downloaded with
urlretrieve()and then run withsubprocess,os.systemoros.exec*.
Why This Matters
Bytes from a cookie, a form or a socket are written by whoever sent them. marshal builds code objects from them; executing downloaded code hands the process to whoever controls the server or the connection. Without an HMAC, a signature or a pinned digest, nothing establishes that the data came from where the code assumes.
Not reported: when the 15 lines above verify an HMAC or signature (hmac.compare_digest, hmac.new, itsdangerous signers, .verify()) or a downloaded file’s hash; json.loads and ast.literal_eval; marshal of local files or of the program’s own dumps(); comments and string literals.
Unpickling untrusted bytes is the same weakness, so it is reported once, by the rule that owns the call: pickle, cPickle, _pickle, dill, cloudpickle and jsonpickle are reported by VNX-PY-003, and the loaders that unpickle under the hood (torch.load, joblib.load, pandas.read_pickle, numpy.load(..., allow_pickle=True)) by VNX-PY-013, wherever the bytes come from.
What Gets Flagged
raw = base64.b64decode(request.form["code"])
code = marshal.loads(raw)
exec(requests.get(f"https://plugins.example.com/{name}.py").text)
path, _ = urllib.request.urlretrieve("https://downloads.example.com/agent.sh", "/tmp/agent.sh")
subprocess.run(["sh", path], check=True)
Remediation
- Exchange data in a format that cannot carry code (JSON) and validate it.
- When a serialised object must round-trip through a client, sign it (itsdangerous,
hmac.newover the bytes) and verify withhmac.compare_digestbefore loading. - Pin downloaded code by SHA-256 or verify its signature before executing it.
References
- CWE-345: Insufficient Verification of Data Authenticity
- CWE-502: Deserialization of Untrusted Data
- CWE-494: Download of Code Without Integrity Check
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-345
// nosec vnx-345
Or record an ignore rule: vulnetix ignore add --rule vnx-345 --reason "...". See Suppressing findings.