Protostar Heap 0-2 Write-ups
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(); }
使用ltrace调试程序运行情况
p的地址为0x0804a008
fp地址为 0x0804a050
计算得到 0x50- 0x08 = 0x48 = 72

查看win()函数地址为0x08048464

构造exp
/opt/protostar/bin/heap0 `python -c ‘print “A”*72+”\x64\x84\x04\x08″‘`

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"); }
Gdb调试heap1,无参数输入直接run,会提示在地址0x0804a018报错

输入参数AAAA,运行依然报错,在第二个地址0x0804a038处,需要输入第二个参数

对应的是代码strcpy()中传入的两个参数i1->name和i2->name
当传入两个参数运行时,不会报错

测试输入一个很长字符时,报错,根据错误提示,目标地址被覆盖为0xFFFF

所以,可以通过第一个参数构造需要重写覆盖的地址,第二个参数构造写入什么内容
Backtrace查看调用的堆栈信息,在main函数地址0x0804855a处

查看此地址处汇编

当调用结束strcpy()之后,编译器将源码中printf()变为了puts()
查看puts()地址代码,调用的是puts在GOT表的地址

将测试参数1的待覆盖地址改为puts的GOT地址0x8049774,此时报错的是第二个赋值参数0x0000

此时查看寄存器,可以看到eip = 0x30303030
所以需要将第二个参数的值改为跳转的winner()函数地址就ok了

查看winner地址为0x08048494

构造第二个参数,执行成功

退出gdb执行也是ok的

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"); } } } }
Heap2需要用到UAF
首先简单的测试下几行命令,可以看到默认auth地址为0x804c008,service地址为0x804c018,然后再次申请地址变为0x28和0x38,当使用reset将auth地址free掉之后,再申请的service地址变为0x804c008,但此时auth地址依然指向0x804c008,属于UAF

使用gdb调试heap2,输入auth AAAA,service BBBB

查看堆空间数据,auth值与service值

代码中结构体命名为struct auth{},指针值命名为auth *auth,同样的名称导致代码
auth = malloc(sizeof(auth));
执行时auth值为指针auth *auth,所以长度显示为0x11
使用print查看auth 和*auth

登录成功需要auth->auth的值为非零
通过赋值service较长字符串来覆盖auth->auth值

此时可以看到auth->auth的值非零

而 十进制1179010629 = 0x46464645
查看一下堆数据,对应的是地址0x804c028处值

通过多次对service赋值,来控制地址0x0804c028处值不为0,则login执行成功
