๐ Super Serial
A detailed write-up of the Web challenge 'Super Serial' from PicoCTF - 2021
๐ 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
1Files: None
๐ Initial Analysis
First Steps
Initially, the website appears as follows:
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 aformwithaction="index.php". So, I tried checking forrobots.txt:and this returned a positive result. As we can see, thereโs a
Disallow: /admin.phps, but.phpsseemed 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 finals, it was possible to see thephpcode:up to this point, nothing too strange, except for that
serialize()which immediately made me think of anInsecure Deserialization. I also noticed that arequire_once("cookie.php");was specified, so I tried visiting the route/cookie.phpsand got the source:it also references another file with a
.phpextension, namelyauthentication.php, so I tried reading the source the same way with/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 10if(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
logincookie is set (in the rest of the code, the login cookie is set only if the login is successful, either as aguestor as anadmin). So, if the cookie exists, it is URL-decoded, base64-decoded, and then deserialized. Therefore, this cookie should be a serializedpermissionsobject, especially since theis_guest()andis_admin()functions, which belong to thepermissionclass, are called afterward. Finally, if any exception is thrown or any type of error occurs, thereโs acatchblock that prints the error and the$permobject (which should be thepermissionclass object). However, as we can see:
1 2 3 4function \__toString() { return $u.$p; }the
permissionclass implements themagic method __toString(). This method defines the behavior of the object when any type of casting to a string is performed. In this case, it concatenatesusernameandpassword, so when a casting is done, as we saw in the previousifstatement, it will print$u.$p. This behavior can be exploited by causing any kind of error during the deserialization of the object in thetryblock. 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 isauthentication.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 25class 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: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 thosefile_get_contentsfunctions will certainly be useful for something. The other vulnerable part is therequire_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 thepermissionclass, 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 theread_log()function is called, we could read the../flagfile instead of theaccess.logfile. 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../flagfile. In fact, by deserializing a maliciousaccess_logobject with$log_file = "../flag", and setting it as thelogincookie, it will enter theifstatement, but when it calls$perm->is_guest(), we will get an error, because we are not using an object of thepermissionclass, which has theis_guest()function declared, but rather using an object of theaccess_logclass, which doesnโt have that function. This will lead to thecatch, and thatโs where the magic happens. In fact, as we can see, theaccess_logobject also declares the magic method__toString(), which calls theread_log()function. So when the exception occurs, it will printDeserialization error. File content passed to the $log_file parameter, because it will cast the object to a string, call theread_log()function, which will grab the content of the../flagfile using thefile_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:10indicates the number of characters inaccess_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 finds:8:"log_file";s:7:"../flag";wheresrepresents the type (String),8is the number of characters inlog_file, and following the same logic, we find the value we want to pass to thelog_fileattribute, 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 theaccess_logclass, correct? So, a much simpler approach would be to copy theaccess_logclass into a.phpfile, 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 fromurlencode, then frombase64, 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:Once serialized, all we have to do is open
ChromeDevTools, add thelogin = Malicious Serialized Objectcookie, and visit the/authentication.phproute to obtain the flag.
Flag capture
๐ ๏ธ 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 thelogincookie aslogin=malicious_serialized_objectand a request is made usingPHP cURL, extracting the flag from the response using a regex.
๐ฉ Flag Capture
Flag
Proof of Execution
๐ง 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: *