Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
92 changes: 71 additions & 21 deletions bin/build-fs-pickle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,105 @@
#
# 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
exit 1
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"
43 changes: 43 additions & 0 deletions docs/HONEYFS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Loading