Skip to content

Commit a58461d

Browse files
committed
fix: correct qemu usage, harden gdb wrappers and release fetches
- Dockerfile: bake libc6-{armhf,arm64}-cross sysroots so qemu-user -L works for dynamic ARM/AARCH64 binaries without per-container apt-get. Also guard GitHub release fetches: only send Authorization header when a token is actually present (avoids empty 'token ' header on anon rebuilds, which can trigger rate-limit edge cases). - config/gdb-{pwndbg,gef,peda}: add -nx to skip system/user/CWD .gdbinit so the explicit -x plugin load is deterministic, and a malicious .gdbinit in a challenge directory cannot auto-execute. - README.md: replace nonexistent 'qemu-user' command with real binaries (qemu-arm, qemu-aarch64), document new sysroot setup, split static vs dynamic vs gdbserver examples, fix history count (78 not 97), refresh stale weekly tag placeholder. - config/zsh_history: same qemu binary fix. - CLAUDE.md: document cross-libc bake decision. - .gitignore: ignore .omc/ workspace.
1 parent a46afac commit a58461d

8 files changed

Lines changed: 41 additions & 19 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@ venv/
3131
*.tar.gz
3232
*.tar.xz
3333
*.zip
34+
35+
# oh-my-claudecode workspace
36+
.omc/

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Single `Dockerfile` with 13 logical layers, organized by purpose:
2828

2929
| Layer | Contents |
3030
|---|---|
31-
| 1. Base | Ubuntu 24.04, system deps, multilib, X11/GL, JDK 21, QEMU user-mode, wabt, neovim, upx, p7zip |
31+
| 1. Base | Ubuntu 24.04, system deps, multilib, X11/GL, JDK 21, QEMU user-mode + arm/aarch64 cross-libc sysroots, wabt, neovim, upx, p7zip |
3232
| 2-4. Python | pwntools, angr, qiling, ropper, ROPgadget, binary-refinery, frida-tools, unblob (pipx). Libraries (capstone, keystone, unicorn, z3, yara, r2pipe) injected into pwntools venv |
3333
| 5. Pip/Manual | binwalk, hash-identifier, opengrep |
3434
| 6. Ruby | one_gadget, seccomp-tools |
@@ -60,3 +60,4 @@ Single `Dockerfile` with 13 logical layers, organized by purpose:
6060
- **`CTF_UID`/`CTF_GID` build args** — default 1000 to match most Linux hosts; avoids X11 socket permission issues
6161
- **`openjdk-21-jdk` (not headless)** — Ghidra needs AWT/Swing for its GUI
6262
- **`--cap-add=SYS_PTRACE`** — required at runtime for GDB to attach to processes
63+
- **`libc6-{armhf,arm64}-cross` baked in** — provides `/usr/arm-linux-gnueabihf` and `/usr/aarch64-linux-gnu` sysroots for `qemu-user -L`. Avoids per-container `apt-get` (broken in air-gapped CTF env) at ~50-80 MB cost

Dockerfile

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
2727
libxkbcommon-x11-0 libxkbcommon0 \
2828
openjdk-21-jdk \
2929
qemu-user qemu-user-static \
30+
libc6-armhf-cross libc6-arm64-cross \
3031
llvm clang lld \
3132
python3 python3-dev python3-pip python3-venv pipx \
3233
ruby-dev
@@ -59,7 +60,8 @@ RUN git clone --depth=1 https://github.com/blackploit/hash-identifier /opt/hash-
5960

