Skip to content

agent/build.sh: ByteBuddy jar is verified then reread — a swap during the build window bypasses the #43 checksum check #48

Description

@ghostpsalm

Found by the breaker on lap 2 while landing #43 (ByteBuddy checksum verification).

Where

agent/build.sh hashes $BB_JAR once (sha256sum -c -), then reads the same path again later: javac -cp "$BB_JAR:$BUILD/boot" ... (compile) and unzip -oq "$BB_JAR" 'net/*' (shade into the shipped agent jar). Nothing pins the bytes verified to the bytes consumed — it's classic verify-then-reread.

Why it matters

Demonstrated: sandbox copy of build.sh, a real (hash-matching) ByteBuddy jar in lib/, and a javac wrapper that swaps $BB_JAR for a poisoned copy immediately after the checksum step runs but before unzip shades it in. Result: build exits 0, and dreamconnect-agent.jar contains a class (net/PWNED.class) that was never hashed. The adversary this defends against — issue #43's "poisoned local cache" — only needs write access to agent/lib/ timed to that window, not to the whole checkout (which would already be a bigger compromise the pin can't help with anyway — that part was already ruled out in lap 1).

Related, same root cause (no locking/staging around agent/lib/): two concurrent build.sh invocations can race on the same file — one process's checksum-mismatch cleanup (rm -f, added in #43's follow-up fix) can delete a jar another process just verified and is about to consume. Low severity, self-healing on re-run, but same fix would address both.

Why not fixed alongside #43

Closing this needs a design decision, not a mechanical change — e.g. stage the verified jar into an immutable build-local copy right after the hash check and consume only that copy, or hold a lock (flock) around the fetch-verify-consume sequence. Either is more than a one-line diff and deserves its own review.

Suggested fix

After verification passes, copy $BB_JAR into $BUILD (e.g. $BUILD/byte-buddy-verified.jar) before any other file in $LIB could plausibly change, and have javac/unzip reference only that copy from then on. This also incidentally fixes the concurrent-build race, since each build gets its own copy under its own $BUILD tree.

Confidence: Confirmed (mutant demonstrated). Severity: Low probability (requires a locally-writable agent/lib/ and precise timing, or a genuinely concurrent second build) but it is the residual gap in the exact control #43 added, so worth closing deliberately rather than by accident.

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

    audit:securityFinding from a security audit

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions