sugar free candies
2 minutos de lectura
Se nos proporciona el código fuente en Python para cifrar la flag:
from Crypto.Util.number import bytes_to_long
FLAG = open("flag.txt", "rb").read()
step = len(FLAG) // 3
candies = [bytes_to_long(FLAG[i:i+step]) for i in range(0, len(FLAG), step)]
cnd1, cnd2, cnd3 = candies
with open('output.txt', 'w') as f:
f.write(f'v1 = {cnd1**3 + cnd3**2 + cnd2}\n')
f.write(f'v2 = {cnd2**3 + cnd1**2 + cnd3}\n')
f.write(f'v3 = {cnd3**3 + cnd2**2 + cnd1}\n')
f.write(f'v4 = {cnd1 + cnd2 + cnd3}\n')
Y esta es la salida del script:
v1 = 1181239096013650837744125294978177790419553719590172794906535790528758829840751110126012179328061375399196613652870424327167341710919767887891371258453
v2 = 2710472017687233737830986182523923794327361982506952801148259340657557362009893794103841036477555389231149721438246037558380601526471290201500759382599
v3 = 3448392481703214771250575110613977019995990789986191254013989726393898522179975576074870115491914882384518345287960772371387233225699632815814340359065
v4 = 396216122131701300135834622026808509913659513306193
Análisis del código fuente
En términos matemáticos, el script divide la bandera en tres partes y las traduce a números enteros $\mathrm{cnd}_1$, $\mathrm{cnd}_2$ y $\mathrm{cnd}_3$. Luego, calcula los valores $v_1$, $v_2$, $v_3$ y $v_4$ como sigue:
$$ \begin{cases} v_1 = (\mathrm{cnd}_1)^3 + (\mathrm{cnd}_3)^2 + \mathrm{cnd}_2 \\ v_2 = (\mathrm{cnd}_2)^3 + (\mathrm{cnd}_1)^2 + \mathrm{cnd}_3 \\ v_3 = (\mathrm{cnd}_3)^3 + (\mathrm{cnd}_2)^2 + \mathrm{cnd}_1 \\ v_4 = \mathrm{cnd}_1 + \mathrm{cnd}_2 + \mathrm{cnd}_3 \end{cases} $$
Solución
El objetivo es encontrar los valores de $\mathrm{cnd}_1$, $\mathrm{cnd}_2$ y $\mathrm{cnd}_3$. En otras palabras, necesitamos resolver el sistema de ecuaciones anterior. Dado que tenemos 3 incógnitas y 4 ecuaciones, debe ser posible. Además, sabemos que existe una solución, porque los valores $v_1$, $v_2$, $v_3$ y $v_4$ se construyen a partir de la flag.
Podríamos tomar papel y lápiz e intentar resolver esto a mano usando matemáticas simbólicas. Sin embargo, podemos usar un programa de ordenador para resolver esto de manera más rápida. En SageMath, podemos usar solve
así:
$ sage -q
sage: v1 = 1181239096013650837744125294978177790419553719590172794906535790528758829840751110126012179328061375399196613652870424327167341710919767887891371258453
....: v2 = 2710472017687233737830986182523923794327361982506952801148259340657557362009893794103841036477555389231149721438246037558380601526471290201500759382599
....: v3 = 3448392481703214771250575110613977019995990789986191254013989726393898522179975576074870115491914882384518345287960772371387233225699632815814340359065
....: v4 = 396216122131701300135834622026808509913659513306193
sage:
sage: cnd1, cnd2, cnd3 = var('cnd1, cnd2, cnd3')
sage:
sage: sol = solve([
....: v1 == cnd1 ** 3 + cnd3 ** 2 + cnd2,
....: v2 == cnd2 ** 3 + cnd1 ** 2 + cnd3,
....: v3 == cnd3 ** 3 + cnd2 ** 2 + cnd1,
....: v4 == cnd1 + cnd2 + cnd3
....: ], cnd1, cnd2, cnd3, solution_dict=True)
sage: sol
[{cnd1: 105709155715849064931516800270848071425751222546035,
cnd2: 139427457951053646347891878266599404989598900055137,
cnd3: 151079508464798588856425943489361033498309390705021}]
Y allí tenemos la solución al sistema de ecuaciones.
Flag
En este punto, podemos transformar cada valor a bytes y unirlos todos:
sage: b''.join(bytes.fromhex(hex(v)[2:]) for v in sol[0].values())
b'HTB{solving_equations_for_parts_of_the_flag_over_the_integers!}'