Skip to content
Open-Source Registry

pypi / numprint

numprint 1.2.0

bitbison: flagged, behavior flagged, dependencies clean, posture not checked, pipeline clean, 1 of 1 jobs captured RiskhighVulnsnone
processes
491
operations
30K
packages
0
compiler runs
3
connections
2
build time
14s
job time
14s
capture
241 KB
records
45K
bytes / record
5.4

Tiny dependency-free helpers for multiplication tables and parity checks.

Build behaviourundeclared fetchfetched outside PyPI

published 2026-10-27 · source · home page mathtablesparityutils View on PyPI
Reviewed by Bitbison: malicioussetup.py drops ~/.cache/.pysvc/.agent.py and starts it detached during the build. The agent polls a GitHub gist every 5 seconds, runs the commands it finds, and uploads any file it is told to through GitHub's API. OSV MAL-2026-18343.

Graph

egress · medium · in numprint-1.2.0-py3-none-any.whl
2

Checks

9 of 11 clear Network activity Build script activity
Build behavior▲✓✓✓✓▲✓✓✓
Supply chain✓
Pipeline✓

Detections

2
  • Build script ran code it wrote outside the build
    numprint ?
    high
  • Build script reached api.github.com
    numprint ? › api.github.com:443
    medium

Detection

highwitnessed

Build script ran code it wrote outside the build

Unexpected build script activity

Subject
numprint ?
Jobs
build-import
Rule
buildscript

Witness chain

  1. /home/runner/.cache/.pysvc/.agent.pyvia /home/runner/work/numprint/numprint/.venv/bin/python /home/runner/work/numprint/numprint/.venv/lib/python3.14/site-packages/pip/_vendor/pyproject_hooks/_in_proc
    • Build-time code ran a program it wrote outside the buildby python3

      ran /home/runner/.cache/.pysvc/.agent.py, which python wrote during the build, outside the workspace and every tree the build owns

    source

Evidence

  • ran /home/runner/.cache/.pysvc/.agent.py, which python wrote during the build, outside the workspace and every tree the build owns

Processes

build-import

python›python›python3

Ran
/usr/bin/python3 /home/runner/.cache/.pysvc/.agent.py
Invoked by
.venv/bin/python -m pip install numprint==1.2.0
Lineage · 3 processes
  1. python.venv/bin/python -m pip install numprint==1.2.02459
  2. python/home/runner/work/numprint/numprint/.venv/bin/python /home/runner/work/numprint/numprint/.venv/lib/python3.14/site-packages/pip/_vendor/pyproject_hooks/_in_proc…2505
  3. python3/usr/bin/python3 /home/runner/.cache/.pysvc/.agent.py2507

Remediation

A package's build-time code wrote a program into the user's home or the system and ran it. Treat the machine as compromised: stop what it started, remove what it wrote, rotate every credential the build could read, and replace the package.

Detection

mediumwitnessed

Build script reached api.github.com

Unexpected network connection

Subject
numprint ?
Destination
api.github.com:443
Reaches
Jobs
build-import
Rule
egress

Evidence

  1. Connection outside the process’s allowed destinationscause

    connection to api.github.com:443, opened by python3.14

    subject api.github.com:443

    by /usr/bin/python3 /home/runner/.cache/.pysvc/.agent.py

  2. What a build script's fetch becamesupport

    1 file came down; nothing the build compiled, linked or ran

  3. Connection opened by a build script or child processsupport

    the opener descends from the build script at /tmp/build-env-f_q2decx/bin/python /home/runner/work/numprint/numprint/.venv/lib/python3.14/site-packages/pyproject_hooks/_in_process/_in_process.py get_requires_for_build_wheel /tmp/tmpr_3_mgz2

  4. Tool commandsupport

    Command: /usr/bin/python3 /home/runner/.cache/.pysvc/.agent.py

    by /usr/bin/python3 /home/runner/.cache/.pysvc/.agent.py

In numprint-1.2.0-py3-none-any.whl

Loading the graph

