4.8 KiB
Twisted
twisted is a reverse engineering challenge where we must recover a flag from a provided binary and an encrypted output string.
Information Gathering
We start by analyzing the file type of the twisted binary:
$ file twisted
twisted: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, ... stripped
The binary is stripped, meaning it lacks debugging symbols like function names. Connecting to the challenge server gives us the encrypted flag:
Here is your twisted flag: 34d133c640536c58ffcebb864a836aaf3bc432c3606b331df2d981a472bd6e80
Reverse Engineering
We open the binary in Ghidra to analyze its logic. Since the binary is stripped, we first locate the entry point (entry), which calls __libc_start_main. The first argument to __libc_start_main is the address of the main function. Following this path leads us to the function at 0x40190a, which we rename to main.
Main Function (0x40190a)
The decompiled code for main reveals the expected arguments and basic validation:
undefined8 main(int param_1,long param_2)
{
size_t sVar1;
if (param_1 < 2) {
printf("Usage: %s <flag>\n",*(undefined8 *)(param_2 + 8)); // Usage string at 0x49e081
return 1;
}
sVar1 = strlen(*(char **)(param_2 + 8));
if (sVar1 == 32) {
FUN_004017b5(*(undefined8 *)(param_2 + 8));
return 0;
}
printf("Error: Flag must be exactly %d characters long.\n",32); // Error string at 0x49e098
return 1;
}
From this, we learn that the input flag must be exactly 32 characters long. If the length is correct, it calls FUN_004017b5.
Transformation Function (0x4017b5)
We analyze FUN_004017b5, which contains the core encryption logic. It performs two distinct operations on the input string.
void FUN_004017b5(long param_1)
{
long lVar1;
int local_84; // Counter for Loop 1
int local_80; // Counter for Loop 2
int local_7c; // Counter for Loop 3
byte local_70 [32]; // Shuffled Buffer
byte local_50 [32]; // Final XOR Buffer
byte local_30 [32]; // Input Copy
// ... Setup and copying input to local_30 ...
// --- STEP 1: Permutation ---
local_84 = 0;
while (local_84 < 32) {
// Load byte from Permutation Table at 0x49e020
// Use it as an index into the input string
local_70[local_84] = local_30[(int)(uint)(byte)(&DAT_0049e020)[local_84]];
local_84 = local_84 + 1;
}
// --- STEP 2: XOR Encryption ---
local_80 = 0;
while (local_80 < 32) {
// XOR the shuffled byte with a Key byte from 0x49e040
local_50[local_80] = local_70[local_80] ^ (&DAT_0049e040)[local_80];
local_80 = local_80 + 1;
}
// --- Print Result ---
printf("Here is your twisted flag: "); // String at 0x49e060
local_7c = 0;
while (local_7c < 32) {
printf("%02x",(ulong)local_50[local_7c]);
local_7c = local_7c + 1;
}
// ...
}
The algorithm is:
- Permutation: Use the array at
0x49e020to reorder the input characters.shuffled[i] = input[PERM[i]] - XOR: XOR the reordered characters with the array at
0x49e040.encrypted[i] = shuffled[i] ^ KEY[i]
Data Extraction
We inspect the memory at the identified addresses to retrieve the Permutation Table and XOR Key.
Permutation Table (0x49e020):
Values: 3, 0, 1, 2, 7, 4, 5, 6, 10, 11, 8, 9, 15, 12, 13, 14, 19, 16, 17, 18, 22, 23, 20, 21, 25, 26, 27, 24, 31, 28, 29, 30
XOR Key (0x49e040):
Values (Hex): 55, AA, 55, AA, 12, 34, 56, 78, 9A, BC, DE, F0, 0F, F0, 0F, F0, 55, AA, 55, AA, 12, 34, 56, 78, 9A, BC, DE, F0, 0F, F0, 0F, F0
Solution
To decrypt the flag 34d133c6..., we reverse the operations:
- Reverse XOR:
shuffled[i] = encrypted[i] ^ KEY[i] - Reverse Permutation:
input[PERM[i]] = shuffled[i]
Solver Script
import sys
# Extracted from 0x49e020
PERM = [
3, 0, 1, 2, 7, 4, 5, 6,
10, 11, 8, 9, 15, 12, 13, 14,
19, 16, 17, 18, 22, 23, 20, 21,
25, 26, 27, 24, 31, 28, 29, 30
]
# Extracted from 0x49e040
KEY = [
0x55, 0xAA, 0x55, 0xAA, 0x12, 0x34, 0x56, 0x78,
0x9A, 0xBC, 0xDE, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0,
0x55, 0xAA, 0x55, 0xAA, 0x12, 0x34, 0x56, 0x78,
0x9A, 0xBC, 0xDE, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0
]
def solve(hex_string):
encrypted_bytes = bytes.fromhex(hex_string)
if len(encrypted_bytes) != 32:
print("Error: Length mismatch")
return
# 1. Reverse XOR
shuffled = [0] * 32
for i in range(32):
shuffled[i] = encrypted_bytes[i] ^ KEY[i]
# 2. Reverse Permutation
original = [0] * 32
for i in range(32):
target_idx = PERM[i]
original[target_idx] = shuffled[i]
print("Flag: " + "".join(chr(b) for b in original))
if __name__ == "__main__":
solve("34d133c640536c58ffcebb864a836aaf3bc432c3606b331df2d981a472bd6e80")
Running the script gives us the flag:
{flag: Reverse_Engineer_The_Map}