Securinets-CTF-2019, Matrix of Hell
Matrix of Hell, 992 pt
Welcome, fellow code adventurers, to the treacherous Matrix of Hell challenge! Brace yourselves as we navigate through the twisted paths of this binary labyrinth and unlock its secrets. Prepare for a journey filled with humor and code-cracking prowess!
As we confront the mighty binary, it challenges us with a password, a crackme-inspired puzzle. Undeterred, we open it in our trusty IDA to unravel its mysteries. But wait! Let’s give the variables some amusing aliases for our convenience.
Behold the three steps of the main function:
Step 1: The Binary’s Byte Symphony
The binary generates a symphony of bytes using a peculiar process and stores it in the “pass_cmp” variable.
v14 = 0;
for ( i = 0; i <= 4; ++i )
{
for ( j = 0; j <= 4; ++j )
{
if ( v14 == 9 )
{
v14 = 10;
--j;
}
else
{
a2 = (char **)j;
a3 = (char **)(4 * (j + 6LL * i));
*(_DWORD *)((char *)pass_cmp + (_QWORD)a3) = v14++ + 65;
}
}
}
Here’s a snippet of the binary’s symphony in Python:
s = ['?']*100
v3=0
v4=0
for i in range(5):
for j in range(5):
if v14==9:
v14=10
j = j-1
else:
a3= (j+6*i)
s[a3]=chr(v14+65)
v14 += 1
print(''.join(s))
output--> ABCDE?FGHI??LMNOP?QRSTU?VWXYZ???? <-- pass_cmp
The symphony echoes: ABCDE?FGHI??LMNOP?QRSTU?VWXYZ????
A cryptic melody indeed!
Step 2: Matrix Manipulations
In this step, the binary checks the length of the input, ensuring it’s 14 characters long. It then compares each byte of the password with a matrix (5x5) using a fascinating dance of loops. Here’s a glimpse of the code in IDA:
printf("PASSWORD:", a2, a3);
gets(password);
if ( strlen(password) != 14 || (sub_83A(), !v3) ) <-- check password length
{
printf("ACCESS DENIED");
exit(0);
}
v16 = 0;
for ( k = 0; k < strlen(password); ++k ) <-- iterate password byte by byte
{
for ( l = 0; l <= 4; ++l )
{
for ( m = 0; m <= 4; ++m )
{
if ( pass_cmp[m + 6LL * l] == password[k] ) <-- Compare password byte at k index
with above output.
{
new_string[v16] = l + 65; <-- form new string
v4 = v16 + 1;
new_string[v4] = m + 49; <-- The length of new string is double
of password.
v16 = v4 + 1;
}
}
}
}
for ( n = 0; n < strlen(new_string); ++n )
s2[n] = n % 4 ^ new_string[n]; <-- doing some xor operation and form new
string s2
if ( strcmp(s1, s2) ) <-- cmp s2 with s1 and s1 is in data
section
{ <-- s1 = 'B0C2A2C6A3A7C5@6B5F0A4G2B5A2'
printf("ACCESS DENIED", s2);
exit(0);
}
A new string, s2
is formed through a series of XOR operations and compared to another string, s1
But fear not, for we shall overcome this matrix madness!
s1 = "B0C2A2C6A3A7C5@6B5F0A4G2B5A2" <-- s1
new_string = ""
for i in range(28):
new_string += chr(i%4^ord(s1[i]))
print(f)
output--> 'B1A1A3A5A2C4C4B5B4D3A5E1B4C1' <-- new_string
Now we have to find out the co-ordinates of matrix (l,m) which is satisfied for this compare if ( pass_cmp[m + 6LL * l] == password[k] )
cord = []
for i in range(0,28,2):
l,m=0,0
l = ord(f[i])-ord('A')
m = ord(f[i+1])-ord('1')
cord.append((l,m))
print(cord),
output--> [(1, 0), (0, 0), (0, 2), (0, 4), (0, 1), (2, 3), (2, 3), (1, 4), (1, 3), (3, 2), (0, 4), (4, 0), (1, 3), (2, 0)]
We got the (l,m) value at which comparision is satisfied.Now we only have to recover the password.
cord = [(1, 0), (0, 0), (0, 2), (0, 4), (0, 1), (2, 3), (2, 3), (1, 4), (1, 3), (3, 2), (0, 4), (4, 0), (1, 3), (2, 0)]
pass_cmp = 'ABCDE?FGHI??LMNOP?QRSTU?VWXYZ????'
password = ""
for i in range(len(cord)):
password += pass_cmp[cord[i][1]+6*cord[i][0]]
print(password)
output --> 'FACEBOO?ISEVIL' <-- one byte is missing '?' we know what it is :)
show password is --> 'FACEBOOKISEVIL'
Step 3: Unleashing the Flag
Once we conquer the second step and recover password the binary generates the flag with a flick of its own magic wand. It unveils the message:
flag : 1337_FD_DDLLLKMO_KUWRRRVL_HAHAHA
Let me know what you think of this article on twitter @0xdr3dd or leave a comment below!