Reverse the Fautif binary to uncover the hidden flag and prove its malicious intent.
Decompile the attachment in ghidra:
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
undefined8 main(int argc,char **argv)
{
undefined *puVar1;
int iVar2;
int iVar3;
__ssize_t _Var4;
size_t lineptr_len;
byte *out_buf;
time_t tVar5;
size_t sVar6;
undefined *puVar7;
byte *pbVar8;
byte local_8 [8];
byte *pbVar9;
puVar7 = PTR_DAT_001202b0;
DAT_001202e0 = PTR_DAT_001202b0 + 0x28;
DAT_001202d0 = PTR_DAT_001202b0 + 0x2c;
*(undefined8 *)(PTR_DAT_001202b0 + 0x1c) = 0x100000000;
*(undefined4 *)(puVar7 + 0x24) = 2;
DAT_001202e8 = puVar7 + 0x30;
DAT_001202d8 = &DAT_0012015d;
DAT_001202f0 = s_RFWKA_00120153;
FUN_00100e08();
puVar7 = &DAT_00120183;
do {
puVar1 = puVar7 + 0x1b;
FUN_00100f40(puVar7);
puVar7 = puVar1;
} while (puVar1 != &DAT_0012020a);
FUN_00100e08();
_Var4 = getdelim((char **)&lineptr,(size_t *)&size,-1,_stdin);
if (0 < _Var4) {
FUN_00100f40(lineptr);
}
lineptr_len = strlen((char *)lineptr);
out_buf = (byte *)malloc(lineptr_len + 1);
tVar5 = time((time_t *)0x0);
srand((uint)tVar5);
iVar2 = rand();
local_8[0] = (byte)iVar2;
iVar3 = rand();
pbVar9 = lineptr;
local_8[1] = (char)iVar3;
if (lineptr_len != 0) {
*out_buf = (byte)iVar2 ^ *lineptr;
if (lineptr_len != 1) {
sVar6 = 1;
do {
out_buf[sVar6] = local_8[sVar6 & 1] ^ pbVar9[sVar6];
sVar6 = sVar6 + 1;
} while (lineptr_len != sVar6);
}
out_buf[lineptr_len] = 0;
pbVar9 = out_buf;
do {
pbVar8 = pbVar9 + 1;
printf("%02x",(ulong)*pbVar9);
pbVar9 = pbVar8;
} while (pbVar8 != out_buf + lineptr_len);
}
free(out_buf);
free(lineptr);
return 0;
}
The code does:
So we need to recover the transformation and the two random numbers. To recover the transformation, we use ltrace
to trace strlen
calls to the transformed text:
$ echo -n "a" | ltrace -e strlen ./Fautif
Fautif->strlen("g") = 1
f0+++ exited (status 0) +++
$ echo -n "aa" | ltrace -e strlen ./Fautif
Fautif->strlen("gb") = 2
c435+++ exited (status 0) +++
$ echo -n "aaa" | ltrace -e strlen ./Fautif
Fautif->strlen("gba") = 3
88698e+++ exited (status 0) +++
$ echo -n "a{aa" | ltrace -e strlen ./Fautif
Fautif->strlen("g{ba") = 4
4a244f3e+++ exited (status 0) +++
Observations:
{
So we can just enumerate all mappings, bruteforce the xor-ed random numbers to find flag:
import string
import os
# find all mappings from (character, position) to character
mapping = dict()
for ch in string.ascii_letters + "_{}!":
os.system(f"echo -n \"{ch * 128}\" | ltrace -o strace.log -e strlen -s 1000 ./Fautif >/dev/null")
res = open("strace.log", "r").read()
out = res.split("(\"")[1].split("\")")[0]
print(ch, out)
for i, c in enumerate(out):
mapping[(c, i)] = ch
# reverse last step of xor with two random numbers
data = bytes.fromhex(open("flag.enc", "r").read())
for r1 in range(256):
for r2 in range(256):
recovered = bytearray()
for i in range(len(data)):
if i % 2 == 0:
recovered.append(data[i] ^ r1)
else:
recovered.append(data[i] ^ r2)
if all(chr(ch) in (string.digits + string.ascii_letters + string.punctuation + "\n") for ch in recovered):
s = ""
for i, c in enumerate(recovered):
# skip non letters
i -= recovered[:i].count(b"{") + recovered[:i].count(b"_")
key = (chr(c), i)
if key in mapping:
s += mapping[key]
if "ASIS" in s:
print(recovered, s)
Flag: ASIS{The_Enigma_cipher_was_a_system_created_using_electromechanical_machines_to_encrypt_and_decrypt_messages!}
.
Only after getting the flag, I realize that it is an implementation of Enigma. 2nd solve:
First 3 solves
WTN
solved in 0d 12h 7m 24s
jiegec
solved in 0d 13h 11m 57s