Hrlies
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

1. 反射魔法——绕过安全检查
// 案例:通过反射调用私有危险方法
try {
Class<?> clazz = Class.forName("com.vuln.InternalService");
Method method = clazz.getDeclaredMethod("deleteAllData");
method.setAccessible(true); // 关键点:强制开启访问权限
method.invoke(null); // 执行敏感操作
} catch (Exception e) {
e.printStackTrace();
}
审计技巧:
搜索
setAccessible(true)
调用点检查反射操作的目标类是否包含敏感方法(如
shutdown
、delete
等)结合调用栈分析(是否用户输入控制了反射的类/方法名)
2. 反序列化Gadget链构造
// 利用Apache Commons Collections链
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
Object obj = ois.readObject(); // 反序列化入口
// 攻击载荷构造(伪代码):
ChainedTransformer chain = new ChainedTransformer(transformers);
TransformedMap.decorate(map, null, chain);
...
审计技巧:
识别所有
readObject
/readUnshared
调用点使用工具检测危险依赖链:
# 使用GadgetInspector检测
java -jar gadget-inspector.jar --target lib/
特别关注
InvokerTransformer
、TemplatesImpl
等危险类
3. 注解绕过鉴权
// Spring Security错误配置案例
@PreAuthorize("hasRole('USER')")
public void viewProfile() { /* ... */ }
// 错误配置允许所有访问
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = false)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll(); // 致命错误
}
}
审计技巧:
检查安全注解与实际配置是否一致
搜索
@PreAuthorize
、@Secured
等注解的空表达式验证
WebSecurityConfigurerAdapter
是否错误开放权限
4. Lambda表达式注入
// 动态生成Lambda导致RCE
public interface Command {
void execute(String cmd) throws Exception;
}
public static void main(String[] args) {
String userInput = "java.lang.Runtime.getRuntime().exec('calc');";
// 动态生成恶意Lambda
Command cmd = (Command) LambdaMetafactory.metafactory(
null, null, null,
(MethodType)MethodType.methodType(void.class, String.class),
MethodHandles.lookup().findVirtual(Runtime.class, "exec",
MethodType.methodType(Process.class, String.class)),
(MethodType)MethodType.methodType(void.class, String.class)
).getTarget().bindTo(Runtime.getRuntime()).invokeWithArguments(userInput);
cmd.execute(""); // 触发命令执行
}
审计技巧:
查找
LambdaMetafactory
使用点跟踪用户输入是否参与Lambda生成
结合动态代理分析(ASM字节码操作)
5. 内部类逃逸攻击
// 案例:通过内部类暴露私有字段
public class BankAccount {
private BigDecimal balance; // 本应是私有字段
public class Auditor {
public void checkBalance() {
System.out.println(balance); // 内部类直接访问外部类私有字段
}
}
}
// 攻击者通过反射获取内部类
Class<?> innerClazz = Class.forName("com.vuln.BankAccount$Auditor");
Object auditor = innerClazz.getDeclaredConstructor(BankAccount.class)
.newInstance(account);
Method method = auditor.getClass().getMethod("checkBalance");
method.invoke(auditor); // 泄露余额
审计技巧:
检查内部类是否暴露敏感数据
分析
getDeclaredConstructor
的参数类型使用FindBugs规则:
EI_EXPOSE_REP
、EI_EXPOSE_REP2
6. 类型混淆攻击
// 案例:利用泛型类型擦除
public class DataHolder<T> {
private T data;
public void setData(Object obj) {
this.data = (T) obj; // 未做类型检查
}
}
// 攻击者注入错误类型
DataHolder<String> holder = new DataHolder<>();
holder.setData(new File("/etc/passwd")); // 运行时不会报错
String data = holder.getData(); // ClassCastException延迟到此处
审计技巧:
查找未进行类型检查的强制转换
使用
checkcast
字节码验证(通过ASM分析)重点关注自定义泛型容器类
7. JNDI注入的隐蔽变种
// 新版绕过技巧(JDK≥8u191后)
String uri = "ldap://127.0.0.1:1389/deserialPayload";
// 使用带Factory的Reference
Context ctx = new InitialContext();
Object obj = ctx.lookup(uri);
// 利用javax.naming.spi.ObjectFactory绕过
public class EvilFactory implements ObjectFactory {
public Object getObjectInstance(Object obj, Name name,
Context ctx, Hashtable<?,?> env) {
Runtime.getRuntime().exec("calc.exe");
return null;
}
}
审计技巧:
搜索所有
InitialContext.lookup()
调用检查LDAP URL是否用户可控
使用JNDI监控工具:
jndimon
8. 动态代理权限逃逸
// 通过动态代理绕过权限检查
public interface AdminOperation {
void deleteDatabase();
}
class RealAdmin implements AdminOperation {
public void deleteDatabase() {
SecurityManager sm = System.getSecurityManager();
sm.checkPermission(new AdminPermission()); // 权限检查
// 实际删除操作...
}
}
// 攻击者构造代理绕过检查
AdminOperation proxy = (AdminOperation) Proxy.newProxyInstance(
loader,
new Class[]{AdminOperation.class},
(proxy1, method, args) -> { // 直接调用方法,绕过检查
return method.invoke(new RealAdmin(), args);
}
);
proxy.deleteDatabase(); // 绕过SecurityManager
审计技巧:
分析
Proxy.newProxyInstance
的使用场景检查InvocationHandler是否跳过安全检查
结合调用栈分析代理方法的实际执行路径
9. 注解处理器漏洞
// 自定义注解处理器实现RCE
@SupportedAnnotationTypes("*")
public class EvilProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (IOException e) { /*...*/ }
return true;
}
}
// META-INF/services/javax.annotation.processing.Processor 文件内容:
com.vuln.EvilProcessor
审计技巧:
检查项目中的
META-INF/services
文件分析自定义注解处理器的行为
使用隔离环境编译代码
10. SPI机制滥用
// 利用ServiceLoader加载恶意实现
ServiceLoader<CipherService> loader =
ServiceLoader.load(CipherService.class);
for (CipherService service : loader) {
// 攻击者可以在classpath中放置恶意实现
String encrypted = service.encrypt(data);
}
审计技巧:
检查
ServiceLoader.load()
的调用位置验证SPI实现类的来源
使用沙箱环境运行关键服务
综合防御策略
深度防御:
// 示例:多层校验
public void process(UserInput input) {
validateSyntax(input); // 语法层校验
validateBusiness(input); // 业务逻辑校验
sanitize(input); // 净化处理
execute(input); // 安全执行
}
工具链集成:
# 组合使用SAST/DAST工具
mvn org.owasp:dependency-check-maven:check # 依赖检查
semgrep --config=p/java # 自定义规则扫描
spotbugs -textui -high -sortByClass target/* # 字节码分析
动态监控:
// 使用Java Agent监控危险操作
public static void premain(String args, Instrumentation inst) {
inst.addTransformer((loader, className, classBeingRedefined,
protectionDomain, classfileBuffer) -> {
if (className.contains("Runtime")) {
// 插入监控代码
}
return classfileBuffer;
});
}
总结
Java代码审计需要关注:
语言特性陷阱:反射、泛型、内部类、动态代理
框架机制滥用:Spring AOP、Hibernate映射、JNDI
运行时环境:类加载机制、SPI扩展点、注解处理器
防御突破技巧:类型混淆、安全检查绕过、异常流控制
实际审计中,建议结合以下方法:
AST模式分析(使用CodeQL)
字节码逆向分析(使用JD-GUI+ASM)
运行时Hook(使用ByteBuddy/Java Agent)
历史漏洞模式匹配(CVE数据库关联分析)
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
