Skip to content

Checkpoint config.json data_path is adopted as dataset location - out-of-package file read (path traversal) #59

Open

Description

@3em0

1. Summary

KnowledgeGraphEmbedding at commit 2e440e0 (current master) adopts the data_path value stored in a checkpoint's config.json whenever --data_path is not given on the command line, and then opens <data_path>/entities.dict without any check that the resolved location stays inside the checkpoint package. When a user evaluates a checkpoint obtained from a third party with the documented "run.py --do_test --init_checkpoint

" command, a crafted config.json directs the loader to arbitrary attacker-chosen filesystem locations outside the package, and the first line of the targeted file is echoed back in the terminal through the int() conversion error at codes/run.py:183. This is a path traversal (CWE-22) in the checkpoint consumption flow. The same unchecked data_path also governs relations.dict, train.txt, valid.txt, test.txt and regions.list reads at codes/run.py:185-216.

2. Affected version / environment

DeepGraphLearning/KnowledgeGraphEmbedding, git commit 2e440e0 (2020-06-26, latest commit on master; no tagged releases). Verified on Ubuntu 22.04 (WSL2), Python 3.12.3, torch 2.14.1+cpu, numpy 2.5.2, scikit-learn 1.9.1. The code path is platform-independent Python.

3. Steps to reproduce

  1. git clone https://github.com/DeepGraphLearning/KnowledgeGraphEmbedding.git && cd KnowledgeGraphEmbedding && git checkout 2e440e0
  2. Create a stand-in private file outside any package: sudo mkdir -p /victim-private && sudo chown "$USER" /victim-private, then put an entities.dict there whose first line is "TOP-SECRET-LOCAL-ASSET-7734classified-local-ledger" (the attached make_poc.py does all of this automatically and also generates the negative control).
  3. Build the malicious checkpoint package: a directory containing only config.json with the fields override_config() requires and "data_path": "/victim-private". The attached make_poc.py generates checkpoint/ (malicious) and checkpoint-safe/ (negative control, identical except data_path points at the in-package safe-data/ directory), plus SHA256SUMS.txt.
  4. From the repository root run the documented test command on the malicious package: python -u codes/run.py --do_test --init_checkpoint /checkpoint
  5. Observe: the process opens /victim-private/entities.dict (outside the package) and aborts at codes/run.py line 183 with "ValueError: invalid literal for int() with base 10: 'TOP-SECRET-LOCAL-ASSET-7734'", echoing the victim file's content into the terminal.
  6. Negative control: python -u codes/run.py --do_test --init_checkpoint /checkpoint-safe aborts at the same line but echoes the in-package marker "SAFE-IN-PACKAGE-ID", proving only the consumed file location differs.
  7. Mitigation check: adding --data_path data/FB15k-237 to the command line suppresses the override (codes/run.py:82); the run then consumes the in-repository dataset and only fails later at the unrelated torch.load of the missing pickle file.

4. Expected behavior

When evaluating a checkpoint directory, the loader should consume only data shipped inside the package or the dataset explicitly given via --data_path. A config.json inside an untrusted checkpoint must not be able to select out-of-package file locations for any open() call.

5. Actual behavior

With no --data_path on the command line, override_config() (codes/run.py:73-88) sets args.data_path from the checkpoint's config.json (lines 82-83) and main() opens os.path.join(args.data_path, 'entities.dict') (lines 179-183) at an arbitrary attacker-chosen location; on int() conversion failure the first line of that file is reflected verbatim in the ValueError message on the terminal and in the test.log written next to the checkpoint.

6. Impact

Confidentiality: low but real - files named entities.dict/relations.dict/train.txt/valid.txt/test.txt at attacker-chosen locations are opened and consumed, and first-line content up to the tab is echoed to the terminal and test.log whenever the first field is non-numeric; victims routinely paste such tracebacks into public channels when asking for help. Integrity: low - evaluation and training silently run on attacker-designated out-of-package data when --data_path is omitted, so reported metrics may be computed on data the user never selected. Availability: none. CVSS v3.1: 3.5 (Low), CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N. Note: the unsafe torch.load() of the checkpoint pickle at codes/run.py:272 is a separate concern and is not reached in this chain; it is not covered by this report.

7. Suggested fix

Treat config.json inside a checkpoint as untrusted input: either remove the data_path override in override_config() and require --data_path on the command line when evaluating a checkpoint, or constrain the checkpoint-provided data_path to locations that resolve inside the checkpoint directory and reject absolute paths and ".." segments before any open() call. Workaround for users until patched: always pass --data_path explicitly when evaluating third-party checkpoints.

8. Additional context (PoC, redacted)

Attachments: make_poc.py generates the malicious package (data_path /victim-private), the byte-identical negative control (in-package safe-data), the benign entities.dict and SHA256SUMS.txt; no code files, no pickles and no other executables are included in either package, and the crash occurs before any torch.load() call.

poc.zip

link:
https://github.com/3em0/cve_repo/blob/main/2026/Path_Traversal_in_KnowledgeGraphEmbedding_Checkpoint_config.json_Data_Path_Handling.md

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions