# MCC 2023 RE Challenge Writeup
**Team Members (Group 4):**
Alysha
Firdaus
Roheender
Amin Safri
Abdullah
## Level 1 (First Flag)
---
The first thing we have to check is the **`level1()`** function, **without needing to look at other things** (according to the hints given by the crews)**.**
For the level 1 challenge, we have decompiled the code as below.
```c
// main
int __fastcall main(int argc, const char **argv, const char **envp)
{
void *v3; // rsp
__int64 v4; // rbx
__int64 v5; // rdx
char Str1[4]; // [rsp+30h] [rbp-90h] BYREF
int v8; // [rsp+34h] [rbp-8Ch] BYREF
__int64 v9; // [rsp+38h] [rbp-88h] BYREF
__int64 v10; // [rsp+40h] [rbp-80h] BYREF
__int64 v11[2]; // [rsp+48h] [rbp-78h] BYREF
char v12[32]; // [rsp+58h] [rbp-68h] BYREF
char Str[34]; // [rsp+78h] [rbp-48h] BYREF
char v14[70]; // [rsp+9Ah] [rbp-26h] BYREF
_main();
v11[0] = 0xAD000000DEi64;
v11[1] = 0xEF000000BEi64;
banner();
printf("\nHow about a deli style chicken sandwich?: ");
scanf("%34s", Str);
v9 = -1i64;
xtea_encrypt((unsigned int *)&v9, (int *)v11);
srand(v9 + HIDWORD(v9));
level1(Str);
v3 = alloca(2 * strlen(Str) + 1);
str2hexstr(Str, Str1);
if ( !strcmp(Str1, "5A0E5471020201480C445F0F535D5E6C68076F320E74510202016C390A0D06564C") )
{
Sleep(0xBB8u);
if ( NtCurrentPeb()->BeingDebugged )
pfacts();
printf("\nHow many slices of cheese would you like to add?: ");
scanf("%d", &v8);
if ( level2(v8) )
{
qmemcpy(v14, &unk_140012C23, 0x26ui64);
decimalToHex(v8);
v10 = 0x6C6B6A6968676665i64;
s20_crypt((__int64)v12, 0, &v10, 0, (__int64)v14, 38);
if ( v14[0] == 77 && v14[1] == 67 && v14[2] == 67 && v14[3] == 50 && v14[4] == 48 && v14[5] == 50 && v14[6] == 51 )
{
v4 = 0i64;
printf("\n");
do
{
v5 = (unsigned __int8)v14[v4++];
printf("%c", v5);
}
while ( v4 != 38 );
}
}
}
else
{
printf("\nGordon Ramsay coming for you!\n");
}
return 0;
}
```
Let’s break the **`level1()`** function down into decompiled code.
```c
//level1()
size_t __fastcall level1(char *Str)
{
unsigned __int64 v1; // rsi
char v3; // bl
char v4; // bp
size_t result; // rax
char v6; // al
v1 = 0i64;
v3 = rand();
v4 = v3;
while ( 1 )
{
result = strlen(Str);
if ( v1 >= result )
break;
v6 = Str[v1];
Str[v1] = v6 ^ v3;
if ( Str[v1 + 1] == v6 )
v6 = v4;
++v1;
v3 = v6;
}
return result;
}
```
It’s kind of difficult to understand all these variable names for **`level1()`** function.
Let’s change the variable names to make it ************************************************meaningful and readable.************************************************
```c
//meaningful level1() var names added
size_t __fastcall decryptMessage(char *message)
{
unsigned __int64 index = 0;
char encryptionKey;
char prevChar;
size_t messageLength;
char currentChar;
encryptionKey = rand();
prevChar = encryptionKey;
while (1)
{
messageLength = strlen(message);
if (index >= messageLength)
break;
currentChar = message[index];
message[index] = currentChar ^ encryptionKey;
if (message[index + 1] == currentChar)
currentChar = prevChar;
++index;
encryptionKey = currentChar;
}
return messageLength;
}
```
Based on the code above, this seems like a normal **XOR encryption** as can be seen on the line of code below.
```c
message[index] = currentChar ^ encryptionKey;
```
and in **`main()`**, there's also a **`strcmp()`** condition where you have to match the specific string below to proceed to the next level (which looks like a **flag**).
```c
if (!strcmp(Str1, "5A0E5471020201480C445F0F535D5E6C68076F320E74510202016C390A0D06564C"))
```
It seems like we have to **reverse the XOR encryption (decrypt)** on the string and discover what is the content.
But the problem is, the **XOR decryption requires the original key**, and the key is set by **`rand()`** which generates random number.
**There's two way that we know to find the flag:**
1. Finding the seed number and predicting the random number generated
```c
// This line of code sets the seed of the random number generator
srand(v9 + HIDWORD(v9));
```
2. Bruteforcing the XOR key for decryption.
**Our way:**
What we did was, we bruteforced the possible XOR key which is between **`0-256`**.
**Well... why 0-256?**
Since we hypothesized that the long string `5A0E5471020201480C445F0F535D5E6C68076F320E74510202016C390A0D06564C` **is the flag**,
then we can safely assume it must be ASCII characters (readable characters basically).
Note: ASCII characters range usually from 0 until 127 for standard and 0 until 256 for extended.
**Final Solution**
We built a simple script to reverse the **XOR encryption** and bruteforced the key from **`0-256`**. Tbh, bruteforcing **`0-127`** would already suffice, but just to be safe.
```python
# Function to reverse the level1 encryption
def reverse_level1(encoded_bytes, initial_key):
v3 = initial_key
v4 = v3
decoded_bytes = bytearray()
for i in range(len(encoded_bytes)):
# XOR operation to reverse the encoding
original_byte = encoded_bytes[i] ^ v3
decoded_bytes.append(original_byte)
# Prepare the XOR key for the next iteration
if (i+1 < len(encoded_bytes)) and (encoded_bytes[i+1] == original_byte):
v3 = v4
else:
v3 = original_byte
return decoded_bytes
# The original string provided is treated as ASCII characters representing hex digits
original_string = "5A0E5471020201480C445F0F535D5E6C68076F320E74510202016C390A0D06564C"
# Convert the ASCII hex representation to a byte array
encoded_bytes = bytes.fromhex(original_string)
# Iterate over all possible byte values for v3
for initial_key in range(0, 256):
# Reverse the level1 encryption
decoded_bytes = reverse_level1(encoded_bytes, initial_key)
decoded_str = ''.join(chr(b) for b in decoded_bytes)
# Show all text, even if not directly printable
print(f"Decoded string with v3 = {initial_key}: {decoded_str}")
```
Though, we didn't get the flag straight away, the flag was dispersed between multiple keys and we need to combine them together. (Probably due to how we did the decryption algorithm, skill issue tbh)
```
Decoded string with v3 = 67: C2023{w3lc0m3_70_mcFDFG+E
Decoded string with v3 = 55: mc7FDFGGDG+CD+c2023_flag1}
```
**Final flag:**
```
MCC2O23{w3lc0m3_70_mcc2023_flag1}
```
## Level 2 (Second Flag)
---
Reading the IDA output for the flag:
```c
_BOOL8 __fastcall level2(int input)
{
int new_input; // eax
int v2; // edx
int result; // ecx
int v5; // r8d
if ( input < 0 )
{
printf(
"\n"
"Oh, negative cheese? Are you trying to break the laws of deliciousness or just conducting a cheesy experiment in r"
"everse munchonomics? Either way, I hope your sandwich doesn't develop an identity crisis - it might start question"
"ing its own existence in a world without positive cheese vibes!\n");
return 0i64;
}
if ( 2 * input <= 20 )
{
result = input - 5;
v5 = result & 3;
if ( (result & 3) != 0 )
return 0i64;
while ( result > v5 )
{
if ( v5 % 3 )
result -= v5;
else
result += v5;
++v5;
}
}
else
{
new_input = input;
v2 = 0;
do
{
if ( (v2 & 1) != 0 )
new_input -= v2;
else
new_input += v2;
++v2;
}
while ( input > v2 );
if ( new_input <= 17 )
return 0i64;
result = new_input + 10;
}
return result == 773314;
}
```
based on this we know that we need to input an integer and after calculation it will output the `result` .
The **`level2`** function in the provided C code is designed to perform a series of calculations on an input integer and return a boolean value. Here's a simplified explanation:
1. **Negative Input Check**: If the input is negative, it prints a humorous message and returns **`false`**.
2. **Calculation and Conditional Checks**:
- For inputs where twice the input is less than or equal to 20, it modifies the input using a specific set of rules and a loop. If certain conditions are met, it may return **`false`**.
- For other inputs, it performs a different set of calculations within a loop. If the modified input is less than or equal to 17 after the loop, it returns **`false`**.
3. **Final Check**: After the calculations, if the resulting value equals **`773314`**, the function returns **`true`**; otherwise, it returns **`false`**.
**###Python script used:**
```python
def level2(input):
if input < 0:
return False
if 2 * input <= 20:
v4 = input - 5
v5 = v4 & 3
if v5 != 0:
return False
while v4 > v5:
if v5 % 3:
v4 -= v5
else:
v4 += v5
v5 += 1
else:
v1 = input
v2 = 0
while input > v2:
if v2 & 1:
v1 -= v2
else:
v1 += v2
v2 += 1
if v1 <= 17:
return False
v4 = v1 + 10
return v4 == 773314
def main():
for i in range(1546600, 1546629):
print(f"Checking: {i}")
if level2(i):
print(f"Input value that satisfies the condition: {i}")
break
if __name__ == "__main__":
main()
```
lets try to input the number in the exe:
```bash
└─$ wine sandwich.exe
it looks like wine32 is missing, you should install it.
as root, please execute "apt-get install wine32:i386"
_.---._
_.-~ ~-._
_.-~ ~-._
_.-~ ~---._
_.-~ ~\
.-~ _.;
:-._ _.-~ ./
`-._~-._ _..__.-~ _.-~
/ ~-._~-._ / .__..--~----._
\_____(_;-._\. _.-~_/ ~).. . \
/(_____ \`--...--~_.-~______..-+_______)
.(_________/`--...--~/ _/___ /\
/-._ \_ (___./_..-~__.....__..-~./
`-._~-._ ~\--------~ .-~_..__.-~ _.-~
~-._~-._ ~---------' / .__..--~
~-._\. _.-~_/
\`--...--~_.-~
`--...--~
How about a deli style chicken sandwich?: MCC2023{w3lc0m3_70_mcc2023_flag1}
How many slices of cheese would you like to add?: 1546608
MCC2023{junk_c0d3_45_4lw4y5_huh_flag2}
```
Python script output:
```bash
└─$ python3 flag2.py
Current: 1546600
Current: 1546601
Current: 1546602
Current: 1546603
Current: 1546604
Current: 1546605
Current: 1546606
Current: 1546607
ok: 1546608
```
Flag2 = `MCC2023{junk_c0d3_45_4lw4y5_huh_flag2}`
## Level 3 (Third Flag)
## Flag 3
For the flag 3, we know that the executable file already give us all the flag for `level1` and `level2`. But the challenge still want us the `flag3`. We consider that the flag maybe somewhere in the executable file.

We make some analyse of the strings on `ghidra` and we can see here there is a part of the flag3. So yeah there is some ways to give input and the executable will give us the flag.
After a while, we found a way to bypass this so it will give us the flag.

In here, we found something juicy that maybe a key to bypass it and get the flag3. We will directly give command like this


Give any input and....

WALLA!! We got the last flag. Thank you
---