Portswigger Labs
Lab 1: Modifying Serialised Objects
To solve this lab, become admin and delete carlos. When logged in as wiener, I saw this cookie being used:

This cookie was a PHP serialised object, and it had an admin field for me to use.
This particular cookie can be recreated using this PHP code:
Afterwards, changing the cookie to the above value gives me access to the administrator panel.

Sending a GET request to /admin/delete?username=carlos should solve the lab.
Lab 2: Modifying Serialized Data Types
To solve this lab, delete carlos as the admin. This is the cookie upon logging in as wiener:
A bit more substantial. The hint is to abuse how PHP compares variables, and the access_token is a string type.
This might be a hint to abuse loose comparison.

So since this is a string, I can compare it to a TRUE value OR 0.
To solve lab:
Lab 3: Application Functionality Exploit
This lab uses deserialisation in the cookie. To solve the lab, delete morale.txt from /home/carlos. I was also given a backup account of gregg:rosebud, probably because I might have to delete an account.
There's a bit more functionality within this lab:

I uploaded a sample PNG, and then deleted my account. THis was the cookie sent in the POST request to /my-account/delete:

The avatar_link was a directory on the website. I can change this to /home/carlos/morale.txt and delete the backup account.
I intercepted the response and changed the cookie using this script:
This solved the lab.
Lab 4: Arbitrary Object Injection
I have to inject an object to this. To solve the lab, delete /home/carlos/morale.txt. In the page source, there was a mention of some source code:

The hint was to append a ~ to read editor-generated backup files, so visiting /lib/CustomTemplate.php~ gave me source code:
For some reason, it calls unlink in __destruct(), meaning the PHP script automatically calls unlink. the only important variable is the lock_file_path one. Using this script can exploit this:
Replace the output as the cookie, and send a request to the website to solve the lab.
Lab 5: Java Deserialisation with Apache Commons
Java deserialisation means ysoserial is required. To solve the lab, delete morale.txt.
The cookie for this lab is no longer PHP based.

This is a CommonsCollections4 payload. First, generate the payload:
Afterwards, replace the session cookie with the output and URL encode it to solve the lab.
Lab 6: PHP Gadget Chains
This lab uses a signed cookie, and a common PHP framework. There is no source code, but there are pre-built gadget chains. To solve the lab, delete /home/carlos/morale.txt.
The cookie looks rather different:

Here's the cookie:
Within the page source, there's a comment:

This page is the pphinfo.php page, and there's a SECRET_KEY variable:

Sending a modified cookie results in this error:

So this uses Symfony and I have a secret key. This means I probably have to use phpggc to generate my payload since gadget chains have to be exploited.
Using symfony/rce4 gives me the same version that the website is running:
Using this, I can generate a base64 encoded payload.
Now, I need to fit this into my cookie. Since I am given the SECRET_KEY variable from earlier, I can sign and forge any cookies.
This solves the lab.
Lab 7: Ruby Gadget Chain
This lab uses the Ruby on Rails framework, and there are documented exploits to enable RCE via a gadget chain. To solve the lab, delete /home/carlos/morale.txt.
Sending a malformed cookie results in a Ruby error:

When searching for exploits, I found this:
Here's the edited final script:
I ran this here:
This gave me this as the output:
URL encode this and set it as the cookie. Sending a request would solve the lab.
Lab 8: Custom Java Gadget Chain
This lab requires me to exploit insecure deserialisation to obtain the administrator's password, then delete carlos.
There's a comment in the page source:

Viewing /backup shows two files:

Here's the source for AccessTokenUser.java:
And here's the other source code:
PortSwigger has provided a simple Java program for serialising objects on Github and replit.
Code Analysis -> Create Object
Starting with ProductTemplate, the class has a string variable of id. This id variable is passed to a SQL query:
The id parameter is not sanitised, and I can try to create a Java program to set it to ' to escape the query. Firstly, I need to create a serialised object for ProductTemplate.
Here are the variables and constructor for ProductTemplate, which need to be copied over.
Note that it uses a Product variable. Since I do not actually care about the product variable, this class can be empty:
Note that the original code packages it as data.productcatalog, so create a package like so:

Afterwards, use package.productcatalog to package it as per the source code.
Here's the code I used:
This program outputs this:
The above can be sent into the website, and I will receive an SQL error:

This means we have effectively injected parameters into the SQL query, and confirmed the SQL Injection vulnerability.
SQL Injection via Deserialisation
Now, I can proceed to enumerate the database. Since I want to extract data, UNION injection must be used.
For this, I modified the Java code to take user input, and then built the .jar:
Now, I can try to automate this testing. I know that the database uses PostgreSQL from the source code, so I can begin to find the number of columns.
Using a payload of ' UNION SELECT NULL-- resulted in this:

From this, I can begin to brute force the number of columns. I wasn't sure how big it was going to be, so I scripted the process:
This returned this payload:
So there are 8 columns. Now, I can check which column returns data. Typing 1 in random columns results in this error:

I was lazy to automate this process, so I tested each payload while printing out the error using some regex:
When changing the 4th, 5th and 6th column to 1, it did not return this error. Instead, it returned this:

Using this, I can try to list the contents of the database using these columns, since they seem to return some values.
Since error messages are being shown, and the 4th column do not expect string types, I can try using CAST to convert it to an integer.
This returns an error with the users table:

I updated the code to print the result:
After some tweaking, I found the columns to be username and password. I can then retrieve the password of the administrator. Here's the full script:

Log in and delete carlos. Fun lab to script!
Lab 9: Custom PHP Gadget Chain
To solve this lab, delete morale.txt. The page has some source code, which can be read via using a ~ character.

Here's the code:
End Goal
End goal is to call call_user_func to delete the file, since this function executes a function to it as arguments. This is within the DefaultMap function, and I can pass in anything as the $callback value, which are the function names like passthru or system.
The function is called within __get(), which is invoked upon trying to retrieve an inaccessible property. The code below shows an example of how this can be used to execute commands:

Calling call_user_func
call_user_funcThe __wakeup() function will call build_product(), and it leads to $this->desc = $desc->$default_desc_type within the Product class. This is suitable because it is called upon deserialization, meaning that when I pass in a cookie, it starts there.
The assignment of $this->desc = $desc->$default_desc_type can be used to trigger the __get() magic method. This is because it attempts to assign $this->desc to an attribute that it does not have. I jsut have to pass in the right stuff to call call_user_func with the right values.
Putting it Together
Here's the exploit explained:
First, create a new
CustomTemplateclass.Within the
CustomTemplateclass, change the$descvariable to contain aDefaultMap('system');object. This would construct aDefaultMapfunction with thesystemfunction within thecall_user_funcfunction.Set
$default_desc_typeto the string containing the commands to be executed. This then callsbuild_product()to create aProductobject with$default_desc_typeand$desc.Within
Product, it tries to get an attribute of$descthat it does not have. This action invokes the__get()method withinDefaultMap, since$descis aDefaultMapobject after all.The values passed to
__get()would be the name of the property it attempted to retrieve. Fpr example, if it attempted to retrieve$desc->'random, the$nameparameter containsrandom.The machine will then execute
call_user_func('system', 'random'). Afterwards, I just have to replace therandomstring with a command I want executed.Print out this object and
base64encode it.
Here's the full exploit script to create the object on my machine:
Here's the output:
Passing that cookie value in solves the lab.
Lab 10: Phar Deserialisation
To solve this lab, exploit phar deserialisation to delete morale.txt.
Logging in provides a upload avatar feature:

Uploading files does a POST request to /my-account/avatar. Afterwards, reloading the page sends a GET request to /cgi-bin/avatar.php?avatar=wiener to load the avatar.

I visited cgi-bin, and found some files:

Here is the CustomTemplate code:
And here's blog.php:
Source Code Analysis
Firstly, I noticed that blog.php imports a twig, which means that SSTI might be possible due depending on what I inject.
The Blog object basically passes $desc to a Twig_Environment, hence $desc is the injection point for SSTI.
Next, for phar:// deserialisation, it can occur for PHP functions that don't eval, like file_get_contents or file_exists. Within the CustomTemplate function, there is a file_exists function used, taking lockFilePath() as the parameter
Gotta manipulate template_file_path with whatever payload I decide to use.
Here's the exploit path:
Set
$descwithinBlogas a SSTI payload which removes the file. Note that it still requires a$userparameter.Pass this object into
CustomTemplate, with$template_file_pathset to theBlogobject. This will pass the payload into the functions required.Using this payload, create a PHAR-JPG payload (the website only accepts JPG files).
Access the payload using the
phar://wrapper to execute it.
Exploit
I used this repo to generate the payload:
Within phar_jpg_polyglot.php, replace the exploit class with the following:
I don't actually care about how CustomTemplate and Blog are formed, I just need the serialised object, thus they are empty classes. Afterwards, run this to generate the JPG file:
Then, upload out.jpg:

Then, visit /cgi-bin/avatar.php?avatar=phar://wiener to solve the lab.
Last updated