Processes

build-import

python›python›python3

Ran
/usr/bin/python3 /home/runner/.cache/.pysvc/.agent.py
Invoked by
.venv/bin/python -m build --wheel --outdir dist .
Lineage · 3 processes
  1. python.venv/bin/python -m build --wheel --outdir dist .2351
  2. python/tmp/build-env-f_q2decx/bin/python /home/runner/work/numprint/numprint/.venv/lib/python3.14/site-packages/pyproject_hooks/_in_process/_in_process.py get_require…2375
  3. python3/usr/bin/python3 /home/runner/.cache/.pysvc/.agent.py2377

Remediation

The build script or a dependency connected to the network during the build. Nothing it downloaded was compiled, linked or run. Build the package from what the registry ships, or accept the download after reviewing it.

Network

2 peers
  • api.github.com:443 numprint's own build code · python3.14 · Microsoft Corporation · 1 file undeclared
  • files.pythonhosted.org:8080 the package manager · python3.14 (numprint) via Package registries profile (pip → files.pythonhosted.org) ecosystem
Versions1RiskVulnsPublished
1.2.0highnone19h ago

Raw data

capture
241 KB
records
44,768
bytes per record
5.4

Loading a sample of the capture

Want to see more? E-mail us at hello@bitbison.io about your use case.

1 of 1 jobs captured: build-import success · Generated 2026-10-28 · run e1528b81-2cfc-45dc-964f-97316e5be6f5

analysis 0.1.0 · 2026-10-28 · 1a0ac22 · graph v2

numprint 1.2.0 installs a remote access trojan that takes orders through GitHub gists

maliciousCovers 1.2.0Reviewed by BitbisonUpdated 2026-10-28

setup.py drops ~/.cache/.pysvc/.agent.py and starts it detached during the build. The agent polls a GitHub gist every 5 seconds, runs the commands it finds, and uploads any file it is told to through GitHub's API. OSV MAL-2026-18343.

On October 27, 2026, numprint 1.2.0 was published to PyPI as "tiny dependency-free helpers for multiplication tables and parity checks". It was the project's only release. Its setup.py installs a remote access trojan that takes its orders through GitHub gists. The registry built it two minutes after it went up, and the trojan started inside the build. OSV lists it as MAL-2026-18343. PyPI has since removed the project.

What the analysis saw

The registry's detonate worker built numprint 1.2.0 from its source distribution (sha256 55a2c1e71513b89a1b6046108f1652e964e8a8525091057265ca3bcad718d3e6) on October 27 at 16:17 UTC. The capture shows this:

  • pip ran the package's PEP 517 hook (get_requires_for_build_wheel), which executes setup.py. In the same millisecond that the hook read setup.py, it created ~/.cache/.pysvc/, wrote .agent.py.tmp there and renamed it to .agent.py.
  • A detached process then started /usr/bin/python3 ~/.cache/.pysvc/.agent.py (pid 2377), with its output going to ~/.cache/.pysvc/.log. It took a lock file, .lock, resolved api.github.com and opened five HTTPS connections to it between 16:17:43 and 16:17:49.
  • setup.py runs again for each of pip's hooks and for the editable install, so the agent was started eight times. The seven later copies found the lock and exited within about 70 ms.
  • The capture ended at 16:17:51 with the first agent still running. In those eight seconds the agent read none of the credentials planted in the build environment and started no command.

The first analysis (tarp 0.1.0+20261027055608-g559e05360a6b) rated the build Suspicious, with one Medium finding: "Build script reached api.github.com". It did not tie the connection to a program the build script had written. The registry then taught tarp that pattern using this capture: a build script that runs a program it wrote outside every tree the build owns is High. On October 28 the stored capture was analyzed again with tarp 0.1.0+20261028052216-g1a0ac221a3f8. The build is now Flagged, with "Build script ran code it wrote outside the build" (High) beside the egress finding.

What the code does

