题目描述
题目给出一个二进制文件 rechall,要求输入正确的 flag 通过验证。
运行程序示例:
解题步骤
0x01 初步分析
-
使用
file
命令检查文件类型1 2
$ file rechall rechall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=5e62deaec60953b0ae6e9e22f18f228f1d75e2f2, for GNU/Linux 3.2.0, not stripped
-
用
strings
查找字符串1 2 3 4 5 6 7 8 9
$ strings ./rechall /lib64/ld-linux-x86-64.so.2 libc.so.6 sprintf .... flag{%08x-%08x-%08x-%08x} Right! Your flag is: %s ...
发现flag格式
0x02 静态分析
-
用IDA打开反编译程序,重命名部分变量。发现flag是由四个十六进制数字组成的
-
透过观察发现要输入的四个数字都通过checker1-4的检查才能拿到flag,并且输入的数字转换成16进制拼在一起就是flag。
-
分别进入checker1-4把代码提取出来
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 46 47 48 49
__int64 __fastcall checker1(int a1, int a2) { __int64 result; // rax if ( a1 - a2 == 0x956438B9 ) result = 0LL; else result = 4294967295LL; return result; } __int64 __fastcall checker2(int a1) { __int64 result; // rax if ( 3 * (a1 & 0x52E3F96B) - (a1 | 0xAD1C0694) - a1 == -477662227 ) result = 0LL; else result = 0xFFFFFFFFLL; return result; } __int64 __fastcall checker3(unsigned int a1) { if ( a1 > 0x10000000 ) return 0xFFFFFFFFLL; if ( -3 * (~a1 | 0xD68E3FE9) + 3 * ~(a1 | 0xD68E3FE9) + 4 * ((~a1 & 0xD68E3FE9) + 2 * ~(~a1 | 0xD68E3FE9)) + 10 * (a1 & 0xD68E3FE9) - (a1 ^ 0xD68E3FE9) == 0x251BC4BD ) return 0LL; return 0xFFFFFFFFLL; } __int64 __fastcall checker4(unsigned int a1) { if ( a1 > 0x10000000 ) return 0xFFFFFFFFLL; if ( 11 * (a1 ^ 0xAE7C284F) + 4 * (a1 & 0xAE7C284F) - (6 * (a1 & 0x5183D7B0) + 12 * ~(a1 | 0x5183D7B0)) + -5 * a1 - 2 * ~(a1 | 0x4EDA6B7C) - (a1 | 0xB1259483) + 3 * (a1 & 0x4EDA6B7C) + 4 * (a1 & 0xB1259483) - -2 * (a1 | 0xB1259483) == 0xD9E7538A ) return 0LL; return 0xFFFFFFFFLL; }
这里一定要让checkers返回0LL才能继续
-
使用z3库编写求解器脚本,先安装一下z3
1
pip install z3-solver
0x03 编写解密脚本
-
开始编写脚本
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
from z3 import * def solve_checker4(): solver = Solver() a1 = BitVec('a1', 32) solver.add(a1 <= 0x10000000) expr = ( 11 * (a1 ^ 0xAE7C284F) + 4 * (a1 & 0xAE7C284F) - (6 * (a1 & 0x5183D7B0) + 12 * ~(a1 | 0x5183D7B0)) + -5 * a1 - 2 * ~(a1 | 0x4EDA6B7C) - (a1 | 0xB1259483) + 3 * (a1 & 0x4EDA6B7C) + 4 * (a1 & 0xB1259483) - -2 * (a1 | 0xB1259483) ) solver.add(expr == 0xD9E7538A) if solver.check() == sat: model = solver.model() return model[a1].as_long() else: return None def solve_checker3(): solver = Solver() a1 = BitVec('a1', 32) solver.add(a1 <= 0x10000000) expr = ( -3 * (~a1 | 0xD68E3FE9) + 3 * ~(a1 | 0xD68E3FE9) + 4 * ((~a1 & 0xD68E3FE9) + 2 * ~(~a1 | 0xD68E3FE9)) + 10 * (a1 & 0xD68E3FE9) - (a1 ^ 0xD68E3FE9) ) solver.add(expr == 0x251BC4BD) if solver.check() == sat: model = solver.model() return model[a1].as_long() else: return None def solve_checker2(): solver = Solver() a1 = BitVec('a1', 32) solver.add(a1 <= 0x10000000) expr = ( 3 * (a1 & 0x52E3F96B) - (a1 | 0xAD1C0694) - a1 ) solver.add(expr == 0xE38773ED) if solver.check() == sat: model = solver.model() return model[a1].as_long() else: return None # 这里其实可以直接算出来checker1需要的值是多少, # 因为伪代码里面已经往checker1里面传入了0x5294B771 def solve_checker1(): return 0x956438B9 + 0x5294B771 solvers = [ solve_checker1, solve_checker2, solve_checker3, solve_checker4 ] times = 0 for i in solvers: times += 1 print(f"flag{times}: {int(i()) if i() is not None else 'No solution'}")
-
运行脚本得到十进制的flag:
1 2 3 4 5
$ python -u ./decryptor.py flag1: 3891851306 flag2: 2426636161 flag3: 88939547 flag4: 232752086
-
把flag输入进程序:
0x04 Flag!
- 得到最终flag
flag{e7f8f02a-90a38781-054d1c1b-0ddf83d6}