164 lines
4.8 KiB
Markdown
164 lines
4.8 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
$ 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:
|
|
|
|
```c
|
|
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.
|
|
|
|
```c
|
|
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:
|
|
1. **Permutation**: Use the array at `0x49e020` to reorder the input characters.
|
|
`shuffled[i] = input[PERM[i]]`
|
|
2. **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:
|
|
1. **Reverse XOR**: `shuffled[i] = encrypted[i] ^ KEY[i]`
|
|
2. **Reverse Permutation**: `input[PERM[i]] = shuffled[i]`
|
|
|
|
### Solver Script
|
|
|
|
```python
|
|
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}` |