JAVA安全基础漏洞
[[../Web安全开发基础/Web安全开发基础-JAVA/Web安全开发基础-JAVA|Web安全开发基础-JAVA]]
命令/代码注入
1 | // ProcessBuilder |
SQL 注入
- 使用
Statement对象,传入参数拼接到 SQL 语句执行,存在漏洞; - 若使用
PreparedStatement通过预编译再去执行,但是若依旧使用拼接的方式来构造 SQL 语句还是存在漏洞; - 使用 Spring 中的
jdbctemplate也一样。
若使用占位符 ? 、ESAPI 验证、强类型转换限制、编码或严格的白名单过滤也可以避免注入。
Mybatis
支持两种参数符号 #、$, # 使用预编译 $ 使用拼接 SQL。
当需要用到 order by、like、in 时也是无法使用预编译
#{}会将对象转换为字符串,导致order by时错误;like模糊搜索时,直接使用%#{}会报错;in之后多个 id 查询时使用#同样会报错。
所以很多研发还是会使用 $。
Hibernate&JPA
接收参数使用 : 进行预编译可防止注入。
白盒审计:确定数据库通讯技术、确定类型找调用方法、再查看写法是否安全。
XXE
1 | // 审计函数 |
对于以上类函数实现,parse 执行后续的变量可控
- 禁用 dtd 实体引用、外部参数实体解析
- 过滤关键词
<!DOCTYPE>和<!ENTITY>,或者SYSTEM和PUBLIC- 使用安全的 XML 解析器或库:考虑使用像 Jackson XML、JAXB 等现代库,通常默认禁用不安全的功能,或者提供更好的安全性控制
SSRF
可能造成 SSRF 的 API
1 | HttpClient |
代码审计 SINK 点:
URL、HttpClient、OkHttpURLConnection、Socket、ImageIO、DriverManager.getConnection、SimpleDriverDataSource.getConnection、HttpURLConnection、RestTemplate、URLConnection、WebClient、JNDI
Linux:file:///etc/hosts Windows:file:///C:\windows\win.ini
URL 跳转
可能存在跳转的参数:sendRedirect、setHeader
代码审计 SINK 点:
redirect、url、redirectUrl、callback、return_url、toUrl、ReturnUrl、fromUrl、redUrl、request、redirect_to、redirect_url、jump、jump_to、target、to、goto、link、linkto、domain、oauth_callback
SpEL 表达式注入
如果一个 Web 应用允许用户输入字符串,并直接把这个字符串丢进 parser.parseExpression() 里去执行,攻击者就可以构造特殊的字符串来执行系统命令 T(java.lang.Runtime).getRuntime().exec('calc')。
SSTI
Thymeleaf、Velocity、FreeMarker
模版文件参数可控
Swagger UI API 框架接口泄露
接口泄露,未正确配置访问控制或未实施安全措施。
Actuator 泄露
- heapdump 堆转储文件,java 进程在某一时刻的内存快照,包含该时刻 jvm 中所有对象信息、类信息和变量值
- 数据库连接字符串、未加密用户的 Session、配置文件中的明文密码、以及刚被处理的用户卡号及个人信息
- druid 数据库连接池,自带监控控制台,用于查看 SQL 执行效率、并发量等
- 系统所有 SQL 语句、数据库连接地址、Session 以及正在访问的用户 ip
- jolokia 通过 http 访问 jmx(Java Management Extensions)的桥接器
- RCE,利用 logback 的配置加载功能、通过 jndi 注入
- gateway(spring cloud 生态系统中的网关)
- CVE-2022-22947 (SpEL 表达式注入)
反序列化漏洞
反序列化利用本质是根据序列化数据里的“类信息”,动态调用对应类的反序列化逻辑。
ClassLoader(类加载器) 的作用:把编译好的
.class文件(字节码)加载到 Java 虚拟机(JVM)中,并将其转换成内存中的java.lang.Class对象。


关注:入口点,链,执行点
- 原生类的反序列化(
ObjectInputStream.readObject()、SnakeYaml、XMLDecoder等) - 第三方组件的反序列化(Fastjson、Jackson、Xstream 等)
java 序列化的数据一般会以标记(ac ed 00 05)开头,base64 编码的特征为 rO0AB
以 URLDNS 链为例
ObjectInputStream.readObject() 本身只是“入口”,真正执行逻辑的是被反序列化对象自己的 readObject 方法。
就像 URLDNS 链,序列化时写入的是 HashMap<URL, Integer> map
1 | 从入口开始 |
1 | // desc.invokeReadObject() 逻辑 |
因为 java.util.HashMap 重写了 readObject,序列化时写的是 ObjectOutputStream.writeObject(map);,map 是 HashMap,所以反序列化一定会进 HashMap.readObject()。
JNDI 注入

- JNDI 支持的服务主要有:DNS、LDAP、CORBA、RMI 等。
- RMI:远程方法调用注册表
- LDAP:轻量级目录访问协议
- RMI 限制:
com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为 false,即不允许从远程的 Codebase 加载 Reference 工厂类,不过没限制本地加载类文件。
- LDAP 限制:
com.sun.jndi.ldap.object.trustURLCodebase 属性的默认值被调整为 false,导致 LDAP 远程代码攻击方式开始失效。这里可以利用 javaSerializedData 属性,当 javaSerializedData 属性 value 值不为空时,本地存在反序列化利用链时触发。
触发模式:
- 远程 Reference 链,通过远程加载攻击工具中的 class 文件中的代码,从而执行操作;
- 本地 Reference 链,通过利用本地服务器项目中原始依赖来执行操作;
- 反序列化链
- jdk 版本不同的 jndi 注入
- 中间件不同的 jndi 注入
- jar 包依赖不同的 jndi 注入
- JDK 6u45、7u21 之后:
java.rmi.server.useCodebaseOnly 的默认值被设置为 true。当该值为 true 时,将禁用自动加载远程类文件,仅从 CLASSPATH 和当前 JVM 的 java.rmi.server.codebase 指定路径加载类文件。使用这个属性来防止客户端 JVM 从其他 Codebase 地址上动态加载类,增加 RMI ClassLoader 安全性。
- JDK 6u141、7u131、8u121 之后:
增加了 com.sun.jndi.rmi.object.trustURLCodebase 选项,默认为 false,禁止 RMI 和 CORBA 协议使用远程 codebase 的选项,因此 RMI 和 CORBA 在以上的 JDK 版本上已经无法触发该漏洞,但依然可以通过指定 URI 为 LDAP 协议来进行 JNDI 注入攻击。
- JDK 6u211、7u201、8u191 之后:
增加了 com.sun.jndi.ldap.object.trustURLCodebase 选项,默认为 false,禁止 LDAP 协议使用远程 codebase 的选项,把 LDAP 协议的攻击途径也给禁了。
待补充待研究
URLDNSlog 链
Java 的 URL 类在计算 hashCode 时,认为“逻辑上等价的 URL 应该有相同的哈希值”,为判断两个域名是否指向同一个地方,Java 会在 URLStreamHandler 中调用网络解析库去查询该域名的 IP 地址。

1 | HashMap 实现了 Serializable 接口 |
1 | public static void main(String[] args) throws MalformedURLException { |
本地构造 POC 时不发送网络请求,通过反射修改 URL 类中的 hashCode,将其不等于 -1 即可:
生成 POC 到本地磁盘
1 | public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { |
反序列化调用:
1 | public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { |
URLDNSlog 链
https://mp.weixin.qq.com/s/9rS6iPMkxLHECgGDdyGXsQ
https://mp.weixin.qq.com/s/synx7l2JjZAtd9UHtXVqng
CC 链

CC1
1 | InvokerTransformer 实现了 Serializable 接口 |
CC2
1 | PriorityQueue::readObject() -> heapify() -> siftDown() -> siftDownUsingComparator() |
CC4
1 | PriorityQueue::readObject() -> heapify() -> siftDown() -> siftDownUsingComparator() |
CB 链
1 | PropertyUtils.getProperty(new User("Bob","man",31),"age"); |

1 | BeanComparator 实现了 Serializable 接口 |
内存马
无文件的 webshell,一种存在于内存当中的后门。
基本原理:在 web 组件或应用程序中,注册一层访问路由,访问者通过这层路由,来执行控制器中的代码。动态地在内存中注册一个新的服务组件,一旦注册成功,该组件就会像正常的系统功能一样,拦截并处理发往服务器的请求。

除通过上传脚本来植入内存马外,还可以借助已知漏洞:如反序列化,SSTI 注入,RCE 等执行 Java 反射逻辑,注入内存马
https://www.cnblogs.com/nongchaoer/p/15561936.html
https://github.com/W01fh4cker/LearnJavaMemshellFromZero
https://www.bilibili.com/video/BV1E84y1w77R/
Servlet
客户端请求的核心组件,通过动态注册 Servlet 来实现的内存攻击。通过程序化地向 Web 容器(如 Tomcat)在运行时注册恶意的 Servlet 对象,使得该 Servlet 能够在没有实际文件存在的情况下执行恶意程序。
https://cloud.tencent.com/developer/article/2130045
https://mp.weixin.qq.com/s/kfN6uU3A-jR72fyK8epnGw
Lisent
关键在于让 web 应用中的 web.xml 正确配置 java 监听器对应的类,在执行/访问指定路由的时候触发监听器,监听器内部写入命令执行等木马内容,从而触发。利用反射机制,编写代码手动调用 web.xml 和 java 类对应的映射关系从而实现这一点。
相当于利用反射机制添加一个服务。
Filter
基本同上
Servlet
编写代码在进行 get/post 请求时触发。
防御:获取所有的监听器、过滤器等,进行筛选删除。
封装为字节码,解密,反射执行。
分离
远程调用、远程类加载
远程读取、写入本地、包含文件、诱导执行
后缀检测、关键字检测
SpringMVC
interceptor
拦截器
Controller
控制器
Agent
Java Agent 一种可以在 JVM 启动时或运行时附加的工具,可以拦截并修改类字节码,通常用于实现 AOP(面相切面编程)、性能监控、日志记录等功能。
Java Agent 的两种加载方式:
- Premain:在 JVM 启动时通过命令行参数
-javaagent:path/to/xx.jar来指定(即通过指定参数 xx.jar 文件可修改目标 jar 文件的功能) - Agentmain:在 JVM 已经启动后,通过 Attach API 动态地附加到正在运行的 JVM 进程上




