pwnlibc

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2026 License: MIT

README

pwnlibc

An all-in-one glibc version manager for CTF/pwn work: download, identify, diff, patch, and build glibc versions — inspired by glibc-all-in-one, reimplemented from scratch in Go, and fully Dockerized so you never need a Go toolchain installed locally.

./pwnlibc.ps1 download 2.31-0ubuntu9.9_amd64
./pwnlibc.ps1 patch workdir/chall
./pwnlibc.ps1 identify workdir/libc.so.6

Why a rewrite

The original tool is a great reference implementation, but it shells out to pyelftools/readelf, downloads sequentially with no checksum verification, and requires a local Python environment. This project is a clean-room reimplementation of the same idea — not a port of its code — built around:

  • No local toolchain, ever. Everything — build, test, lint, run — happens through Docker. go build never runs on your machine.
  • Reliability. SHA256-verified downloads, path-traversal/zip-slip-safe extraction, decompression-bomb caps, retrying mirrors with a per-session circuit breaker, and a PROVENANCE.json audit trail per downloaded version.
  • Performance. Native Go debug/elf parsing (no subprocess calls), concurrent mirror racing, and a local bbolt index for O(1) lookups.
  • A few extra tools the original doesn't have: patch (pwninit-style auto-patch), run (disposable repro container with gdb), bundle (air-gapped export/import), vuln (known-CVE lookup), doctor (environment self-check).

Install

Three ways to get pwnlibc, in order of how much you want Docker to manage for you:

git clone https://github.com/0xCyb3rgh0st/pwnlibc.git
cd pwnlibc
./pwnlibc.sh mirror update          # or ./pwnlibc.ps1 on Windows
./pwnlibc.sh download 2.31-0ubuntu9.9_amd64
./pwnlibc.sh identify libs/2.31-0ubuntu9.9/amd64/libc.so.6
2. Prebuilt binary (GitHub Releases)

Every tagged version is cross-compiled for linux/darwin/windows (amd64 + arm64) by CI (.goreleaser.yml, driven by the release-binaries job in .github/workflows/ci.yml) and attached to that tag's GitHub Release as ready-to-run archives — download the one for your OS/arch, extract, and run pwnlibc. These are plain binaries: patch needs patchelf on your PATH and build/run need a local Docker daemon, since neither is bundled outside the container image.

3. go install (if you already have Go)
go install github.com/0xCyb3rgh0st/pwnlibc/cmd/pwnlibc@latest

Same caveat as the prebuilt binaries: patch/build/run expect patchelf/docker to already be on your machine.

The first Docker run builds the pwnlibc:latest image; after that, every command is just docker compose run --rm cli <args> under the hood. Downloaded glibc versions land in ./libs, persisted on the host. Drop challenge binaries into ./workdir before running patch/run against them — those two commands launch nested containers via the host Docker socket, and can only reach files under ./libs or ./workdir (see "How build/run work" below).

Commands

Command What it does
mirror list / mirror update List/refresh the apt mirrors (tuna, ustc, ubuntu-archive, old-releases, plus any custom ones from config).
search <query> Fuzzy-search available versions.
search --libc <path> --symbol <name|glob> Local symbol offset lookup / glob match.
search --libc <path> --ends-with <hex> Symbols whose offset ends in a given hex suffix (partial-overwrite gadget hunting).
search --libc <path> --str <string> Scan .rodata/.data for a string.
search --symbol name=addr [--symbol ...] [--tol N] Reverse lookup via libc.rip.
search --buildid <hash> BuildID lookup (local index, then libc.rip).
download <version>_<arch> Download + extract a glibc version, with checksum verification, provenance manifest, and automatic local indexing.
identify <file> [--offline] Identify a glibc version via BuildID or anchor-symbol fingerprint.
diff <a> <b> Symbol + security-attribute (RELRO/NX/Canary/PIE/RUNPATH) diff.
patch <binary> [--version ...] pwninit-style: auto-detect, download, and patch interpreter+RPATH.
run <binary> [--version ...] Patch (unless --no-patch) and drop into gdb inside a matching Ubuntu container.
build <version> <arch> Compile glibc from source inside the period-correct Ubuntu image.
vuln <version> Known CVEs affecting a version (curated, best-effort — cross-check NVD).
bundle export/import Pack/unpack the whole libs/ cache for air-gapped use.
doctor Self-check: Docker reachability, disk space, mirror reachability, index health.

