Challenge
L5D
is another web challenge in our Balsn CTF 2020.
In this challenge, it combined multiple deserialization tricks.
- Type: Web
- Solved: 17
- Author: kaibro
Description
「Taking L5D was a profound experience, one of the most important things in my life.」 |
Solution
In the website, the author puts the source code on /?action=src
.
You can check the source code here.
From the source code, we can see several classes with __wakeup
and __destruct
functions.
In addition, there is a unserialize
function with a user-controllable input.
Therefore, just as the description of this challenge, it is an "Unserialize-Oriented Programming System".
Some people call this exploitation as POP chain. (Property Oriented Programming chain) As for how to utilize those classes (gadgets), we need to read it more carefully.
Deserialization Gadget
After scanning the gadgets, we can realize the function of each gadget.
L5D_Command
- This gadget can trigger
system
function when$is_unser_finished
is true. - Obviously, it is required to reach RCE.
- This gadget can trigger
L5D_ResetCMD
- It can set global variable
$cmd
when$_SESSION['name']
equals to"wubalubadubdub"
. - To reach arbitrarily code execution, this gadget is needed to replace the initial command (
whoami
).
- It can set global variable
L5D_Login
- This one can set
$_SESSION['name']
to"wubalubadubdub"
if we can know the hash of/flag
. - It looks helpful, however, it is unlikely to obtain the hash of
/flag
.
- This one can set
L5D_SayMyName
- It just echoes the
$_SESSION['name']
- We can use this to check the result of deserialization during testing.
- It just echoes the
L5D_Upload
- This gadget can set
$GLOBALS
if an image is successfully uploaded. - Somehow we can use it to revise some global variables.
- This gadget can set
Global variables overwrite
In the L5D_Upload
, it noticeworthy that part of the code seems vulnerable.
foreach ($_FILES as $key => $value) |
With that, we can overwrite all of the global variables. However, after testing, we can only assign a value in a specific structure.
According to the PHP document, a $_FILES
object is an array of file objects.
And each file object consists of name, type, tmp_name, error, and size attributes.
For example, when I upload l5d_file
, the $_FILES
object would be like following:
array(1) { |
Thus, we can not rewrite the following useful variables to certain strings.
$status
$cmd
$is_unser_finished
Fortunately, we can control the $_SESSION['name']
via uploading another file object called _SESSION
and controlling the name attribute.
It means that we can try to set the command to execute.
WAF Bypass
To set protected attribute, it necessary to use *
.
After lots of searching, I find a trick can bypass this character in PHPGGC.
According to this document, we can replace s
with S
to assigned a string with hexadecimal ASCII code.
That is to say, to represent s:1:*
, we can simply use S:1:\2A
to bypass the WAF.
Connect Gadgets
To connect all the gadgets, we need to call and free each gadget at the proper timing.
From PHPGGC, we can find another trick called 'fast destruct'. When users assign two values with the same key in an array, the first one value would be destroyed automatically.
Therefore, I can connect gadgets in arbitrary order by a crafted array. So I could trigger functions by the following sequence and reach RCE.
- call
L5D_ResetCMD
- call
L5D_Command
- call
L5D_Upload
- free
L5D_Upload
- free
L5D_ResetCMD
L5D_Command
free by default
Therefore, my exploitation script would be like below:
import requests |
Flag:
BALSN{Link_5_Destruct__is_toooo_easy_for_you:D} |
Founding and Trials
Failed Attempt 1 - Error hash
At first, I tried to test the hash checking in L5D_Login
.
For some cases, if the reading occurs failure, it would return false
.
And the result of hash function could be expected.
Yet, it is not the case here.
Failed Attempt 2 - Global variables overwrite
According to PHP documentation, we can control the $_FILES
object by assigning a file name with[]
.
By this technique, we can control the depth and structure of the object.
However, after testing, it is unlikely to control the variables like $status
, $cmd
, $is_unser_finished
to our expected value.