xxx代码审计面试
xxx代码审计面试
Yki-yawa自我介绍
## 讲一下你怎么审计项目
我从开发那里拿到 Java 源码后,然后采用 ’ 工具辅助 + 人工精查 ’ 的分层审计策略。首先通过静态扫描工具发现常见漏洞(如 SQL 注入、反序列化),然后重点审计核心业务流程(如支付、认证),同时关注依赖组件的安全性。对于发现的每个漏洞,我会进行复现、评估风险等级,并提供具体的修复建议。例如在某项目中,我们通过追踪数据流发现了一处 XXE 漏洞,最终通过配置 XML 解析器的安全特性解决了问题。此外,我认为审计不应局限于发现问题,还应推动团队建立持续安全的机制,如安全测试自动化和开发规范培训。
面试官:那你能结合项目,讲你审计过的漏洞
在我之前的项目中审计出过反序列化 RCE 漏洞。当时是通过工具扫描结合人工分析,在用户个人资料模块发现ObjectInputStream
,我查看利用条件依赖 Commons Collections 3.1,当时依赖的配置版本低于这个版本。修复方案:
- 替换为 JSON 序列化
- 添加
ObjectInputFilter
白名单 - 升级依赖至安全版本
Sql注入你是怎么审计的
我会用工具辅助,人工代码审查、安全测试相结合的系统化方法
先使用动态工具扫描,使用 SQLMap、OWASP ZAP 等工具对 Web 应用进行自动化扫描。SQLMap 可通过参数注入测试识别可能存在漏洞的 URL 或表单,ZAP 则能对整个 Web 应用进行爬取并检测 SQL 注入风险。但需注意自动化工具可能存在误报,需结合人工验证。
通过 Burp Suite 抓包分析 HTTP 请求与响应,手动修改参数内容(如添加单引号、尝试闭合 SQL 语句),观察数据库返回的错误信息(如报错信息包含数据库表结构、字段名),判断是否存在注入点。若返回统一错误页面,则需结合盲注技术(布尔盲注、时间盲注)进行验证。
人工对代码静态分析。先检查接收外部输入的接口(如 HTTP 请求参数、数据库存储过程的传入值),关注用户可控数据与 SQL 语句拼接的位置,例如 Java 中的Statement对象直接拼接参数、PHP 中未处理的$_GET变量直接用于 SQL 查询。
检查是否使用预编译语句(如 Java 的PreparedStatement、Python 的psycopg2模块)或 ORM 框架(Hibernate、Django ORM),确认其参数绑定机制是否正确。同时,查看是否存在自定义 SQL 拼接函数,若有则需审查其过滤规则是否覆盖常见攻击字符(如单引号、分号、注释符)。
对部署中间件进行检查,检查数据库权限配置,确保应用使用的数据库账户权限最小化(如仅授予必要的查询、插入权限),避免使用root等超级管理员账户。同时,查看是否启用数据库防护功能,如 MySQL 的sql_mode是否设置为严格模式,防止恶意 SQL 执行。
审查数据库日志(如 MySQL 的慢查询日志、错误日志),查找异常 SQL 语句,分析是否存在高频错误、异常的 SQL 结构或非预期的查询操作。
最后进行业务逻辑测试,针对业务流程设计特殊测试用例,例如在登录注册模块尝试注入 SQL 语句绕过身份验证,或在搜索框中构造跨表查询语句获取敏感数据。
数据类型验证:验证应用是否对输入数据类型进行严格校验,例如仅允许数字的字段是否拒绝字符串输入,防止通过类型绕过注入防护。
若发现未使用预编译的 SQL 拼接漏洞,会建议改用参数化查询,并提供具体的代码修改示例;对于复杂的业务逻辑漏洞,会结合业务场景设计针对性的防护方案,确保审计结果可落地。
Cc链了解过吗,讲一下cc1吧
CC1 链(CommonsCollections1 链)是 Java 反序列化漏洞中最经典的利用链之一,它利用 Apache Commons Collections 库的漏洞,允许攻击者通过反序列化不可信数据执行任意代码。
CC1 链基于 Java 反序列化的安全缺陷:当应用程序反序列化来自不可信源的数据时,攻击者可构造恶意序列化对象,触发一系列类的readObject()方法,最终通过反射调用Runtime.getRuntime().exec()执行任意命令。 关键组件:Apache Commons Collections 库(版本 3.1-3.4,高版本修复了部分漏洞)。
它的核心组件与调用链
CC1 链的核心是通过Transformer 链实现反射调用,主要涉及以下类:
- Transformer 接口:定义transform(Object input)方法,用于转换对象。
- ChainedTransformer:组合多个 Transformer,按顺序执行转换。
- ConstantTransformer:返回固定值(如Runtime.class)。
- InvokerTransformer:通过反射调用任意方法(如getMethod()、invoke())。
- AnnotationInvocationHandler(JDK 类):重写了readObject(),触发 Transformer 链。
简化的调用路径: 反序列化触发readObject() → AnnotationInvocationHandler.readObject() → LazyMap.get() → ChainedTransformer.transform() → Runtime.exec()。
利用条件
- 依赖版本:Commons Collections 3.1-3.4(需在类路径中)。
- 反序列化入口:存在接收用户输入并进行反序列化的代码(如 RMI、HTTP 接口)。
- JDK 版本限制:原生 CC1 链仅在JDK 8u71 及以下版本有效,高版本通过修复AnnotationInvocationHandler的readObject()方法阻止了链式调用。
防御措施
- 升级依赖:使用 Commons Collections 4.0 + 或修复版本,避免已知漏洞。
- JDK 版本控制:升级到 JDK 8u71 以上,并配置com.sun.jndi.rmi.object.trustURLCodebase=false。
- 输入过滤:拒绝反序列化不可信数据,或使用白名单类过滤器(如 Java 9 + 的ObjectInputFilter)。
- 运行时防护:使用安全管理器(SecurityManager)限制反射权限,或部署 Web 应用防火墙(WAF)拦截特征性 Payload。
面试加分项
- 变种与绕过:高版本 JDK 可通过CC6/CC7 链绕过限制,利用不同类组合实现相同效果。
- 实战场景:结合 RMI/HTTP 反序列化漏洞,通过攻击未授权的 RMI 接口执行命令。
- 工具推荐:使用ysoserial工具一键生成 CC1 Payload(java -jar ysoserial.jar CommonsCollections1 “calc.exe”)。
反序列化的原理
反序列化是将序列化后的数据恢复为原始对象的过程,其核心原理主要包含以下几个关键步骤:
首先是解析数据格式。序列化后的数据会以特定格式存在,比如常见的 JSON、XML,还有 Protocol Buffers 等二进制格式。在反序列化时,第一步就是识别数据格式,并依据对应格式的语法规则进行解析。以 JSON 为例,它采用键值对形式描述对象,反序列化时就要按照 JSON 的语法规范,将这些文本数据转化为程序能够理解和处理的内部表示形式。
接着是创建对象。解析完数据格式后,程序会根据获取到的对象类型等信息,在内存中实例化对象。在 Java 中,会调用对应类的构造函数;而在一些使用设计模式的场景下,可能会通过工厂方法来创建对象实例,以此在内存中构建出符合要求的对象雏形。
然后是设置对象属性。程序将解析出的数据值,按照对象的类定义和序列化数据中的属性对应关系,逐个赋给新创建对象的相应属性。比如从 JSON 数据{“name”: “Alice”, “age”: 25} 反序列化时,就会把“Alice“赋值给Person对象的name属性,25赋值给age属性,从而完整构建对象的状态。
最后是重建对象关系。当对象之间存在关联关系,像引用关系、继承关系等时,反序列化还需恢复这些关系。例如,若Book类对象包含Author类对象的引用,反序列化Book对象时,不仅要还原Book自身属性,还要反序列化Author对象,并正确建立两者之间的引用关联 ,让对象之间的关系与序列化之前保持一致。
通过以上步骤,反序列化实现了从存储或传输的序列化数据,到内存中可用对象的转换,方便程序对数据进行后续的逻辑处理、业务操作等。
反序列化漏洞的基本原理
在Java反序列化中,会调用被反序列化的readObject方法,当readObject方法被重写不当时产生漏洞
public class demon { |
此处重写了readObject方法,执行了 Runtime.getRuntime().exec()
defaultReadObject方法为ObjectInputStream中执行readObject后的默认执行方法
运行流程:
myObj对象序列化进object文件
从object反序列化对象
调用readObject方法
执行Runtime.getRuntime().exec(“calc.exe”);
总体来说讲,面试还是基础的,有些知识点点背的不够充分,好好准备的话还是能过面试的