Every command supports --json for scripting.

How build/run work (Docker-in-Docker)

build and run launch nested containers by shelling out to docker run against the host's Docker daemon. Two consequences:

  1. They need the Docker socket, which is opt-in via the build-src compose profile (pwnlibc.sh/pwnlibc.ps1 route these two subcommands there automatically). Socket access is equivalent to root on the host — only grant it if you're comfortable with that.
  2. Bind-mount paths for the nested container are resolved by the host daemon, not pwnlibc's own container filesystem — so only paths under ./libs and ./workdir are reachable (the compose file exports HOST_LIBS_DIR/HOST_WORKDIR_DIR so pwnlibc can translate between the two). This is why challenge binaries need to live in ./workdir.

Configuration

Optional config.yaml, passed via --config:

libs_dir: /data/libs
mirror_priority: ["ustc", "tuna"]
custom_mirrors:
  - name: corp-mirror
    base_url: https://mirror.corp.internal/ubuntu
max_retries: 5

Development (still no local Go needed)

make test     # go vet + go test, in a container
make lint     # golangci-lint, in a container
make build    # build the pwnlibc:latest image

CI (.github/workflows/ci.yml) runs the same containerized test/lint steps, then a Trivy vulnerability scan on the final image, and publishes multi-arch (amd64/arm64) images to GHCR on tagged releases.

Notes

  • The vuln database is a small, hand-curated list of well-known CVEs, not an authoritative feed — always cross-check against the NVD before relying on it for anything beyond "does this ring a bell."
  • This is an independent reimplementation for CTF/security-research use; it is not affiliated with the upstream glibc-all-in-one project.

Directories

