Contents

๐ŸŒ Super Serial

A detailed write-up of the Web challenge 'Super Serial' from PicoCTF - 2021

/images/PicoGym/PicoCTF-2021/SuperSerial/challenge_presentation.png
Challenge Presentation

๐Ÿ“Š Challenge Overview

Category Details Additional Info
๐Ÿ† Event PicoGym Event Link
๐Ÿ”ฐ Category Web ๐ŸŒ
๐Ÿ’Ž Points 500 Out of 500 total
โญ Difficulty ๐ŸŸก Medium Personal Rating: 4/10
๐Ÿ‘ค Author madStacks Profile
๐ŸŽฎ Solves (At the time of flag submission) 7.240 solve rate
๐Ÿ“… Date 20-02-2025 PicoGym
๐Ÿฆพ Solved By mH4ck3r0n3 Team:

๐Ÿ“ Challenge Information

Try to recover the flag stored on this website http://mercury.picoctf.net:25395/

๐ŸŽฏ Challenge Files & Infrastructure

Provided Files

1
Files: None

๐Ÿ” Initial Analysis

First Steps

Initially, the website appears as follows:

/images/PicoGym/PicoCTF-2021/SuperSerial/site_presentation.png
Site Presentation

with a login screen. Initially, I thought it might be an SQL Injection, but after trying some classic payloads, nothing happened. Since itโ€™s a blackbox challenge, I decided to inspect the page source, but I didnโ€™t find anything interesting. There was just a form with action="index.php". So, I tried checking for robots.txt:

/images/PicoGym/PicoCTF-2021/SuperSerial/robots.png
Robots

and this returned a positive result. As we can see, thereโ€™s a Disallow: /admin.phps, but .phps seemed strange. The first question I asked myself was, “Is this some sort of typo?” But I quickly answered this myself. Indeed, after trying to visit the route /index.phps, I discovered that by adding the final s, it was possible to see the php code:

/images/PicoGym/PicoCTF-2021/SuperSerial/index.png
Index.phps

up to this point, nothing too strange, except for that serialize() which immediately made me think of an Insecure Deserialization. I also noticed that a require_once("cookie.php"); was specified, so I tried visiting the route /cookie.phps and got the source:

/images/PicoGym/PicoCTF-2021/SuperSerial/cookie.png
Cookie.phps

it also references another file with a .php extension, namely authentication.php, so I tried reading the source the same way with /authentication.phps:

/images/PicoGym/PicoCTF-2021/SuperSerial/authentication.png
Authentication.phps

Now that I have the source code of the challenge, we can move to the code analysis phase. The first vulnerable part can be found in cookie.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
if(isset($_COOKIE["login"])){
  try{
  	$perm = unserialize(base64_decode(urldecode($_COOKIE["login"])));
  	$g = $perm->is_guest();
  	$a = $perm->is_admin();
  }
  catch(Error $e){
  	die("Deserialization error. ".$perm);
  }
}

here, as we can see, there is a check to see if the login cookie is set (in the rest of the code, the login cookie is set only if the login is successful, either as a guest or as an admin). So, if the cookie exists, it is URL-decoded, base64-decoded, and then deserialized. Therefore, this cookie should be a serialized permissions object, especially since the is_guest() and is_admin() functions, which belong to the permission class, are called afterward. Finally, if any exception is thrown or any type of error occurs, thereโ€™s a catch block that prints the error and the $perm object (which should be the permission class object). However, as we can see:

1
2
3
4
  
  function \__toString() {
  	return $u.$p;
  }

