From 1a4af6db30ca80982a0283d538a98c832081903d Mon Sep 17 00:00:00 2001 From: tabledevil Date: Sat, 13 Jun 2026 18:17:40 +0200 Subject: [PATCH] =?UTF-8?q?Initial=20import=20=E2=80=94=20ntdsextract2=20D?= =?UTF-8?q?ocker=20build=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cargo-installs --locked from crates.io on rust:1.96-alpine3.21 with build-base + linux-headers + gettext-dev (libesedb's bundled C tree needs all three). Final image is ~26MB on alpine:3.21. --- Dockerfile | 32 ++++++++++++++++++++++++++++++++ README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ start.sh | 31 +++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 Dockerfile create mode 100644 README.md create mode 100755 start.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e033113 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +# tabledevil/ntdsextract2 — Jan Starke's NTDS.DIT forensic analyser, Rust. +# +# Modern replacement for the old Python ntdsxtract; pulls users/groups/ +# computers from an offline NTDS.DIT (and the matching SYSTEM hive for +# secret-decryption), emits a bodyfile timeline, walks the AD DIT tree. +# +# Upstream moved off GitHub to https://codeberg.org/janstarke/ntdsextract2 +# (still on crates.io; we install from there to keep the build hermetic). +# +# docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 \ +# user /data/ntds.dit +# docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 \ +# timeline /data/ntds.dit --bodyfile > /tmp/timeline.body + +FROM rust:1.96-alpine3.21 AS build +# build-base brings gcc + libc-dev + binutils + make (libesedb's bundled C +# tree fails to compile without them); linux-headers + gettext-dev are +# libyal-on-musl prerequisites (bindtextdomain etc.). +RUN apk add --no-cache --virtual .build build-base clang pkgconf linux-headers gettext-dev + +# Crates.io build pulls upstream + embedded C code; takes ~5 min cold. +# --locked uses the upstream Cargo.lock (otherwise resolved transitive deps +# can demand newer rustc than the base image ships). +RUN cargo install --locked --root /opt/ntdsextract2 ntdsextract2 + +FROM alpine:3.21 +RUN apk add --no-cache libgcc +COPY --from=build /opt/ntdsextract2/bin/ntdsextract2 /usr/local/bin/ntdsextract2 +COPY start.sh /start.sh +RUN chmod +x /start.sh && mkdir -p /data +WORKDIR /data +ENTRYPOINT ["/start.sh"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ffc69c --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# docker_ntdsextract2 + +Build context for `tabledevil/ntdsextract2` — Jan Starke's Rust NTDS.DIT +forensic analyser. + +Upstream: (the GitHub +mirror at is archived; same +author, same code). + +## What it does + +Offline analysis of an extracted Active Directory database: + +| subcommand | output | +|---|---| +| `user` | account list with SID, RID, status, last logon, password timestamps | +| `group` | groups + members | +| `computer` | joined hosts with last-logon and OS strings | +| `timeline` | bodyfile-format timeline (feed `mactime`) | +| `tree` | the DIT hierarchy | +| `entry` | dump a single entry by DN | +| `search` | regex search over entries | +| `types` | list the AD schema's defined types | + +## Getting the input + +`NTDS.DIT` is locked while AD is running. You need to extract it (and the +matching `SYSTEM` registry hive) via vshadow / a forensic acquisition. +`tabledevil/vhdi` or `tabledevil/libewf` handle E01/VHDX mounts so you can +pull the file off cleanly. + +## Usage + +```bash +docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 user /data/ntds.dit +docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 timeline /data/ntds.dit > timeline.body +``` + +Run with no args for the in-image quick-reference banner. + +## Build context + +The image installs from crates.io (`cargo install ntdsextract2`) so the +build always picks up Jan's latest release. To refresh the published +image with a newer version, just rebuild — no Dockerfile edits required. diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..8cf1c4c --- /dev/null +++ b/start.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Banner on bare run; pass-through to ntdsextract2 otherwise. +if [ $# -eq 0 ]; then + cat >&2 <<'EOF' +============================================================ + ntdsextract2 — Active Directory database analyser (Jan Starke) +------------------------------------------------------------ + Mount your NTDS.DIT at /data, then pick a subcommand: + + docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 \ + user /data/ntds.dit # account list + docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 \ + group /data/ntds.dit # group membership + docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 \ + computer /data/ntds.dit # joined hosts + docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 \ + timeline /data/ntds.dit > timeline.body # bodyfile for mactime + docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 \ + tree /data/ntds.dit # DIT hierarchy + docker run --rm -v /evidence:/data:ro tabledevil/ntdsextract2 \ + search '' /data/ntds.dit # entry search + + NTDS.DIT files are normally locked while AD runs — extract with + vshadow / volume shadow copy first; ntdsextract2 reads the offline + copy directly. +============================================================ +EOF + ntdsextract2 --help 2>&1 | head -20 >&2 + exit 64 +fi +exec ntdsextract2 "$@"