[GDOUCTF 2023]真男人下120层
nssctf地址:https://www.nssctf.cn/problem/3663
不是平常的漏洞利用,主要是针对随机数的条件匹配
看了一些wp发现很多都是用ctypes这个库,了解了一下还是很有学习的必要,顺便学习一下新的函数
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
|
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
int v4; // eax
int v6; // [rsp+8h] [rbp-18h] BYREF
unsigned int i; // [rsp+Ch] [rbp-14h]
__int64 v8; // [rsp+10h] [rbp-10h]
unsigned __int64 v9; // [rsp+18h] [rbp-8h]
v9 = __readfsqword(0x28u);
setbuf(_bss_start, 0LL);
v3 = time(0LL); // 返回当前时间戳
srand(v3); // 设置随机数种子
v8 = 2772839826LL;
v4 = rand(); // 依据上面的种子生成随机数
srand(v4 % 3 - 1522127470); // 重新设置种子
printf("\x1B[31m");
puts("\n\n");
puts(" ## ## ####### ####### ######## ####### ");
puts(" ## ## ### ## ## ## ");
puts(" ####### ### ## ## #### ");
puts(" ## ## ### ## ## ## ");
puts(" ## ## ####### ####### ## ## \n\n");
printf("\x1B[0m");
puts("Welcome!");
puts(aThereAreFourDo);
puts("Only one door leads to the right path. If you choose the wrong one, you will be killed by a trap.\n");
for ( i = 1; (int)i <= 120; ++i ) // 通过120次的条件满足输出flag
{
print_door();
printf("\t\t\tFloor %d\n\n", i);
__isoc99_scanf("%d", &v6);
if ( rand() % 4 + 1 != v6 )
{
printf("\x1B[31m");
puts("YOU DIED!");
printf("\x1B[0m");
return 0;
}
system("clear");
}
puts("Congratulation!");
cat_flag();
return 0;
}
|
time()
这是一个返回距离1970年1月1日00:00:00 UTC经过的秒数
1
|
time_t time(time_t *t);
|
该函数有两种用法
- 返回值接收时间戳,参数为0或NULL
- 变量接收时间戳,函数参数为该变量指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <stdio.h>
#include <time.h>
int main()
{
time_t current_time_1 = time(0); // 1
time_t current_time_2 = time(NULL); // 1
time_t current_time_3;
time(¤t_time_3); // 2
printf("current_time_1=%ld\n",current_time_1);
printf("current_time_2=%ld\n",current_time_2);
printf("current_time_3=%ld\n",current_time_3);
return 0;
}
|

srand()
用于设置伪随机数种子的函数,种子决定随机数的值,为了让伪随机不那么伪随机通常参数用当前的时间戳
1
|
void srand(unsigned int seed);
|
rand()
返回伪随机数的函数
没有调用srand()设置随机数种子,就相当于srand(1),也就是默认种子是1
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(1);
for(int i = 0;i < 5;i++)
printf("%d ",rand());
printf("\n");
return 0;
}
|

python库ctypes
ctypes是一个提供C语言共享库接口的python标准库,通过ctypes可以在python调用C语言的函数。反编译出来的伪代码显示程序是用某一时刻的时间戳作为随机数种子,除非手速足够快可以让python脚本和C程序同时运行(当然这基本不可能)获取相同的时间戳,不然只能在python调用C函数或者在python中运行C程序来实现时间戳的统一。
ctypes解题
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
|
from pwn import *
import sys
import ctypes
context.log_level = 'debug'
context.arch = 'amd64'
context.terminal = ["tmux","splitw","-v"]
elf = ELF('./bin')
if len(sys.argv) > 1 and sys.argv[1] == "local":
p = process("./bin")
libc = elf.libc
gdb.attach(p)
else:
p = remote('node4.anna.nssctf.cn',28283)
# 调用C函数
libc = ctypes.CDLL("libc.so.6")
libc.srand.argtypes = [ctypes.c_uint]
libc.srand(libc.time(0))
res = libc.rand()
libc.srand(res % 3 - 1522127470)
p.recvuntil(b"trap.\n")
for i in range(120):
rand_r = libc.rand() % 4 + 1
p.sendlineafter(f"Floor {i + 1}\n\n".encode(),str(rand_r).encode())
p.interactive()
|
运行C程序解题
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
|
from pwn import *
import sys
import subprocess
context.log_level = 'debug'
context.arch = 'amd64'
context.terminal = ["tmux","splitw","-v"]
elf = ELF('./bin')
if len(sys.argv) > 1 and sys.argv[1] == "local":
p = process("./bin")
libc = elf.libc
gdb.attach(p)
else:
p = remote('node4.anna.nssctf.cn',28283)
res = subprocess.run(["./demo"], capture_output=True, text=True).stdout
res = res.replace("\n","")
res = res.split()
p.recvuntil(b"trap.\n")
for i in range(120):
rand_r = res[i]
p.sendlineafter(f"Floor {i + 1}\n\n".encode(), rand_r)
p.interactive()
|
当然要提前编译好C程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// gcc demo.c -o demo
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(time(0));
srand(rand() % 3 - 1522127470);
for(int i = 1;i <= 120;i++)
{
printf("%d \n",rand() % 4 + 1);
}
return 0;
}
|
小结
不是什么漏洞利用的pwn题,见过练一练就有思路怎么做了,新手做一做可以开拓下知识面