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执行成功

您可能还喜欢...