Objective: Write key generator.
Reversing main()
with Ghidra and r2
To obtain final flag we need to provide password that meets folowing requirements:
password[0] + password[1] = 0xa6
password[3] ^ password[5] = 0x50
password[2] + password[6] = 0xd7
password[4] / password[7] = 0x01
password[4] * password[11] = 0x3c0f
password[8] - password[2] = 0x09
password[9] + password[3] = 0x97
password[10] & password[11] = 0x64
password[0] + password[11] = 0xf3
password[1] + password[10] = 0x94
password[9] % password[2] = 0x33
password[3] ^ password[8] = 0x16
password[4] | password[7] = 0x7f
password[5] + password[6] = 0xa2
int main(void)
{
size_t len;
long in_FS_OFFSET;
undefined8 password;
byte local_30;
char cStack_2f;
byte bStack_2e;
byte bStack_2d;
undefined2 uStack_2c;
undefined2 uStack_2a;
undefined6 uStack_28;
undefined8 local_22;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
password = 0;
local_30 = 0;
cStack_2f = '\0';
bStack_2e = 0;
bStack_2d = 0;
uStack_2c = 0;
uStack_2a = 0;
uStack_28 = 0;
local_22 = 0;
__isoc99_scanf(&DAT_00102004,&password);
len = strlen((char *)&password);
if (((((len == 0xc) && ((int)password[1] + (int)(char)password[0] == 0xa6)) &&
((password[3] ^ password.[5]) == 0x50)) &&
((((int)password[6] + (int)password[2] == 0xd7 &&
((int)(char)password[4] / (int)(char)password[7] == 1)) &&
(((int)(char)password[4] * (int)(char)password[5] == 0x3c0f &&
(((int)(char)local_30 - (int)password[2]== 9 &&
((int)(char)password[3] + (int)(char)password[9] == 0x97)))))))) &&
(((password[10] & password[11]) == 100 &&
((((((int)(char)password + (int)(char)password[11] == 0xf3 &&
((int)password[1] + (int)(char)password[10] == 0x94)) &&
((int)cStack_2f % (int)password[2] == 0x33)) &&
(((password[3] ^ local_30) == 0x16 && ((password[4] | password[7]) == 0x7f)))) &&
((int)(char)password[5] + (int)password[6] == 0xa2)))))) {
printf("%s is a valid flag!",&password);
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
Building password
import itertools
from z3 import *
# I have no fucking idea what is going on here, I stole that from someone else
# https://stackoverflow.com/questions/11867611/z3py-checking-all-solutions-for-equation
def models(formula, max = 100):
solver = Solver()
solver.add(formula)
count = 0
while count < max:
count +=1
if solver.check() == sat:
model = solver.model()
yield model
block = []
for z3_decl in model:
arg_domains = []
for i in range(z3_decl.arity()):
domain, arg_domain = z3_decl.domain(i), []
for j in range(domain.num_constructors()):
arg_domain.append( domain.constructor(j) () )
arg_domains.append(arg_domain)
for args in itertools.product(*arg_domains):
block.append(z3_decl(*args) != model.eval(z3_decl(*args)))
solver.add(Or(block))
password = []
requirements = []
for i in range(12):
password.append(BitVec(f"password_{str(i).zfill(2)}", 8))
requirements += [ password[i] >= 0x21, password[i] <= 0x7e ] # we are interested only in printable ascii characters
requirements += [password[0] + password[1] == 0xa6,
password[3] ^ password[5] == 0x50,
password[2] + password[6] == 0xd7,
password[4] / password[7] == 0x01,
password[4] * password[11] == 0x3c0f,
password[8] - password[2] == 0x09,
password[9] + password[3] == 0x97,
password[11] & password[10] == 0x64,
password[0] + password[11] == 0xf3,
password[1] + password[10] == 0x94,
password[9] % password[2] == 0x33,
password[3] ^ password[8] == 0x16,
password[4] | password[7] == 0x7f,
password[5] + password[6] == 0xa2 ]
for m in models(requirements):
m_dict = {d.name():m[d].as_long() for d in m}
print(''.join([chr(m_dict[k]) for k in sorted(m_dict.keys())]))
Output:
v0id{4n?r3d}
v0id{4n_r3d}
v0id{4nWr3d}
v0id{4nGr3d}
v0id{4nFr3d}
v0id{4nfr3d}
v0id{4nvr3d}
v0id{4nwr3d}
v0id{4ngr3d}
v0id{4ner3d}
v0id{4ndr3d}
v0id{4ntr3d}
v0id{4nur3d}
v0id{4nEr3d}
v0id{4nMr3d}
v0id{4nOr3d}
v0id{4nNr3d}
v0id{4nnr3d}
v0id{4nlr3d}
v0id{4nDr3d}
v0id{4nTr3d}
v0id{4nLr3d}
v0id{4n\r3d}
v0id{4n]r3d}
v0id{4nmr3d}
v0id{4nor3d}
v0id{4n>r3d}
v0id{4n^r3d}
v0id{4nVr3d}
v0id{4nUr3d}
Final result:
⚡ root ./a.out
v0id{4nUr3d}
v0id{4nUr3d} is a valid flag!#