Spooky License
4 minutos de lectura
Se nos proporciona un binario llamado spookylicense:
$ file spookylicence
spookylicence: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b4f93c6f8f1c73bbd5f9c2b6862be023abed1b47, for GNU/Linux 3.2.0, stripped
Ingeniería inversa
Si usamos Ghidra, veremos esta función main:
int main(int argc, char **argv) {
int ret;
size_t length;
char *data;
if (argc == 2) {
length = strlen(argv[1]);
if (length == 32) {
data = argv[1];
if ((((((((data[0x1d] == (char) ((data[5] - data[3]) + 0x46)) &&
((char) (data[2] + data[0x16]) == (char) (data[0xd] + 0x7b))) &&
((char) (data[0xc] + data[4]) == (char) (data[5] + 0x1c))) &&
((((char) (data[0x19] * data[0x17]) == (char) (*data + data[0x11] + 0x17) &&
((char) (data[0x1b] * data[1]) == (char) (data[5] + data[0x16] - 0x15))) &&
(((char) (data[9] * data[0xd]) == (char) (data[0x1c] * data[3] - 9) &&
((data[9] == 0x70 && ((char) (data[0x13] + data[0x15]) == (char) (data[6] - 0x80)))))))
)) && (data[0x10] == (char) ((data[0xf] - data[0xb]) + 0x30))) &&
(((((((char) (data[7] * data[0x1b]) == (char) (data[1] * data[0xd] + 0x2d) &&
(data[0xd] == (char) (data[0x12] + data[0xd] - 0x65))) &&
((char) (data[0x14] - data[8]) == (char) (data[9] + 0x7c))) &&
((data[0x1f] == (char) ((data[8] - data[0x1f]) - 0x79) &&
((char) (data[0x14] * data[0x1f]) == (char) (data[0x14] + 0x04))))) &&
((char) (data[0x18] - data[0x11]) == (char) (data[0x15] + data[8] - 0x17))) &&
((((char) (data[7] + data[5]) == (char) (data[5] + data[0x1d] + 0x2c) &&
((char) (data[0xc] * data[10]) == (char) ((data[1] - data[0xb]) - 0x24))) &&
((((char) (data[0x1f] * *data) == (char) (data[0x1a] - 0x1b) &&
((((char) (data[1] + data[0x14]) == (char) (data[10] - 0x7d) &&
(data[0x12] == (char) (data[0x1b] + data[0xe] + 0x02))) &&
((char) (data[0x1e] * data[0xb]) == (char) (data[0x15] + 0x44))))) &&
((((char) (data[5] * data[0x13]) == (char) (data[1] - 0x2c) &&
((char) (data[0xd] - data[0x1a]) == (char) (data[0x15] - 0x7f))) &&
(data[0x17] == (char) ((data[0x1d] - *data) + 0x58))))))))))) &&
(((data[0x13] == (char) (data[8] * data[0xd] - 0x17) &&
((char) (data[6] + data[0x16]) == (char) (data[3] + 0x53))) &&
((data[0xc] == (char) (data[0x1a] + data[7] - 0x72) &&
(((data[0x10] == (char) ((data[0x12] - data[5]) + 0x33) &&
((char) (data[0x1e] - data[8]) == (char) (data[0x1d] - 0x4d))) &&
((char) (data[0x14] - data[0xb]) == (char) (data[3] - 0x4c))))))))) &&
(((char) (data[0x10] - data[7]) == (char) (data[0x11] + 0x66) &&
((char) (data[1] + data[0x15]) == (char) (data[0xb] + data[0x12] + 0x2b))))) {
puts("License Correct");
ret = 0;
} else {
puts("License Invalid");
ret = -1;
}
} else {
puts("Invalid License Format");
ret = -1;
}
} else {
puts("./spookylicence <license>");
ret = -1;
}
return ret;
}
Aquí vemos que necesitamos agregar una licencia como argumento. Además, la longitud de licencia debe ser 32.
Luego, hay un gran bloque if que realiza muchas comprobaciones en los caracteres de la licencia.
Resolución con z3
Esta es una situación perfecta para usar z3, solo necesitamos definir algunas variables en un modelo y agregar las restricciones, para que z3 pueda encontrar una solución:
#!/usr/bin/env python3
from z3 import BitVec, Solver
a = [BitVec(f'a{i}', 8) for i in range(32)]
s = Solver()
s.add(a[0x1d] == a[5] - a[3] + 0x46)
s.add(a[2] + a[0x16] == a[0xd] + 0x7b)
s.add(a[0xc] + a[4] == a[5] + 0x1c)
s.add(a[0x19] * a[0x17] == a[0] + a[0x11] + 0x17)
s.add(a[0x1b] * a[1] == a[5] + a[0x16] - 0x15)
s.add(a[9] * a[0xd] == a[0x1c] * a[3] - 9)
s.add(a[9] == 0x70)
s.add(a[0x13] + a[0x15] == a[6] - 0x80)
s.add(a[0x10] == a[0xf] - a[0xb] + 0x30)
s.add(a[7] * a[0x1b] == a[1] * a[0xd] + 0x2d)
s.add(a[0xd] == a[0x12] + a[0xd] - 0x65)
s.add(a[0x14] - a[8] == a[9] + 0x7c)
s.add(a[0x1f] == a[8] - a[0x1f] - 0x79)
s.add(a[0x14] * a[0x1f] == a[0x14] + 0x04)
s.add(a[0x18] - a[0x11] == a[0x15] + a[8] - 0x17)
s.add(a[7] + a[5] == a[5] + a[0x1d] + 0x2c)
s.add(a[0xc] * a[10] == a[1] - a[0xb] - 0x24)
s.add(a[0x1f] * a[0] == a[0x1a] - 0x1b)
s.add(a[1] + a[0x14] == a[10] - 0x7d)
s.add(a[0x12] == a[0x1b] + a[0xe] + 0x02)
s.add(a[0x1e] * a[0xb] == a[0x15] + 0x44)
s.add(a[5] * a[0x13] == a[1] - 0x2c)
s.add(a[0xd] - a[0x1a] == a[0x15] - 0x7f)
s.add(a[0x17] == a[0x1d] - a[0] + 0x58)
s.add(a[0x13] == a[8] * a[0xd] - 0x17)
s.add(a[6] + a[0x16] == a[3] + 0x53)
s.add(a[0xc] == a[0x1a] + a[7] - 0x72)
s.add(a[0x10] == a[0x12] - a[5] + 0x33)
s.add(a[0x1e] - a[8] == a[0x1d] - 0x4d)
s.add(a[0x14] - a[0xb] == a[3] - 0x4c)
s.add(a[0x10] - a[7] == a[0x11] + 0x66)
s.add(a[1] + a[0x15] == a[0xb] + a[0x12] + 0x2b)
s.check()
model = s.model()
flag = ''.join(chr(model[i].as_long()) for i in a)
print(flag)
Flag
Si ejecutamos el código anterior, encontraremos la flag:
$ python3 solve.py
HTB{The_sp0000000key_liC3nC3K3Y}
El script completo se puede encontrar aquí: solve.py.