116 lines
3.9 KiB
Markdown
116 lines
3.9 KiB
Markdown
# False Flags
|
|
|
|
`falseFlags` is a beginner-friendly reverse engineering challenge. We are given a binary that contains multiple "fake" flags, and our goal is to identify the correct one.
|
|
|
|
## 1. Initial Analysis
|
|
|
|
We start by examining the file type and basic properties.
|
|
|
|
```bash
|
|
$ file false_flags
|
|
false_flags: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=..., for GNU/Linux 3.2.0, stripped
|
|
```
|
|
|
|
It's a standard 64-bit ELF executable. Let's try running it. The challenge is also available remotely at `<SERVER_IP>:1301`.
|
|
|
|
```bash
|
|
$ nc <SERVER_IP> 1301
|
|
=== The Vault of Falsehoods ===
|
|
There are many keys, but only one opens the door.
|
|
Enter the password: test
|
|
[-] Wrong! That was merely a decoy.
|
|
```
|
|
|
|
Since the description mentions "hiding passwords in the binary", the `strings` command is a good first step to see what's inside.
|
|
|
|
```bash
|
|
$ strings false_flags | grep flag
|
|
{flag:This_Is_Definitely_Not_It}
|
|
{flag:Try_Harder_To_Find_The_Key}
|
|
{flag:Strings_Are_Misleading_You}
|
|
...
|
|
{flag:Reverse_Engineering_Is_Cool}
|
|
...
|
|
```
|
|
|
|
We see a long list of potential flags. We could try them one by one, but that's tedious (and "brute-force" isn't the elegant way!). We need to find out *which* specific string the program compares our input against.
|
|
|
|
## 2. Static Analysis
|
|
|
|
We can analyze the binary using `objdump` to look at the assembly code. Since the binary is stripped, we won't see function names like `main`. However, we can find the entry point.
|
|
|
|
```bash
|
|
$ readelf -h false_flags | grep "Entry point"
|
|
Entry point address: 0x4019f0
|
|
```
|
|
|
|
The entry point is at `0x4019f0`. If we disassemble at this address, we'll see the startup code (`_start`), which calls `__libc_start_main`. The first argument to `__libc_start_main` is the address of `main`.
|
|
|
|
```bash
|
|
$ objdump -d -M intel --start-address=0x4019f0 --stop-address=0x401a20 false_flags
|
|
|
|
00000000004019f0 <.text+0x830>:
|
|
...
|
|
401a08: 48 c7 c7 52 1b 40 00 mov rdi,0x401b52 <-- Address of main
|
|
401a0f: 67 e8 5b 15 00 00 addr32 call 0x402f70 <-- Call to __libc_start_main
|
|
...
|
|
```
|
|
|
|
So `main` is located at `0x401b52`. Let's disassemble it.
|
|
|
|
```bash
|
|
$ objdump -d -M intel --start-address=0x401b52 --stop-address=0x401c50 false_flags
|
|
```
|
|
|
|
In the output, we see a call early in the function:
|
|
|
|
```assembly
|
|
401ba0: e8 70 ff ff ff call 0x401b15
|
|
401ba5: 89 85 6c ff ff ff mov DWORD PTR [rbp-0x94],eax
|
|
```
|
|
|
|
The return value (in `eax`) is stored in `[rbp-0x94]`. This variable is later used to access an array. Let's look at what `0x401b15` does.
|
|
|
|
```bash
|
|
$ objdump -d -M intel --start-address=0x401b15 --stop-address=0x401b52 false_flags
|
|
|
|
0000000000401b15 <.text+0x955>:
|
|
...
|
|
401b4b: b8 0c 00 00 00 mov eax,0xc
|
|
401b50: 5d pop rbp
|
|
401b51: c3 ret
|
|
```
|
|
|
|
Despite some loop logic before it, the function ultimately returns `0xc` (decimal 12). This index is used to select the correct flag from the array of strings we saw earlier.
|
|
|
|
## 3. The Solution
|
|
|
|
Now we simply need to find the string at index 12 (counting from 0) in the list we found earlier.
|
|
|
|
0. {flag:This_Is_Definitely_Not_It}
|
|
1. {flag:Try_Harder_To_Find_The_Key}
|
|
2. {flag:Strings_Are_Misleading_You}
|
|
...
|
|
10. {flag:Do_Not_Trust_Simple_Strings}
|
|
11. {flag:Index_Twelve_Is_Not_Real_11}
|
|
12. {flag:Reverse_Engineering_Is_Cool}
|
|
|
|
The string at index 12 is:
|
|
`{flag:Reverse_Engineering_Is_Cool}`
|
|
|
|
Let's verify by connecting to the remote server:
|
|
|
|
```bash
|
|
$ nc <SERVER_IP> 1301
|
|
=== The Vault of Falsehoods ===
|
|
There are many keys, but only one opens the door.
|
|
Enter the password: {flag:Reverse_Engineering_Is_Cool}
|
|
|
|
[+] Correct! Access Granted.
|
|
[*] The flag is indeed: {flag:Reverse_Engineering_Is_Cool}
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
This challenge demonstrates that while `strings` can reveal interesting data, understanding the *logic* (Control Flow) of the program is often necessary to distinguish useful data from decoys.
|