the permission class implements the magic method __toString(). This method defines the behavior of the object when any type of casting to a string is performed. In this case, it concatenates username and password, so when a casting is done, as we saw in the previous if statement, it will print $u.$p. This behavior can be exploited by causing any kind of error during the deserialization of the object in the try block. In the challenge hints, we are told that the flag is located at the route ../flag, so keep that in mind because it will become useful shortly when analyzing the new vulnerable code. The other file that contains vulnerable code is authentication.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class access_log
{
blic $log_file;
nction \__construct($lf) {
his->log_file = $lf;

nction \__toString() {
turn $this->read_log();

nction append_to_log($data) {
le_put_contents($this->log_file, $data, FILE_APPEND);

nction read_log() {
turn file_get_contents($this->log_file);

}

require_once("cookie.php");
if(isset($perm) && $perm->is_admin()){
sg = "Welcome admin";
og = new access_log("access.log");
og->append_to_log("Logged in at ".date("Y-m-d")."\n");
} else {
sg = "Welcome guest";
}

Trying to visit the page, we only get a Welcome Guest, as we can see from the code above:

/images/PicoGym/PicoCTF-2021/SuperSerial/auth.png
Auth

Disregarding this, regarding the vulnerable code above, we can definitely say that it can be exploited in some way to read ../flag, as I believe itโ€™s the only way and those file_get_contents functions will certainly be useful for something. The other vulnerable part is the require_once("cookie.php"), as it will invoke the check seen earlier, which is vulnerable to deserialization of a malicious object. However, with an object of the permission class, we cannot do anything, unfortunately. Fortunately, we have another class available: access_log, with which we can serialize a malicious object. Indeed, we can exploit the serialization of the object to set the attribute $log_file = "../flag", so when the read_log() function is called, we could read the ../flag file instead of the access.log file. However, this is only possible because of the previously seen check, as without that check, we wouldnโ€™t be able to achieve deserialization, let alone read the output of the ../flag file. In fact, by deserializing a malicious access_log object with $log_file = "../flag", and setting it as the login cookie, it will enter the if statement, but when it calls $perm->is_guest(), we will get an error, because we are not using an object of the permission class, which has the is_guest() function declared, but rather using an object of the access_log class, which doesnโ€™t have that function. This will lead to the catch, and thatโ€™s where the magic happens. In fact, as we can see, the access_log object also declares the magic method __toString(), which calls the read_log() function. So when the exception occurs, it will print Deserialization error. File content passed to the $log_file parameter, because it will cast the object to a string, call the read_log() function, which will grab the content of the ../flag file using the file_get_contents() function and print it.

๐Ÿ”ฌ Vulnerability Analysis

Potential Vulnerabilities

  • PHP Insecure Deserialization

๐ŸŽฏ Solution Path

Exploitation Steps

Initial setup

Once the vulnerability and how to exploit it are understood, we need to figure out how to create the malicious serialized object. We could do this manually, because by doing a couple of searches, we can understand how the serialize() function in PHP serializes objects and by what criteria it does so. For example (a little spoiler, the final serialized object is the following: O:10:"access_log":1:{s:8:"log_file";s:7:"../flag";}). In this case, O:10 indicates the number of characters in access_log (which is the class of the object to be serialized). Next, we find :1: which indicates the number of parameters the class has. Inside the curly braces, we find s:8:"log_file";s:7:"../flag"; where s represents the type (String), 8 is the number of characters in log_file, and following the same logic, we find the value we want to pass to the log_file attribute, in this case, obviously ../flag. However, as we can see, itโ€™s better not to go crazy and write it manually. Letโ€™s move on to the exploitation phase to see how to proceed thinking outside the box.

Exploitation

The serialize() function produces an output like the one we saw earlier, right? This function takes an object to serialize, and in this case, we want to serialize the object of the access_log class, correct? So, a much simpler approach would be to copy the access_log class into a .php file, or directly use https://onlinephp.io/, instantiate it with the attribute $log_file = "../flag", and serialize it to obtain the serialized object output (even though in the code analyzed earlier, as we mentioned, when it is deserialized, it first decodes from urlencode, then from base64, and then deserializes it). So, to get the correct output, we can simply reverse the encoding order and follow the process described earlier. To recap and make it clearer, Iโ€™ll leave you a screenshot of the code to obtain the serialized object:

/images/PicoGym/PicoCTF-2021/SuperSerial/phpsandbox.png
Online PHP Exploit

Once serialized, all we have to do is open ChromeDevTools, add the login = Malicious Serialized Object cookie, and visit the /authentication.php route to obtain the flag.

Flag capture

/images/PicoGym/PicoCTF-2021/SuperSerial/manual_flag.png
Manual Flag

๐Ÿ› ๏ธ Exploitation Process

Approach

The automatic exploit serializes the object by passing "../flag" in the constructor to set $log_file = "../flag". Once the malicious serialized object is built, it is set in the login cookie as login=malicious_serialized_object and a request is made using PHP cURL, extracting the flag from the response using a regex.

๐Ÿšฉ Flag Capture

Flag

Proof of Execution

/images/PicoGym/PicoCTF-2021/SuperSerial/automated_flag.png
Automated Flag
Screenshot of successful exploitation

๐Ÿ”ง Tools Used

Tool Purpose
PHP Exploit
ChromeDevTools Web Testing

๐Ÿ’ก Key Learnings

New Knowledge

I have learned to exploit the PHP vulnerability related to object deserialization.

Time Optimization

Replicating the class of the vulnerable object right from the start and serializing it while modifying the necessary attributes that we need.

Skills Improved

  • Binary Exploitation
  • Reverse Engineering
  • Web Exploitation
  • Cryptography
  • Forensics
  • OSINT
  • Miscellaneous

๐Ÿ“š References & Resources

Learning Resources


๐Ÿ“Š Final Statistics

Metric Value Notes
Time to Solve 00:20 From start to flag
Global Ranking (At the time of flag submission) Challenge ranking
Points Earned 500 Team contribution

Created: 20-02-2025 โ€ข Last Modified: 20-02-2025 *Author: mH4ck3r0n3 โ€ข Team: *