From 284056c9247412c5402a1dda0436a8f9ed6f0734 Mon Sep 17 00:00:00 2001 From: Stefan Grosser <13567009+Bierchermuesli@users.noreply.github.com> Date: Sat, 9 May 2026 14:37:23 +0200 Subject: [PATCH] feat: add FAMILY switch to build-fs-pickle.sh (apt|opkg|none) Today the script is hardcoded to apt-get / Debian. Generalize it so the same wrapper can produce pickles for non-Debian personas: - FAMILY=apt (default) Debian/Ubuntu, current behaviour - FAMILY=opkg OpenWrt rootfs, installs python3-light - FAMILY=none no install step; image must contain python3 OUT= already existed and now it's actually useful: build pickles for multiple personas into separate paths from one tree. Update the Makefile help text and document the wrapper + the FAMILY switch in docs/HONEYFS.rst (right after the existing createfs section). --- Makefile | 2 +- bin/build-fs-pickle.sh | 92 ++++++++++++++++++++++++++++++++---------- docs/HONEYFS.rst | 43 ++++++++++++++++++++ 3 files changed, 115 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index a9a9c8d12f..7d81a181bc 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_COWRIE := $(shell python -m setuptools .PHONY: build-fs-pickle -build-fs-pickle: ## Rebuild src/cowrie/data/fs.pickle from a Debian container +build-fs-pickle: ## Rebuild src/cowrie/data/fs.pickle from a container (FAMILY=apt|opkg|none, IMAGE=...) DOCKER=$(DOCKER) bin/build-fs-pickle.sh .PHONY: docker-build diff --git a/bin/build-fs-pickle.sh b/bin/build-fs-pickle.sh index 030c0fd245..a4278af151 100755 --- a/bin/build-fs-pickle.sh +++ b/bin/build-fs-pickle.sh @@ -4,28 +4,65 @@ # # SPDX-License-Identifier: BSD-3-Clause -# Rebuild src/cowrie/data/fs.pickle from a real Debian container so the -# emulated filesystem layout matches a current Debian release. +# Rebuild a cowrie fs.pickle from a real container so the emulated +# filesystem layout matches a target OS. # -# Run from the repo root: bin/build-fs-pickle.sh -# or: make build-fs-pickle +# Run from the repo root: +# bin/build-fs-pickle.sh # default: Debian 12, writes +# # src/cowrie/data/fs.pickle.new +# make build-fs-pickle # same as above # -# Override defaults with env vars, e.g.: -# IMAGE=debian:12.5 PACKAGES="openssh-server vim curl" bin/build-fs-pickle.sh +# Customize via env vars. Common cases: # -# Output is written to src/cowrie/data/fs.pickle.new. Diff it against the -# committed pickle, then `mv` it into place when you're happy. +# # Debian/Ubuntu (default family): +# FAMILY=apt PACKAGES="openssh-server vim curl" bin/build-fs-pickle.sh +# +# # OpenWrt: +# FAMILY=opkg \ +# IMAGE=openwrt/rootfs:x86-64-23.05.5 \ +# OUT=/path/to/personas/openwrt/fs.pickle.new \ +# bin/build-fs-pickle.sh +# +# # BusyBox-like rootfs (the image must already contain python3, since +# # FAMILY=none performs no install step. python:3-alpine is a busybox +# # userland with python3 layered on, close enough for cowrie): +# FAMILY=none \ +# IMAGE=python:3-alpine \ +# OUT=/path/to/personas/busybox/fs.pickle.new \ +# bin/build-fs-pickle.sh +# +# FAMILY values: +# apt - Debian/Ubuntu base. Installs python3 + $PACKAGES via apt-get. +# opkg - OpenWrt rootfs. Installs python3-light + $PACKAGES via opkg. +# none - No install step. IMAGE must already contain python3. +# +# Output is written to $OUT (default src/cowrie/data/fs.pickle.new). Diff +# it against the existing pickle, then mv it into place when happy. set -eu DOCKER="${DOCKER:-docker}" -IMAGE="${IMAGE:-debian:12}" +FAMILY="${FAMILY:-apt}" OUT="${OUT:-src/cowrie/data/fs.pickle.new}" -# Package set to install in the container before pickling. This shapes the -# OS surface that attackers see (binaries under /usr/bin, libs, /etc/*, etc). -# Tweak to match the persona you're emulating. -PACKAGES="${PACKAGES:-openssh-server sudo vim-tiny curl wget net-tools iproute2 procps htop ca-certificates gnupg cron rsyslog systemd less man-db bash-completion}" +case "$FAMILY" in + apt) + : "${IMAGE:=debian:12}" + : "${PACKAGES:=openssh-server sudo vim-tiny curl wget net-tools iproute2 procps htop ca-certificates gnupg cron rsyslog systemd less man-db bash-completion}" + ;; + opkg) + : "${IMAGE:?FAMILY=opkg requires IMAGE (e.g. openwrt/rootfs:x86-64-23.05.5)}" + : "${PACKAGES:=dropbear curl wget}" + ;; + none) + : "${IMAGE:?FAMILY=none requires IMAGE (the image must already contain python3)}" + : "${PACKAGES:=}" + ;; + *) + echo "error: unknown FAMILY=$FAMILY (want apt|opkg|none)" >&2 + exit 1 + ;; +esac if [ ! -f src/cowrie/scripts/createfs.py ]; then echo "error: run from the cowrie repo root" >&2 @@ -33,26 +70,39 @@ if [ ! -f src/cowrie/scripts/createfs.py ]; then fi mkdir -p "$(dirname "$OUT")" +OUTDIR="$(cd "$(dirname "$OUT")" && pwd)" # Mount points are named with 'cowrie' in the path so createfs's built-in # blacklist (*cowrie*) skips them when walking /. "$DOCKER" run --rm \ -v "$(pwd)/src/cowrie/scripts/createfs.py:/cowrie-build/createfs.py:ro" \ - -v "$(pwd)/$(dirname "$OUT"):/cowrie-out" \ - -e PACKAGES="$PACKAGES" \ + -v "$OUTDIR:/cowrie-out" \ -e OUTNAME="$(basename "$OUT")" \ + -e FAMILY="$FAMILY" \ + -e PACKAGES="$PACKAGES" \ "$IMAGE" \ sh -eu -c ' - export DEBIAN_FRONTEND=noninteractive - apt-get update -qq - # shellcheck disable=SC2086 - apt-get install -y -qq --no-install-recommends python3 $PACKAGES - apt-get clean + case "$FAMILY" in + apt) + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + # shellcheck disable=SC2086 + apt-get install -y -qq --no-install-recommends python3 $PACKAGES + apt-get clean + ;; + opkg) + opkg update + # shellcheck disable=SC2086 + opkg install python3-light $PACKAGES + ;; + none) + ;; + esac python3 /cowrie-build/createfs.py -l / -o /cowrie-out/"$OUTNAME" chmod 0644 /cowrie-out/"$OUTNAME" ' echo -echo "Wrote $OUT (image: $IMAGE)" +echo "Wrote $OUT (image: $IMAGE, family: $FAMILY)" echo "Review, then install with:" echo " mv $OUT src/cowrie/data/fs.pickle" diff --git a/docs/HONEYFS.rst b/docs/HONEYFS.rst index db8532649f..5d1c86eaa6 100644 --- a/docs/HONEYFS.rst +++ b/docs/HONEYFS.rst @@ -41,3 +41,46 @@ Make sure your config picks up custom.pickle, by referencing it in `cowrie.cfg`: Or set an environment variable:: $ export COWRIE_SHELL_FILESYSTEM=custom.pickle + +Building from a real container +****************************** + +For a more realistic surface than hand-curating a directory, ``bin/build-fs-pickle.sh`` +spins up a real container, optionally installs a package set, and runs ``createfs`` +against ``/`` to produce a pickle. This is how ``src/cowrie/data/fs.pickle`` itself +is regenerated. + +The default invocation builds the stock Debian 12 surface:: + + $ bin/build-fs-pickle.sh # or: make build-fs-pickle + +Output goes to ``src/cowrie/data/fs.pickle.new``; review it, then ``mv`` it into +place. + +Override the OS family with ``FAMILY=`` and the base image with ``IMAGE=``. Three +families are supported: + +- ``apt`` (default) — Debian/Ubuntu. Installs ``python3`` plus ``$PACKAGES`` via + ``apt-get``. +- ``opkg`` — OpenWrt rootfs. Installs ``python3-light`` plus ``$PACKAGES`` via + ``opkg``. Requires ``IMAGE=`` (e.g. ``openwrt/rootfs:x86-64-23.05.5``). +- ``none`` — no install step. The image must already contain ``python3``. Useful + for prebuilt rootfs images and BusyBox-style targets (e.g. ``IMAGE=python:3-alpine``). + +Examples:: + + # Custom Debian package set: + $ FAMILY=apt PACKAGES="openssh-server vim curl" bin/build-fs-pickle.sh + + # OpenWrt persona: + $ FAMILY=opkg IMAGE=openwrt/rootfs:x86-64-23.05.5 \ + OUT=/path/to/personas/openwrt/fs.pickle.new \ + bin/build-fs-pickle.sh + + # BusyBox-like persona: + $ FAMILY=none IMAGE=python:3-alpine \ + OUT=/path/to/personas/busybox/fs.pickle.new \ + bin/build-fs-pickle.sh + +``OUT=`` may point anywhere on the host, so the same script can build pickles for +multiple personas into separate output paths.