shell.php looked like a webshell you’d find left behind by an attacker. See the bottom of this post for the full challenge code.
It wants a password, hashes it, and then decrypts that big base64 encoded string while using the decrypted plaintext as key expansion. The key itself is very long and will take centuries to brute force, so there must be a smarter way to go about this.
The key length
First, we’ll need to know the key length of the key that will be put into $o_o
. This is easy to determine: start XOR’ing with a plausible key (one that contains the ASCII-characters 0-9 and a-f) and see if the resulting data is also all ASCII-characters. If yes: that’s the key. Turns out; the key is 64 bytes long.
Getting the actual key
Since we know the encrypted data is going to be PHP-code and that code is being used as key expansion, we can start guessing what might be in there. To do this, I wrote a little script that takes a string and XORs it with the encrypted data, incrementing the position of where to start by 1 each time. I tried this with a few commonly used PHP-functions. For instance, searching for preg_replace(
revealed the text < strlen($d))
at offset 1843.
Now that we have a known piece of plaintext at a certain offset, we can start working backwards. Knowing that we have 13 bytes at 1843 and that the key is 64 bytes long, we can work backwards to reveal key bytes 51 to 64. Doing this a couple of more times, we finally get the key:
db6952b84a49b934acb436418ad9d93d237df05769afc796d067bccb379f2cac
Layer 2
Decrypting the first part gives a new script:
<?php
$d='';
$key = "";
if (isset($_POST['o_o']))
$key = $_POST['o_o'];
if (isset($_POST['hint']))
$d = "www.p01.org";
if (isset($_POST['t'])) {
if ($_POST['t'] == 'c') {
$d = base64_decode('SDcGHg1feVUIEhsbDxFhIBIYFQY+VwMWTyAcOhEYAw4VLVBaXRsKADMXTWxrSH4ZS1IiAgA3GxYUQVMvBFdVTysRMQAaQUxZYTlsTg0MECZSGgVcNn9AAwobXgcxHQRBAxMcWwodHV5EfxQfAAYrMlsCQlJBAAAAAAAAAAAAAAAAAFZhf3ldEQY6FBIbGw8RYlAxGEE5PkAOGwoWVHgCQ1BGVBdRCAAGQVQ2Fk4RX0gsVxQbHxdKMU8ABBU9MUADABkCGHdQFQ4TXDEfW0VDCkk0XiNcRjJxaDocSFgdck9CTgpPDx9bIjQKUW1NWwhERnVeSxhEDVs0LBlIR0VlBjtbBV4fcBtIEU8dMVoDACc3ORNPI08SGDZXA1pbSlZzGU5XVV1jGxURHQoEK0x+a11bPVsCC1FufmNdGxUMGGE=');
$key = preg_replace('/(.)../', '$1', $key);
}
if ($_POST['t'] == 's') {
$d = base64_decode('VBArMg1HYn1XGAwaAw1GDCsACwkeDgABUkAcESszBEdifVdNSENPJRkrNwgcGldMHFVfSEgwOjETEE9aRlJoZFMKFzsmQRALSilMEQsXHEUrPg9ZDRAoAwkBHVVIfzkNGAgaBAhUU00AAAAAAAAAAAAAAAAASkZSVV0KDAUCHBFQHA0MFjEVHB0BCgBNTAJVX3hkAkQiFh8ESw0AG0M5MBNRGkpdWV4bVEEVdGJGRR9XGBgcAgpVCDAsCA0GGAVWBAwcBxQqKwRCGxgbVkJFR11IdHcbRFxOUkNNV0RAVXIKSgxCWk1aVkdGQVI8dxRTVl5CR0JLVAQdOStbXkRfXlxOFEULUCp2SFJIUlVGQlUtRhExMQQLJyMmIFgDTUQtYmZIRUAECB4MHhtWRHA9Dh0WSWZmWUEHHBUzYQ==');
$key = preg_replace('/.(.)./', '$1', $key);
}
if ($_POST['t'] == 'w') {
$d = base64_decode('DycdGg1hYjl8FURaAVZxPhgNOQpdMxVIRwNKc0YDCCsDVn5sJxJMHmJJOgArB1olFA0JHQN+TlcpOgFBKUEAA1M+RVUVDjsWEy8PQUEMV3IsSgJxCFY0IkJAGVY3HV9DbQsRaU1eSxl6IR0SEykOX2gnEAwZGHJHRU0OUn4hFUUADlw8UhRPNwpaJwlZE14Df1IRDi1HS30JFlZAHnRAEQ4tR0p9CRZXQB50LFkHNgNfEgROWkVLZV1bGHVbHyJMSRFZCQtGRU0bQAFpSEtBHxsLVEdaeEEUfCd2akdKYAFaJXBdT3BeHBRFV3IdXCV1PhsUXFUBBR5hXFwwdxsab1kECFoaM0FET2pEd2owBXpAC2ZAS11sMhVmJREWVlFyDV4ldFIdcUMBWlBbcl5CSGFTUCEPW08eEyYNSgJhYjl8Tk9BCUpvDxsAODBeLwUfE08AAAAAAAAAAAAAAAAAEXFkfV1wB0ctDRM=');
$key = preg_replace('/..(.)/', '$1', $key);
}
while(strlen($key) < strlen($d))
$key = $key.$key;
$d = $d ^ $key;
}
if (strlen($d))
echo $d;
else
echo '<form action="shell.php" method="post"><input type="hidden" name="o_o" value="'.$key.'"><input type="radio" name="t" value="c"> Raytraced Checkboard<br> <input type="radio" name="t" value="s"> p01 256b Starfield<br> <input type="radio" name="t" value="w"> Wolfensteiny<br><input type="submit" value="Show"/></form>';
This is a new mechanism which is easier - each choice takes a part of the key, then expands it by just repeating it and XOR’ing the encrypted data of the three options with it. The code even contains a hint of a website that actually contains the plaintext - great!
So, by taking parts of the real plaintext from www.p01.org we get three parts:
_4froct_rsaat_4froct_rsaat_4froct_rs
yowkleno hx__ayowkleno hx__ayowkl
[email protected][email protected]_
We need to chop and re-order these a bit, based on the knowledge that the flag ends in .com:
t_rsaat_4froc
hx__ayowkleno
[email protected]
Now read top-to-bottom, left-to-right and there is the flag! :)
This was a cool challenge and, if it was based on an actual webshell, a pretty funny reminder not to roll your own crypto.
Original challenge code
<?php
$o__o_ = base64_decode('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');
$o_o = isset($_POST['o_o'])?$_POST['o_o']:"";
$o_o = md5($o_o).substr(MD5(strrev($o_o)),0,strlen($o_o));
for($o___o=0;$o___o<2268;$o___o++){
$o__o_[$o___o]=chr((ord($o__o_[$o___o])^ord($o_o[$o___o]))%256);
$o_o.=$o__o_[$o___o];
}
if(MD5($o__o_)=='43a141570e0c926e0e3673216a4dd73d'){
if(isset($_POST['o_o']))
@setcookie('o_o', $_POST['o_o']);
$o___o=create_function('',$o__o_);
unset($o_o,$o__o_);
$o___o();
} else {
echo '<form method="post" action="shell.php"><input type="text" name="o_o" value=""/><input type="submit" value=">"/></form>';
}
?>