Path Synopsis
cmd
pwnlibc command
Command pwnlibc is the entry point for the pwnlibc CLI.
Command pwnlibc is the entry point for the pwnlibc CLI.
internal
archive
Package archive handles unpacking .deb files (a Unix "ar" archive containing debian-binary, control.tar.*, and data.tar.*) and safely extracting the compressed tarballs inside, guarding against path traversal, symlink escapes, and decompression bombs.
Package archive handles unpacking .deb files (a Unix "ar" archive containing debian-binary, control.tar.*, and data.tar.*) and safely extracting the compressed tarballs inside, guarding against path traversal, symlink escapes, and decompression bombs.
buildsrc
Package buildsrc compiles glibc from source inside a period-correct Ubuntu base image (selected by version, since old glibc releases need an old-enough gcc/binutils to build cleanly), driven via `docker run`.
Package buildsrc compiles glibc from source inside a period-correct Ubuntu base image (selected by version, since old glibc releases need an old-enough gcc/binutils to build cleanly), driven via `docker run`.
bundle
Package bundle packs/unpacks the whole libs/ cache as a single tar.gz so it can be carried into an air-gapped CTF environment that can't reach any mirror.
Package bundle packs/unpacks the whole libs/ cache as a single tar.gz so it can be carried into an air-gapped CTF environment that can't reach any mirror.
cache
Package cache resolves on-disk paths used by pwnlibc's persistent state: the bbolt symbol/fingerprint index, the aggregated package list, and per-version provenance manifests.
Package cache resolves on-disk paths used by pwnlibc's persistent state: the bbolt symbol/fingerprint index, the aggregated package list, and per-version provenance manifests.
cli
Package cli wires pwnlibc's Cobra command tree together.
Package cli wires pwnlibc's Cobra command tree together.
config
Package config loads pwnlibc's YAML configuration: storage paths, mirror priority, and user-defined custom mirrors.
Package config loads pwnlibc's YAML configuration: storage paths, mirror priority, and user-defined custom mirrors.
diffcmd
Package diffcmd compares two ELF files (typically two glibc versions): symbol additions/removals/offset-changes and security-attribute transitions, computed natively via debug/elf.
Package diffcmd compares two ELF files (typically two glibc versions): symbol additions/removals/offset-changes and security-attribute transitions, computed natively via debug/elf.
elfinfo
Package elfinfo extracts everything pwnlibc needs from an ELF file using only the Go standard library's debug/elf — no shelling out to readelf, nm, or pyelftools like the original tool.
Package elfinfo extracts everything pwnlibc needs from an ELF file using only the Go standard library's debug/elf — no shelling out to readelf, nm, or pyelftools like the original tool.
fetch
Package fetch implements pwnlibc's HTTP retrieval strategy: bounded timeouts, exponential-backoff retries per mirror, and racing several mirrors concurrently so the fastest reachable one wins.
Package fetch implements pwnlibc's HTTP retrieval strategy: bounded timeouts, exponential-backoff retries per mirror, and racing several mirrors concurrently so the fastest reachable one wins.
glibcver
Package glibcver parses the "2.NN" major.minor component out of glibc version strings like "2.31-0ubuntu9.9" for ordering comparisons.
Package glibcver parses the "2.NN" major.minor component out of glibc version strings like "2.31-0ubuntu9.9" for ordering comparisons.
identify
Package identify determines which glibc version an unknown .so/binary is, trying (in order) BuildID exact match against the local index, BuildID lookup via libc.rip, and finally anchor-symbol fingerprint matching against every locally indexed version.
Package identify determines which glibc version an unknown .so/binary is, trying (in order) BuildID exact match against the local index, BuildID lookup via libc.rip, and finally anchor-symbol fingerprint matching against every locally indexed version.
index
Package index maintains pwnlibc's local persistent index: a BuildID -> version map and a per-version symbol table cache, backed by bbolt (pure Go, no cgo) so lookups are O(1) instead of re-parsing ELF files on every search/identify/diff invocation.
Package index maintains pwnlibc's local persistent index: a BuildID -> version map and a per-version symbol table cache, backed by bbolt (pure Go, no cgo) so lookups are O(1) instead of re-parsing ELF files on every search/identify/diff invocation.
jsonout
Package jsonout provides the shared --json output envelope used by every pwnlibc subcommand so machine-readable output has one consistent shape.
Package jsonout provides the shared --json output envelope used by every pwnlibc subcommand so machine-readable output has one consistent shape.
libcrip
Package libcrip is a client for the libc.rip public reverse-symbol-lookup API, used by `search --symbol` and the online path of `identify`.
Package libcrip is a client for the libc.rip public reverse-symbol-lookup API, used by `search --symbol` and the online path of `identify`.
mirrors
Package mirrors defines the registry of apt mirrors pwnlibc downloads glibc packages from, and probes them concurrently for availability.
Package mirrors defines the registry of apt mirrors pwnlibc downloads glibc packages from, and probes them concurrently for availability.
packages
Package packages parses apt pool directory listings into a unified, locally cached list of available libc6 / libc6-dbg package files across every configured mirror, so `search`/`download` never re-hit the network.
Package packages parses apt pool directory listings into a unified, locally cached list of available libc6 / libc6-dbg package files across every configured mirror, so `search`/`download` never re-hit the network.
patcher
Package patcher implements pwninit-style ELF patching: rewrite a target binary's PT_INTERP to point at a specific ld.so and set its RPATH/RUNPATH to a specific libc directory, so it runs against the exact glibc version a CTF challenge shipped with.
Package patcher implements pwninit-style ELF patching: rewrite a target binary's PT_INTERP to point at a specific ld.so and set its RPATH/RUNPATH to a specific libc directory, so it runs against the exact glibc version a CTF challenge shipped with.
pwnerr
Package pwnerr defines the typed error taxonomy used across pwnlibc so both human and --json output can report actionable, stable error codes instead of ad-hoc wrapped strings.
Package pwnerr defines the typed error taxonomy used across pwnlibc so both human and --json output can report actionable, stable error codes instead of ad-hoc wrapped strings.
vulndb
Package vulndb ships a small curated list of well-known glibc CVEs and answers "which of these affect version X" queries for the `vuln` subcommand.
Package vulndb ships a small curated list of well-known glibc CVEs and answers "which of these affect version X" queries for the `vuln` subcommand.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL