java基础
路线
IDEA
Maven
Java Web
反射
ASM/Javassist
JNDI:8u191低版本和高版本怎么打(反序列化/本地工厂),以及如何审计
RMI是什么
Java Agent:启动原理和RASP的实现原理
JMX/JDWP
反序列化基础:gadget链、JEP290是什么
FastJson反序列化
WebLogic:二次反序列化、XML Decoder、IIOP/T3
Xstream反序列化
Hessian反序列化:dubbo
SnakeYAML反序列化
Shiro:Shiro经典漏洞、Padding Oracle漏洞形成原理、如何通过Shiro注入内存马
Struts2
Spring:Spring4Shell、Spring EL、SpringBoot Actuator利用
Tomcat:Tomcat AJP RCE
内存马原理:原理是什么、有哪些内存马 (动手实践调试每一种内存马)
内存马如何查杀
Log4J
其他组件漏洞:Apache Solr、Flink
进阶:tabby、codeql等静态分析
反射可以学习下高版本的绕过,jndi这里尝试自己写一个简单的jndi exploit。java agent这里自己尝试能否写一个简易rasp。log4j这里尝试自己写burp工具
最后可以学习下jdbc攻击,尝试自己写一个fake server
分层模型
MVC框架
Servlet
反射
1 | Class cls = Class.forName("java.lang.String"); |
总结
我们先获取到类型A的Class对象,通过Class对象的newInstance方法可以得到A的实例
通过Class对象可以获取到Constructor对象,进一步可以使用Constructor对象来得到A的实例
通过Class对象可以获取到Method对象,通过Method的invoke方法我们可以调用一些方法
通过Class对象可以获取到Field对象,我们可以对这个实例的一些字段进行赋值取值操作
Constructor
对象封装了构造方法的所有信息;
通过Class
实例的方法可以获取Constructor
实例:getConstructor()
,getConstructors()
,getDeclaredConstructor()
,getDeclaredConstructors()
;
通过Constructor
实例可以创建一个实例对象:newInstance(Object... parameters)
; 通过设置setAccessible(true)
来访问非public
构造方法
Java的反射API提供的Method对象封装了方法的所有信息:
通过Class
实例的方法可以获取Method
实例:getMethod()
,getMethods()
,getDeclaredMethod()
,getDeclaredMethods()
;
通过Method
实例可以获取方法信息:getName()
,getReturnType()
,getParameterTypes()
,getModifiers()
;
通过Method
实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters)
;
通过设置setAccessible(true)
来访问非public
方法;
通过反射调用方法时,仍然遵循多态原则。
Java的反射API提供的Field
类封装了字段的所有信息:
通过Class
实例的方法可以获取Field
实例:getField()
,getFields()
,getDeclaredField()
,getDeclaredFields()
通过Field实例可以获取字段信息:getName()
,getType()
,getModifiers()
通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)
来访问非public
字段
通过反射读写字段是一种非常规方法,它会破坏对象的封装
通过Class
对象可以获取继承关系:
Class getSuperclass()
:获取父类类型;Class[] getInterfaces()
:获取当前类实现的所有接口
通过Class
对象的isAssignableFrom()
方法可以判断一个向上转型是否可以实现
JDK动态代理
代理模式是一种设计模式,能够使得在不修改源目标的前提下,额外扩展源目标的功能。即通过访问源目标的代理类,再由代理类去访问源目标。这样一来,要扩展功能,就无需修改源目标的代码了,只需要在代理类上增加就可以了
一个最简单的动态代理实现如下:
1 | import java.lang.reflect.InvocationHandler; |
在运行期动态创建一个interface
实例的方法如下:
- 定义一个
InvocationHandler
实例,它负责实现接口的方法调用 - 通过 Proxy.newProxyInstance() 创建 interface 实例,它需要3个参数:
- 使用的
ClassLoader
,通常就是接口类的ClassLoader
- 需要实现的接口数组,至少需要传入一个接口进去
- 用来处理接口方法调用的
InvocationHandler
实例
- 使用的
- 将返回的
Object
强制转型为接口
动态代理实际上是JVM在运行期动态创建class字节码并加载的过程,它并没有什么黑魔法,把上面的动态代理改写为静态实现类大概长这样:
1 | public class HelloDynamicProxy implements Hello { |
其实就是JVM帮我们自动编写了一个上述类(不需要源码,可以直接生成字节码),并不存在可以直接实例化接口的黑魔法
小结
Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例
动态代理是通过Proxy
创建代理对象,然后将接口方法“代理”给InvocationHandler
完成的相对于静态代理类来说,无论有多少接口,动态代理只需要一个代理类
动态代理意义: 少修改代码 适配强
在反序列化漏洞中的作用:
1、readObject -> 反序列化自动执行 2、invoke -> 有函数调用 3、拼接两条链 4、任意->固定
要利用反序列化的漏洞是需要一个入口类的,先假设存在一个能够漏洞利用的类为 B.f
,比如Runtime.exec
这种,我们将入口类定义为A
,我们最理想的情况是 A[O] -> O.f ,那么我们将传进去的参数 O
替换为 B
即可。但是在实战的情况下这种情况是极少的
回到实战情况,比如我们的入口类A
存在 O.abc
这个方法,也就是 A[O] -> O.abc
如果O 是一个动态代理类,O 的invoke
方法里存在 .f
的方法,便可以漏洞利用了
1 | A[O] -> O.abc |
动态代理在反序列化当中的利用和readObject
是异曲同工的:
readObject
方法在反序列化当中会被自动执行,而invoke
方法在动态代理当中会自动执行
类的动态加载
Maven基础
Maven是一个Java项目管理和构建工具,它可以定义项目结构、项目依赖,并使用统一的方式进行自动化构建,是Java项目不可缺少的工具
Maven就是是专门为Java项目打造的管理和构建工具,它的主要功能有:
- 提供了一套标准化的项目结构
- 提供了一套标准化的构建流程(编译,测试,打包,发布……)
- 提供了一套依赖管理机制
Maven项目结构
一个使用Maven管理的普通的Java项目,它的目录结构默认如下:
1 | a-maven-project |
Maven是一个Java项目的管理和构建工具:
- Maven使用
pom.xml
定义项目内容,并使用预设的目录结构 - 在Maven中声明一个依赖项可以自动下载并导入classpath
- Maven使用
groupId
,artifactId
和version
唯一定位一个依赖
JDBC编程
程序运行的时候,往往需要存取数据。现代应用程序最基本,也是使用最广泛的数据存储就是关系数据库。
Java为关系数据库定义了一套标准的访问接口:JDBC(Java Database Connectivity)
反序列化
序列化就是把对象转换成字节流,便于保存在内存、文件、数据库中;反序列化即逆过程,由字节流还原成对象,一般用于远程调用、通过网络将对象传输至远程服务器、存储对象到数据库或本地等待重用等场景中
Java中的 ObjectOutputStream
类的 writeObject()
方法可以实现序列化,类 ObjectInputStream
类的 readObject()
方法用于反序列化。如果要实现类的反序列化,则是对其实现 Serializable
接口
当远程服务接受不可信的数据并进行反序列化且当前环境中存在可利用的类时,就认为存在反序列化漏洞
常见触发点
- JDBC 反序列化
- JSON 反序列化
存在危险的基础库
com.mchange:c3p0 0.9.5.2
com.mchange:mchange-commons-java 0.2.11
commons-beanutils 1.9.2
commons-collections 3.1
commons-fileupload 1.3.1
commons-io 2.4
commons-logging 1.2
org.apache.commons:commons-collections 4.0
org.beanshell:bsh 2.0b5
org.codehaus.groovy:groovy 2.3.9
org.slf4j:slf4j-api 1.7.21
org.springframework:spring-aop 4.1.4.RELEASE
回显方式
- 通过中间件特性回显
- 通过抛出异常回显
- 通过OOB回显
- 通过写静态文件回显
RMI
RMI (Remote Method Invocation,远程方法调用) 能够让在客户端Java虚拟机上的对象像调用本地对象一样调用服务端Java虚拟机中的对象上的方法。其中RMI标准实现是Java RMI,之外还有Weblogic RMI、Spring RMI等不同的实现
RMI中比较重要的两个概念是Stub和Skeleton,Stub和Skeleton对同一套接口进行实现,其中Stub由Client端调用,并不进行真正的实现,而是和Server端通信。Skeleton是Server端,监听来自Stub的连接,根据Stub发送的数据进行真正的操作
JNDI注入
JNDI注入是2016年由pentester在BlackHat USA上的 A Journey From JNDI LDAP Manipulation To RCE
议题提出的
其攻击过程如下
- 攻击者将Payload绑定到攻击者的命名/目录服务中
- 攻击者将绝对URL注入易受攻击的JNDI查找方法
- 应用程序执行查找
- 应用程序连接到攻击者控制的JNDI服务并返回Payload
- 应用程序解码响应并触发有效负载
OGNL表达式语言
OGNL(Object-Graph Navigation Language的简称),对象图导航语言,它是一门表达式语言,除了用来设置和获取Java对象的属性之外,另外提供诸如集合的投影和过滤以及lambda表达式等