合作者:@9vvert
组织使用了一个定制的虚拟机程序来保护他们的秘密。你需要找到能够通过验证的口令。有时候,最复杂的迷宫也有它的规律...
The organization used a customized virtual machine program to protect their secrets. You need to find a password that can be verified. Sometimes, even the most complex maze has its own patterns ..
题目给了一个二进制需要逆向,通过标准输入读取 flag,然后打印比较是否成功。经过逆向,可以发现 flag 一共有 24 字节,在循环中,每次循环对输入的一个字节进行一定的操作,然后判断是否成功。一般对于这种检查前缀的题目,思路是:
为了得到程序匹配成功的前缀长度,传统方法是利用 QEMU 进行侧信道测量,例如 Instruction Stomp,但在这里,@9vvert 提出了一个更巧妙的办法:当循环中比较失败的时候,把循环的迭代次数寄存器(在这里是 r15)作为 main 的返回值,那么就可以直接通过 exit code 来判断匹配的前缀长度了。当然了,原来的二进制并没有这个功能,所以需要进行一些 patch:
--- hyperjump_orig.S
+++ hyperjump.S
@@ -1,5 +1,5 @@
-hyperjump_orig: file format elf64-x86-64
+hyperjump: file format elf64-x86-64
Disassembly of section .init:
@@ -288,7 +288,9 @@
143f: c3 ret
1440: 48 8d 3d 14 42 00 00 lea 0x4214(%rip),%rdi # 565b <__cxa_finalize@plt+0x457b>
1447: e8 04 fc ff ff call 1050 <puts@plt>
- 144c: b8 01 00 00 00 mov $0x1,%eax
+ 144c: 44 89 f8 mov %r15d,%eax
+ 144f: 90 nop
+ 1450: 90 nop
1451: eb db jmp 142e <__cxa_finalize@plt+0x34e>
1453: 48 8d 3d f5 41 00 00 lea 0x41f5(%rip),%rdi # 564f <__cxa_finalize@plt+0x456f>
145a: e8 f1 fb ff ff call 1050 <puts@plt>
有了这个以后,再去搜索就简单很多了。但是实现的时候,发现在特定位置,有很多种解,这就导致 DFS 的效果比较差,所以这里改成了 BFS,搜索的就比较快了:
from pwn import *
import string
import subprocess
guess = list("flag{") + ["A"] * 19
assert len(guess) == 24
jobs = []
jobs.append((guess, 5))
while len(jobs) > 0:
job = jobs[0]
jobs = jobs[1:]
guess, i = job
if i == 24:
continue
res = []
for ch in string.ascii_letters + string.digits + string.punctuation:
guess[i] = ch
p = subprocess.Popen(
[
"./hyperjump_patched",
],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
p.communicate(input="".join(guess).encode(), timeout=10)[0]
count = p.returncode
if count == 0:
# correct
count = 24
res.append((ch, count))
# print("".join(guess), count)
print("Best:")
best = list(sorted(res, key=lambda count: count[1]))[-1]
for j in range(len(res)):
if res[j][1] == best[1]:
guess[i] = res[j][0]
print("".join(guess), best[1])
jobs.append((guess.copy(), i + 1))
结果:
Best:
flag{m4i3d_vm_jump5__42} 24
Best:
flag{m4z3d_vm_jump5__42} 24
Best:
flag{m4!3d_vm_jump5__42} 24
Best:
flag{]4i3d_vm_jump5__42} 24
Best:
flag{]4z3d_vm_jump5__42} 24
Best:
flag{]4!3d_vm_jump5__42} 24
经过尝试,flag{m4z3d_vm_jump5__42} 是正确的 flag。