本文为人工撰写,仅使用生成式AI校对。
依旧是IDA。
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-14h] BYREF
unsigned int i; // [rsp+10h] [rbp-10h]
int v6; // [rsp+14h] [rbp-Ch]
unsigned __int64 v7; // [rsp+18h] [rbp-8h]
v7 = __readfsqword(0x28u);
init(argc, argv, envp);
initseed();
srand(seed);
puts("My lock looks strange鈥攃an you help me?");
for ( i = 1; (int)i <= 10; ++i )
{
printf("password %d\n>", i);
v6 = rand() % 10000;
__isoc99_scanf("%d", &v4);
if ( v6 != v4 )
lose();
}
win();
return 0;
}
查看initseed()。
__int64 initseed()
{
__int64 result; // rax
int i; // [rsp+8h] [rbp-8h]
int fd; // [rsp+Ch] [rbp-4h]
fd = open("/dev/urandom", 0, 0LL);
if ( fd < 0 )
{
puts("urandom");
exit(1);
}
read(fd, &seed, 3uLL);
close(fd);
seed = seed % 0x64 + 1;
for ( i = 1; i <= 120; ++i )
change();
while ( 1 )
{
result = seed & 1;
if ( (seed & 1) != 0 )
break;
change();
}
return result;
}
注意到seed = seed % 0x64 + 1。
并且:
__int64 change()
{
__int64 result; // rax
if ( (seed & 1) != 0 )
{
result = 3 * seed + 1;
seed = 3 * seed + 1;
}
else
{
result = seed >> 1;
seed >>= 1;
}
return result;
}
这里seed只有0x64种可能,并且后续的change()也是固定算法,我们可以爆破种子。
然后使用ctypes库来生成随机数。
爆破写到一半注意到:
while ( 1 ) { result = seed & 1; if ( (seed & 1) != 0 ) break; change(); } return result;这里只能返回1。“老大我们被耍了!”。
那就直接用1为种子来算即可。
pwntools脚本:
from pwn import *
import ctypes
context(arch='amd64', os='linux', log_level='error')
io = connect("127.0.0.1", 38141)
libc = ctypes.CDLL("libc.so.6")
libc.srand(1)
for i in range(10):
rand = libc.rand() % 10000;
print(rand)
io.sendline(str(rand).encode())
io.interactive()
输出:
9383
886
2777
6915
7793
8335
5386
492
6649
1421
My lock looks strange—can you help me?
password 1
>password 2
>password 3
>password 4
>password 5
>password 6
>password 7
>password 8
>password 9
>password 10
>It opened—how did you do that?
moectf{THIS_IS_FLAG}