TryHackMe: Compiled CTF Walkthrough
TryHackMe | Compiled CTF Challenge
🧰 Writeup Overview
This writeup explains the reverse-engineering of the provided ELF binary Compiled.Compiled
, culminating in finding the correct input that triggers the message Correct!.
Initial Enumeration
File Type Check
1
file Compiled.Compiled
- ELF 64-bit LSB pie executable, dynamically linked (using
ld‑linux‑x86_64.so.2
), not stripped: symbol names are available.
Strings Inspection
1
strings Compiled.Compiled
Key findings:
Password:
DoYouEven%sCTF
__dso_handle
_init
Correct!
&Try again!
These strongly hint that input is processed via a custom scanf
format string, with logic comparing two strings.
Runtime Behavior
Test Basic Execution
1
2
3
4
./Compiled.Compiled
# Try random password:
Password: test
Try again!
Monitor with ltrace
1
ltrace ./Compiled.Compiled
Input:
1
test
Output trace:
1
2
3
4
5
6
7
write("Password: ", 1, 10, 0x7f7af50705c0) = 10
__isoc99_scanf(0x55d159b6700f, 0x7ffe92dfe6b0, 0, 1024Password: test
) = 0
strcmp("", "__dso_handle") = -95
strcmp("", "_init") = -95
printf("Try again!") = 10
Try again!+++ exited (status 0) +++
Reverse Engineering the Logic
Disassemble or Use Ghidra
/ IDA
Here’s a deep dive explanation of decompiled pseudo‑C
logic main()
function, Let’s break it down line-by-line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
undefined8 main(void)
{
int iVar1;
char local_28 [32]; // 32-byte buffer
fwrite("Password: ", 1, 10, stdout);
__isoc99_scanf("DoYouEven%sCTF", local_28);
iVar1 = strcmp(local_28, "__dso_handle");
if ((-1 < iVar1) && (iVar1 = strcmp(local_28,"__dso_handle"), iVar1 < 1)) {
printf("Try again!");
return 0;
}
iVar1 = strcmp(local_28, "_init");
if (iVar1 == 0) {
printf("Correct!");
}
else {
printf("Try again!");
}
return 0;
}
🧠 Line-by-Line Breakdown
char local_28[32];
- A 32-byte buffer to store the user input.
- Used to store the result of the
scanf
.
fwrite("Password: ", 1, 10, stdout);
- Displays the prompt:
Password:
- Equivalent to
printf("Password: ");
but done using fwrite() here.
__isoc99_scanf("DoYouEven%sCTF", local_28);
, 📌 Key Point
- The format string
DoYouEven%sCTF
means: - It expects the user to input a string that matches this exact pattern.
- For example, if user types DoYouEven
ABC
CTF, then: The string"ABC"
will be extracted and stored intolocal_28
. - Only the
%s
part is stored – the surrounding text (DoYouEven
andCTF
) is just expected for formatting.
🧪 Example:
1
2
Input: DoYouEventestCTF
Then local_28 = "test"
Behavior:
The format string “DoYouEven%sCTF” expects:
Literal “DoYouEven” at the start.
A string (%s) in the middle.
Literal “CTF” at the end.
Only the %s part is stored in input.
🔑 Important:
%s
is wrapped by fixed strings, so user must input format as string
strcmp(local_28, "__dso_handle")
- Compares user input against
"__dso_handle"
. - If equal, strcmp() returns
0
.
Then we enter this condition:
1
if ((-1 < iVar1) && (iVar1 = strcmp(local_28,"__dso_handle"), iVar1 < 1))
🧠 What’s Going On?
- This is a tricky condition.
- It does a double comparison against
"__dso_handle"
, structured like:
1
if (strcmp(...) >= 0 && strcmp(...) <= 0)
- So the only possible value that passes is when:
strcmp(...) == 0
If input is "__dso_handle"
until if true:
printf("Try again!")
is shown. -Function returns early.
🔐 This acts as a blacklist: if you input "__dso_handle"
→ it gets blocked.
strcmp(local_28, "_init")
- If the above check is bypassed, the code now compares input with “_init”.
✅ If local_28 == “_init” Message: Correct!
❌ Else Message: Try again!
✅ Correct Summary
- If input is
"__dso_handle"
→ “Try again!” Failure because Blacklisted. - If input is
"_init"
→ “Correct!” - Anything else → “Try again!”
1
DoYouEven<password>
And only <password>
is stored.
Final Payload & Result
This triggers the “Correct!” message because:
scanf
extracts_init
(betweenDoYouEven
and theend of input
)._init
is not blacklisted and matches the expected string.If you
omit CTF
, scanf will still store _init (since%s
stops at whitespace or end of input) ==> correct, but some binaries may reject due to extra input such this case.CTF
(matches format, but ignored).
%s
in scanf stops reading when it encounters several cases:
- Whitespace (space, tab, newline).
- End of input (if no more characters are available).
like that
1
./Compiled.Compiled
Enter:
1
DoYouEven_initCTF
Output:
1
Try again!
1
./Compiled.Compiled
Enter:
1
DoYouEven_init
Output:
1
Correct!
1
ltrace ./Compiled.Compiled
Enter:
1
DoYouEven_init
Output:
1
2
3
4
5
6
7
8
fwrite("Password: ", 1, 10, 0x7f95a0c605c0) = 10
__isoc99_scanf(0x5601aa8f200f, 0x7ffd41387e90, 0, 1024Password: DoYouEven_init
) = 1
strcmp("_init", "__dso_handle") = 10
strcmp("_init", "__dso_handle") = 10
strcmp("_init", "_init") = 0
printf("Correct!") = 8
Correct!+++ exited (status 0) +++
👏 Clear indication that _init
is accepted after bypassing the blacklist & making strcmp("_init", "_init") = 0
.