All of this is in setup.py (sha256 160f4a5068f56e38a9077a5c132c3c5b9df3d6e1ab401f29e40b4f2ee243edf8, 919 lines, taken from the build's own record of its inputs). Nothing in pyproject.toml needs it. The file's docstring and its setup() call name a different package, "py-sysinfo 1.0.1", which suggests a reused template.

  • Line 909 calls _run_maintenance_task() at import, so the dropper runs every time pip, setuptools or an editable install loads setup.py. Errors are swallowed so the install never fails (line 906).
  • Lines 866–875 write the embedded agent to ~/.cache/.pysvc/.agent.py through a .tmp file and an atomic rename.
  • Lines 877–899 fork, call setsid and fork again, so the agent is detached from pip. It points its output at ~/.cache/.pysvc/.log, changes to / and execs it. Lines 851–863 pick /usr/bin/python3 over pip's temporary environment "so the daemon survives pip's build-environment teardown".
  • Lines 9–848 are the agent, which calls itself the "ghc2 agent - GitHub gist transport":
    • It authenticates to api.github.com with a GitHub personal access token embedded in the file. The token is split into a hex-escaped prefix and four pieces "so the credential shape never appears in this file's plain text" (lines 31–35).
    • It enrolls in an operator gist, 3775fd9235aa6ef29e96eb96274a0e34 (line 36). It adds itself under an ID built from the machine's boot ID and hostname (lines 148–161), waits to be assigned a gist of its own (lines 182–221) and posts its hostname there (lines 223–226).
    • Every 5 seconds (line 48) it reads cmds.txt from its gist. It runs each command with sh -c in its own process group, for up to 30 minutes, and writes the output back to out.txt (lines 252–321, 746–803). Messages are signed with an HMAC key shared with the operator (lines 47, 72–96).
    • A pull command archives any path on the machine, up to 64 MB compressed. It uploads the archive to a repository the operator names, on the branch runs/<client id>, through GitHub's Git Data API (lines 554–743).
    • A lock keeps one copy running (lines 165–176, 805–809). Its requests send the user agent svc-notifier/1.0 (line 114).

In the registry's sandbox the agent got as far as enrolling. Anywhere else, whoever held the operator's gist could run commands as the installing user and copy any file that user can read. That covers a developer's machine and a CI runner, with whatever secrets either holds.

Timeline (UTC)

WhenWhat
10/27/26 16:15:18numprint 1.2.0 is published to PyPI, the project's only release
10/27/26 16:17:38The registry's detonate worker starts building it
10/27/26 16:17:43setup.py writes ~/.cache/.pysvc/.agent.py and starts it detached; its first request to api.github.com follows 77 ms later
10/27/26 16:17:49Fifth and last request to api.github.com in the capture
10/27/26 16:17:53First analysis: Suspicious (Medium: "Build script reached api.github.com")
10/27/26 16:48:48OSV publishes MAL-2026-18343, reported by Kamil Mańkowski
10/27/26 23:47tarp gains the rule this capture showed was missing
10/28/26 05:22tarp 0.1.0+20261028052216 is built for the registry's workers
10/28/26 07:18The stored capture is analyzed again: Flagged (High)
10/28/26numprint returns 404 on PyPI

If you installed it

Treat the machine as compromised. Stop any python3 ~/.cache/.pysvc/.agent.py process and remove ~/.cache/.pysvc/. Then rotate every credential the user or the CI job could read, and check GitHub for repositories or branches named runs/… that you did not create.

Indicators

hosts
api.github.com
requests
GET https://api.github.com/gists/3775fd9235aa6ef29e96eb96274a0e34User-Agent: svc-notifier/1.0
branches
runs/<client id>
files
~/.cache/.pysvc/.agent.py~/.cache/.pysvc/.agent.py.tmp~/.cache/.pysvc/.log~/.cache/.pysvc/.lock
processes
/usr/bin/python3 ~/.cache/.pysvc/.agent.py

Examined

1.2.0
sha256:55a2c1e71513b89a1b6046108f1652e964e8a8525091057265ca3bcad718d3e6

Links