A simple image to help you get your PWN/remote challenges up and running in no time
4.1K
You can use this image to easily deploy your challenges without having to setup anything.
Under the hood, pwn-chal utilizes ynetd to serve the challenge to host. socat can be utilized as well by setting BASE environment variable.
A sample Dockerfile is as follows:
FROM theflash2k/pwn-chal:latest
ENV CHAL_NAME=baby-pwn
COPY ${CHAL_NAME} ${CHAL_NAME}
COPY flag.txt flag.txt
| NOTE: Challenge binary MUST be placed inside the /app directory. Default WORKDIR is set to /app
The binaries will run in the context of user ctf-player rather than root.
| This CAN be overriden with the use of OVERRIDE_USER environment variable
Following environment variables can be changed to your own likings:
CHAL_NAME
PORT
BASE
START_DIR
FLAG_FILE
OVERRIDE_USER
SETUID_USER
REDIRECT_STDERR
NO_FLAG
ADD_READFLAG
ROOT_ONLY_FLAG
OUTBOUND_BLOCK
POW
There are other image-specific environment variables as well:
This is the name of the challenge binary. (I know, should've been CHAL_BIN or something, but ;-;).
The port that the challenge will listen on internally. The default port is 8000
The BASE binary to use for listening. Can be one of the following:
In case the challenge gives a shell, this is the directory the user will land in. Default is /app.
User can specify the path to the flag file. Needs to be an absolute value as the container will set chattr +i on this file. Default is /app/flag.txt. In case the flag_file is random, chattr won't work place but file will exist.
| NOTE: There is a FLAG_FILE_SYMLINK environment variable, which isn't set by default, but if set, will generate a symlink for the flag in /app/flag.txt if FLAG_FILE is not /app/flag.txt.
| NOTE: When using theflash2k/pwn-chal:windows, make sure to put flag in /app/flag.txt or set the FLAG_FILE accordingly. Randomly generated flag files may not work as the flag will be copied from /app/flag.txt or $FLAG_FILE to C:\flag.txt
By default, the binaries will run in the context of ctf-player user. This can be overriden by OVERRIDE_USER variable. It can be a valid user. But, if the user doesn't exist, it will default to root.
This environment variable will change owner and group of CHAL_NAME to SETUID_USER and then give it suid permissions. And will run as OVERRIDE_USER. Permission set is 4755. if SETUID_USER doesn't exist, it will default to root.
| NOTE: When the SETUID_USER is set, FLAG_FILE will automatically be set to 400, i.e. READ-ONLY to $SETUID_USER. This can be overriden with the FORCE_FLAG_RO parameter. Just set that to any value except y and the flag will be 440.
This environment variable will simply allow redirection of stderr through the socket. By default it is set to y, meaning that stderr will also be redirected.
This environment variable will simply remove the $FLAG_FILE. (Made this specifically for Showdown-based challenges where there's a submitter binary.)
This environment variable will set the
This will set the flag to be read only by root user and no one else. The use-case for this particular flag is when we're trying to use a SUID readflag binary. Default n
This blocks all the outbound connection. This is done using iptables so CAP_NET_ADMIN is required for the running container. If it is not provided, it doesn't SOFT-EXIT, but rather continues anyways. (Default=y)
Proof of Work. The value can be between 0-256. Default is 0. 256 means HAAARD. Usually, you go for 25-32.
The POW was originally implemented by HXPCTF team. You can see in pow script
Environment variables may also be specified with the docker run command:
docker run -it --rm -e PORT=5012 -p 54251:5012 -e BASE=socat theflash2k/pwn-chal:latest
| NOTE: If no CHAL_NAME is provided, the default binary will run on the specified port and upon connecting, you'll get the following message:
pwn-chal container successfully deployed. Please setup your challenge by specifying the CHAL_NAME environment variable and placing your binary in /app.
Regards,
TheFlash2k
For the kernel image, I decided to use QEMU under-the-hood. You can modify the following variables
By default, KASLR (Kernel Address Space Layout Randomization) is set to 1, i.e. it will be enabled. You can modify it by setting either 0 or 1 .
By default, KPTI (Kernel Page Table Isolation) is set to 1, i.e. it will be enabled. You can modify it by setting either 0 or 1 .
By default, SMEP (Supervisor Mode Execution Prevention) is set to 1, i.e. it will be enabled. You can modify it by setting either 0 or 1 .
By default, SMAP (Supervisor Mode Access Prevention) is set to 1, i.e. it will be enabled. You can modify it by setting either 0 or 1 .
By default, Panic on OOPS is set to 1, i.e. it will be enabled. You can modify it by setting either 0 or 1 .
MODE specifies how your payload will be downloaded/placed inside the vm. There are two available options:
The amount of RAM that will be allocated to the virtual machine
The INITRAMFS file name. This file MUST be stored at /app.
Default name is: initramfs.cpio.gz.
The name of the KERNEL. This file MUST be stored at /app.
Default name is: vmlinuz
The CPU that will be passed to the underlying qemu. There are two available options:
qemu64 [DEFAULT]kvm64The CPU threads/cores defined in SMP will be passed directly.
By default it is set to 1.
Since the windows machine uses wine under the hood to run the binaries and xvfb to emulate a virtual display, following variables can be modified:
This will let wine know whether the CHAL_NAME binary is a x86 or x64 binary.
This is the folder that contains the wine configurations, if the configurations do not already exist, they will take around 10-15 seconds to generate the entire configurations. By default, $RUN_AS/.wine is set as the WINEPREFIX and upon each container start, the configs are automatically generated.
This is a custom variable, if set to any value, it will use ynetd to serve the wine-served binary and upon crash, it will show the debug logs to the user on the connection. If you want to see detailed logs, you can also set the REDIRECT_STDERR=y so that all the stderr logs are also passed through the socket. However, this should only be used when trying a challenge locally.
| NOTE: Since ASLR is disabled by default in the wine configuration, all ASLR related challenges might not work as expected.
In order to run the arm and arm64 containers, you need to run the following command on the host first:
# Install the qemu packages
sudo apt-get install qemu binfmt-support qemu-user-static
# This step will execute the registering scripts
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
For debugging, you can set the following environment variable:
This port will be passed to the underlying qemu-arm command and will enable remote GDB debugging on the specified port.
| NOTE: When debugging is enabled, the container will run, and run the GDB port on the specified port, and since the program will run in a while [[ 1 ]] loop, it will continue to do so. However, the stdin, stdout and stderr aren't redirected to GDB, and therefore, running the container with -d option will not work the way you'd expect it to.
If you know how to fix it, please contact me, or a simple Pull Request with the fix ;).
A sample Dockerfile with debugging enabled:
FROM theflash2k/pwn-chal:arm
ENV CHAL_NAME=baby-arm
ENV QEMU_GDB_PORT=7000
COPY ${CHAL_NAME} ${CHAL_NAME}
| Tag | Version | Usage |
|---|---|---|
| latest | Ubuntu 25.04@sha256:9a302811bba2ae9533ddae0b563af29c112f1262329e508f13c0c532d5ba7c19 | Github |
| 2504 | Ubuntu 25.04@sha256:9a302811bba2ae9533ddae0b563af29c112f1262329e508f13c0c532d5ba7c19 | Github |
| 2410 | Ubuntu 24.10@sha256:02b7410af4f33ca28aad0764e34bb7442ef739ff253ea6c28e008e20f8921c52 | Github |
| 2404 | Ubuntu 24.04@sha256:6e75a10070b0fcb0bead763c5118a369bc7cc30dfc1b0749c491bbb21f15c3c7 | Github |
| 2304 | Ubuntu 23.04@sha256:5a828e28de105c3d7821c4442f0f5d1c52dc16acf4999d5f31a3bc0f03f06edd | Github |
| 2204 | Ubuntu 22.04@sha256:3d1556a8a18cf5307b121e0a98e93f1ddf1f3f8e092f1fddfd941254785b95d7 | Github |
| 2004 | Ubuntu 20.04@sha256:e5a6aeef391a8a9bdaee3de6b28f393837c479d8217324a2340b64e45a81e0ef | Github |
| 1804 | Ubuntu 18.04@sha256:152dc042452c496007f07ca9127571cb9c29697f42acbfad72324b2bb2e43c98 | Github |
| 1604 | Ubuntu 16.04@sha256:1f1a2d56de1d604801a9671f301190704c25d604a416f59e03c04f5c6ffee0d6 | Github |
| kernel | Ubuntu 25.04@sha256:9a302811bba2ae9533ddae0b563af29c112f1262329e508f13c0c532d5ba7c19 with QEMU | Github |
| x86 | theflash2k/pwn-chal:latest with gcc-multilib installed for 32-bit support | Github |
| x86-cpp | theflash2k/pwn-chal:latest with g++-multilib installed for 32-bit support | Github |
| seccomp | theflash2k/pwn-chal:latest with libseccomp-dev installed | Github |
| py38 | python:3.8-slim-buster with my magic | Github |
| py311 | python:3.11-slim-buster with my magic | Github |
| crypto | python:3.11-slim-buster with Pycryptodome and my magic | Github |
| sagemath | Using sagemath/sagemath:latest Docker image | Github |
| cpp | theflash2k/pwn-chal:latest with libstdc++ for C++ support | Github |
| arm | 25.04@sha256:9a302811bba2ae9533ddae0b563af29c112f1262329e508f13c0c532d5ba7c19 with QEMU [Also with GDB Remote Debugging] | Github |
| arm64 | 25.04@sha256:9a302811bba2ae9533ddae0b563af29c112f1262329e508f13c0c532d5ba7c19 with QEMU [Also with GDB Remote Debugging] | Github |
| windows | Ubuntu 20.04@sha256:e5a6aeef391a8a9bdaee3de6b28f393837c479d8217324a2340b64e45a81e0ef with WINE and XVFB | Github |
ASLR and PIE, so the stack and library addresses are always constant.Content type
Image
Digest
sha256:2e8cbec30…
Size
30.2 MB
Last updated
about 1 year ago
docker pull theflash2k/pwn-chal