import telnyx in jadoonf/pypi-analysis-feed, run 23662517211, after Telnyx PyPI 4.87.2 landed. The record shows the import spawning a child Python process, opening TCP egress to 83.142.209.203:8080, and reparenting to systemd(1) before the workflow step ended. The step exited; the child outlived its parent.Public reporting documents the technique — Telnyx PyPI 4.87.1 and 4.87.2 shipped with a backdoor in _client.py that fires at import time, spawning a base64-decoded child Python process and reusing keys from earlier campaigns (Aikido). The run below imports Telnyx on a Garnet-instrumented runner; the focus from here is what the record shows when an import-time hook spawns a detached child.
Execution lineage
Run 23662517211 · jadoonf/pypi-analysis-feed
Analyse local PyPI archives
The lineage above contains both versions side by side. Version 4.87.1 crashed on import (NameError: setup defined, Setup called) before any payload could execute — the record captures the import attempt and two early signals, but no child process or network activity followed. Version 4.87.2 succeeded: _client.py fired at import time and spawned a base64-decoded child Python process that opened the C2 socket and then reparented to systemd(1) after the parent exited.
Assertions flagged
| Check (class) | Granular id | Result | Evidence |
|---|---|---|---|
| Network egress | no_bad_egress_domain | fail | One persistent TCP connection from 10.1.0.106:50584 → 83.142.209.203:8080, status ongoing on the base64-decoded child Python process. |
What was observed
Process ancestry. The 4.87.2 branch carries the import-time backdoor: _client.py fires under python3.11(2657), which spawns a base64-decoded python3.11(2659) child. The captured command args of the child partially decoded to import subprocess, import tempfile, import os, import base64, import s... (truncated at the sensor's field-length limit) — consistent with a network-capable payload that stages files to disk. The 4.87.1 branch terminates at _client.py on the NameError. The version comparison:
4.87.1 | 4.87.2 | |
|---|---|---|
| Import / trigger | Crashes on NameError (setup vs Setup) | Backdoor runs at import |
| Child process | None | Base64-decoded Python subprocess |
| Network | None observed | TCP to 83.142.209.203:8080 |
| Orphaning / persistence | N/A | Child reparented under systemd |
On the same ancestry the record carries code_on_the_fly (dynamic code execution via base64 decode in the child Python process), interpreter_shell_spawn (Python spawning a subprocess at import time), and exec_from_unusual_dir (the child running from hostedtoolcache). On the 4.87.1 branch, code_on_the_fly and exec_from_unusual_dir fired on the import attempt before the NameError halted execution.
Network. Six flow entries on 10.1.0.106:50584 → 83.142.209.203:8080/TCP, all sharing a single source port: one persistent connection observed across the sensor's polling intervals (status ongoing — TCP established, not just a SYN). The 4.87.1 branch produced no outbound traffic.
File / memory access. The record contains no file writes, no credentials_files_access, and no additional outbound flows from the orphaned child — the outbound destination was reachable but did not serve payloads at run time.
Notable absences. No binary_self_deletion on either branch. No file writes from the orphaned child during the ~8 seconds it ran under systemd(1). The expected later-stage harvest is absent from the record — the payload stalled at the network call. The record captures what ran, not what was intended.
python3.11(2657) (the import process) terminated, but python3.11(2659) continued running, reparented to systemd(1). The record contains this ancestry shift directly: at 18:55:47Z the process sat under the full Runner.Worker chain; by 18:55:55Z it reported to systemd — the workflow step's exit code says nothing about it.
Analysis
The interesting property of this record is the ancestry shift: a process started under the workflow tree continued running after the parent step exited, reparented to systemd(1), and held an open TCP connection across that boundary. The headline import telnyx produced one signal in logs (exit 0) and a structurally different signal in the lineage (a child still running after the step ended). The record is what makes the handoff legible.
Garnet records process ancestry, file access, and network egress straight from the kernel for every workflow run by the Garnet GitHub Action. Import-time triggers, base64 child spawns, outbound destinations, and process orphaning all appear in the same artifact.
More field notes: LiteLLM · TanStack runtime profiles · Axios.