6061
RUN --mount=type=secret,id=github_token \
6162
tok=$(cat /run/secrets/github_token 2>/dev/null || true); \
62-
url=$(curl -fsSL -H "Authorization: token ${tok}" https://api.github.com/repos/opengrep/opengrep/releases/latest \
63+
curl_auth=(); [ -z "$tok" ] || curl_auth=(-H "Authorization: token ${tok}"); \
64+
url=$(curl -fsSL "${curl_auth[@]}" https://api.github.com/repos/opengrep/opengrep/releases/latest \
6365
| jq -er '.assets[] | select(.name == "opengrep_manylinux_x86") | .browser_download_url' | head -1); \
6466
[ -n "$url" ] || { echo "ERROR: opengrep asset URL not found"; exit 1; }; \
6567
wget -qO /usr/local/bin/opengrep "$url"; \
@@ -102,7 +104,8 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
102104
# Cutter (rizin GUI) — extracted AppImage for Docker compatibility
103105
RUN --mount=type=secret,id=github_token \
104106
tok=$(cat /run/secrets/github_token 2>/dev/null || true); \
105-
url=$(curl -fsSL -H "Authorization: token ${tok}" https://api.github.com/repos/rizinorg/cutter/releases/latest \
107+
curl_auth=(); [ -z "$tok" ] || curl_auth=(-H "Authorization: token ${tok}"); \
108+
url=$(curl -fsSL "${curl_auth[@]}" https://api.github.com/repos/rizinorg/cutter/releases/latest \
106109
| jq -er '.assets[] | select(.name | test("Linux-x86_64\\.AppImage$")) | .browser_download_url' | head -1); \
107110
[ -n "$url" ] || { echo "ERROR: Cutter asset URL not found"; exit 1; }; \
108111
wget -qO /tmp/cutter.AppImage "$url"; \
@@ -117,7 +120,8 @@ RUN --mount=type=secret,id=github_token \
117120
# Ghidra (platform-independent zip)
118121
RUN --mount=type=secret,id=github_token \
119122
tok=$(cat /run/secrets/github_token 2>/dev/null || true); \
120-
url=$(curl -fsSL -H "Authorization: token ${tok}" https://api.github.com/repos/NationalSecurityAgency/ghidra/releases/latest \
123+
curl_auth=(); [ -z "$tok" ] || curl_auth=(-H "Authorization: token ${tok}"); \
124+
url=$(curl -fsSL "${curl_auth[@]}" https://api.github.com/repos/NationalSecurityAgency/ghidra/releases/latest \
121125
| jq -er '.assets[] | select(.name | endswith(".zip")) | select(.name | test("PUBLIC")) | .browser_download_url' | head -1); \
122126
[ -n "$url" ] || { echo "ERROR: Ghidra asset URL not found"; exit 1; }; \
123127
wget -qO /tmp/ghidra.zip "$url"; \
@@ -132,7 +136,8 @@ RUN --mount=type=secret,id=github_token \
132136
RUN --mount=type=secret,id=github_token \
133137
mkdir -p /opt/retdec; \
134138
tok=$(cat /run/secrets/github_token 2>/dev/null || true); \
135-
url=$(curl -fsSL -H "Authorization: token ${tok}" https://api.github.com/repos/avast/retdec/releases/latest \
139+
curl_auth=(); [ -z "$tok" ] || curl_auth=(-H "Authorization: token ${tok}"); \
140+
url=$(curl -fsSL "${curl_auth[@]}" https://api.github.com/repos/avast/retdec/releases/latest \
136141
| jq -er '.assets[] | select(.name | test("[Ll]inux.*tar")) | .browser_download_url' | head -1); \
137142
[ -n "$url" ] || { echo "ERROR: retdec asset URL not found"; exit 1; }; \
138143
wget -qO /tmp/retdec.tar.xz "$url"; \
@@ -164,7 +169,8 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
164169
--mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
165170
--mount=type=secret,id=github_token \
166171
tok=$(cat /run/secrets/github_token 2>/dev/null || true); \
167-
url=$(curl -fsSL -H "Authorization: token ${tok}" https://api.github.com/repos/WerWolv/ImHex/releases/latest \
172+
curl_auth=(); [ -z "$tok" ] || curl_auth=(-H "Authorization: token ${tok}"); \
173+
url=$(curl -fsSL "${curl_auth[@]}" https://api.github.com/repos/WerWolv/ImHex/releases/latest \
168174
| jq -er '.assets[] | select(.name | test("Ubuntu.*24\\.04.*\\.deb$")) | .browser_download_url' | head -1); \
169175
[ -n "$url" ] || { echo "ERROR: ImHex asset URL not found"; exit 1; }; \
170176
wget -qO /tmp/imhex.deb "$url"; \
@@ -176,7 +182,8 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
176182
# pycdc (pre-built from decompyle-builds)
177183
RUN --mount=type=secret,id=github_token \
178184
tok=$(cat /run/secrets/github_token 2>/dev/null || true); \
179-
meta=$(curl -fsSL -H "Authorization: token ${tok}" https://api.github.com/repos/extremecoders-re/decompyle-builds/releases/latest); \
185+
curl_auth=(); [ -z "$tok" ] || curl_auth=(-H "Authorization: token ${tok}"); \
186+
meta=$(curl -fsSL "${curl_auth[@]}" https://api.github.com/repos/extremecoders-re/decompyle-builds/releases/latest); \
180187
url_pycdc=$(echo "$meta" | jq -er '.assets[] | select(.name == "pycdc.x86_64") | .browser_download_url' | head -1); \
181188
url_pycdas=$(echo "$meta" | jq -er '.assets[] | select(.name == "pycdas.x86_64") | .browser_download_url' | head -1); \
182189
[ -n "$url_pycdc" ] && [ -n "$url_pycdas" ] || { echo "ERROR: pycdc/pycdas asset URLs not found"; exit 1; }; \
@@ -218,7 +225,8 @@ RUN git clone --depth=1 https://github.com/niklasb/libc-database /opt/libc-datab
218225
# pwninit
219226
RUN --mount=type=secret,id=github_token \
220227
tok=$(cat /run/secrets/github_token 2>/dev/null || true); \
221-
url=$(curl -fsSL -H "Authorization: token ${tok}" https://api.github.com/repos/io12/pwninit/releases/latest \
228+
curl_auth=(); [ -z "$tok" ] || curl_auth=(-H "Authorization: token ${tok}"); \
229+
url=$(curl -fsSL "${curl_auth[@]}" https://api.github.com/repos/io12/pwninit/releases/latest \
222230
| jq -er '.assets[] | select(.name == "pwninit") | .browser_download_url' | head -1); \
223231
[ -n "$url" ] || { echo "ERROR: pwninit asset URL not found"; exit 1; }; \
224232
wget -qO /usr/local/bin/pwninit "$url"; \

README.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ docker run -it --rm --cap-add=SYS_PTRACE \
3333
docker build --build-arg CTF_UID=$(id -u) --build-arg CTF_GID=$(id -g) -t pwndocker-reverse .
3434
```
3535

36-
You land in `/ctf` as user `ctf` with sudo, oh-my-zsh, and 97 pre-loaded history entries searchable via Ctrl+R.
36+
You land in `/ctf` as user `ctf` with sudo, oh-my-zsh, and 78 pre-loaded history entries searchable via Ctrl+R.
3737

3838
## Reproducibility & Verification
3939

@@ -44,7 +44,7 @@ The image is **rebuilt every Monday** from `master` so `latest` stays fresh agai
4444
docker pull ghcr.io/gl0bal01/pwndocker-reverse:latest
4545

4646
# Dated weekly snapshot — fresh but frozen, good for a CTF weekend
47-
docker pull ghcr.io/gl0bal01/pwndocker-reverse:weekly-20260406
47+
docker pull ghcr.io/gl0bal01/pwndocker-reverse:weekly-20260427 # replace with latest weekly-YYYYMMDD tag
4848

4949
# Immutable digest — fully reproducible, pin this in team setups / scripts
5050
docker pull ghcr.io/gl0bal01/pwndocker-reverse@sha256:<digest>
@@ -76,7 +76,7 @@ docker buildx imagetools inspect ghcr.io/gl0bal01/pwndocker-reverse:latest --for
7676
| **Analysis** | binary-refinery, opengrep, hash-identifier |
7777
| **Networking** | socat, ncat, tcpdump, tshark, nmap |
7878
| **Editors** | vim, neovim |
79-
| **System** | QEMU user-mode, wabt, gdb-multiarch, p7zip, oh-my-zsh |
79+
| **System** | QEMU user-mode (+ arm/aarch64 cross-libc sysroots), wabt, gdb-multiarch, p7zip, oh-my-zsh |
8080

8181
## Usage
8282

@@ -176,9 +176,19 @@ libc-dump ./libc.so.6 system __free_hook
176176

177177
### Emulation
178178

179+
ARM and AARCH64 cross-libc sysroots are pre-installed, so dynamic binaries run out of the box.
180+
179181
```bash
180-
qemu-user -L /usr/arm-linux-gnueabihf ./arm_binary
181-
qemu-user -g 1234 ./binary # start with GDB server on port 1234
182+
# Static binaries: no sysroot needed
183+
qemu-arm ./arm_static_binary
184+
qemu-aarch64 ./aarch64_static_binary
185+
186+
# Dynamic binaries: -L points at the cross-libc sysroot baked into the image
187+
qemu-arm -L /usr/arm-linux-gnueabihf ./arm_binary
188+
qemu-aarch64 -L /usr/aarch64-linux-gnu ./aarch64_binary
189+
190+
# Run under GDB server on :1234, then attach with `gdb-multiarch -ex 'target remote :1234'`
191+
qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu ./aarch64_binary
182192
```
183193

184194
### Networking
@@ -192,7 +202,7 @@ tshark -r capture.pcap # analyze pcap
192202

193203
## Pre-Populated History
194204

195-
Hit Ctrl+R and search. 97 commands covering all installed tools are already in your history -- no need to remember syntax.
205+
Hit Ctrl+R and search. 78 commands covering all installed tools are already in your history -- no need to remember syntax.
196206

197207
## Project Structure
198208

config/gdb-gef

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/bin/bash
2-
exec gdb -q -x /etc/gdb/gdbinit.d/init-plugins -ex init-gef "$@"
2+
exec gdb -q -nx -x /etc/gdb/gdbinit.d/init-plugins -ex init-gef "$@"

config/gdb-peda

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/bin/bash
2-
exec gdb -q -x /etc/gdb/gdbinit.d/init-plugins -ex init-peda "$@"
2+
exec gdb -q -nx -x /etc/gdb/gdbinit.d/init-plugins -ex init-peda "$@"

config/gdb-pwndbg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/bin/bash
2-
exec gdb -q -x /etc/gdb/gdbinit.d/init-plugins -ex init-pwndbg "$@"
2+
exec gdb -q -nx -x /etc/gdb/gdbinit.d/init-plugins -ex init-pwndbg "$@"

config/zsh_history

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,5 @@ tcpdump -i any -w capture.pcap
9393
tshark -r capture.pcap
9494

9595
# === Emulation ===
96-
qemu-user -L /usr/arm-linux-gnueabihf ./binary
97-
qemu-user -g 1234 ./binary
96+
qemu-arm -L /usr/arm-linux-gnueabihf ./arm_binary
97+
qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu ./aarch64_binary

0 commit comments

Comments
 (0)