Deadface 2024 CTF
This is a writeup for DeadFace ctf 2024
Reverse Engineering
Cereal Killer 01
We are given a binary and an executable file. Running the executable we are prompted for a password I loaded up the binary file in IDA Pro and looked at the strings maybe the password is hardcoded lets see! The string
obboreel
stands out trying it out as the password and it was not the password. I did spin up x64dbg to debug the file and set a breakpoint before the jump of printing the bad message Access Denied.
After
The input is being reversed the word booberry
looking up the word its a cereal and trying it out it was definetly the password and we get the flag!
Cereal Killer 02
Running the binary gave me the cryptic message, “Please enter the password.” Challenge accepted. I tossed this bad boy into IDA Pro, and right away, I zeroed in on the function sub_13DC
. The name didn’t scream “password handler,” but it was clearly doing something important. A few strings in the output hinted it might be a password validation function, so let’s crack it wide open.
sub_13DC
– The Password Sieve
So, we’ve got a big ol’ pile of variables in here—s
, s1
, s2
, and some more arrays. This is where the binary initializes everything, probably hashing or messing with the password input in some convoluted way. Here’s the fun part:
The Password Prompt: The function prompts for the password, which gets shoved into
inputpassword
. Right after, a string of mysterious sub-functions start grinding away at the input. This was a clear invitation to reverse engineer each one.- Breaking Down the Mystery Functions: First up,
sub_132F
. This function takes a string like"976e86dafec99d2da69bbaa762aba8cc"
and stores some hashed result intos2
. It was hashing hardcoded strings, which meant the binary was expecting something specific to pass that check - So, after finding the password yellowschoolbus using the hash
104e5dc03561ebf96acf9a0b2b5f184f
online, I confidently punched it into the binary. But guess what? It didn’t work! Classic case of getting your hopes up, right?
Then I spotted that sneaky second hash, 976e86dafec99d2da69bbaa762aba8cc
. I knew that one was going to need some serious firepower, likely requiring Hashcat to crack it.
Instead, I opted to patch the program. I mean, who wouldn’t choose a shortcut when the end goal is just a patch away? So, I skipped the hassle of cracking that second hash, and just like that, I was ready to snag the flag without any fuss!
To really wrap things up, I decided to NOP (No Operation) the JNE
instruction in the binary.
Cereal Killer 03
We’ve conquered the 250-point challenges, and now it’s time to gear up for the big leagues as we tackle the 500-point ones!
When analyzing the pseudocode we see the password is being hashed inspecting the memory layout I did set a breakpoint before jump is taken the cmp eax, [ecx]
synchronizing the ecx
variable with the hex view we can see our input password stephen
is md5 hashed 7F F3 67 97 53 91 30 F7 74 45 F4 8D A5 D4 A1 26
The correct password being saved in the
edx
register I synchronized it as well and it sits on top of the ecx
register EC C8 8B 0A F9 02 53 E8 70 06 97 4B 2F 3F DC
I did assemble the edx
bytes to the ecx
to make a match for the comparison to pass
Running the executable after setting a breakpoint before retn
to get the flag
Cereal Killer 04
Running the binary we see Access Violation occurred. Ahem seems like its trying to access a memory location that is not valid. Upon inspecting the strings and the source code we see the Windows API functions like VirtualAlloc
and VirtualProtect
We see the program accesses an invalid memory location 2BAD2BAD
I did set an ip
to another instruction from the call accessing the invalid memory location
We see the program tries again to access an invalid memory location DEADFACE
I did set an ip
to another instruction from the call accessing the invalid memory location running the executable it terminated. I did inspect the last call to the invalid memory location DEADFACE
by stepping into it
We see as highlighted below the invalid memory address being saved to eax
and a jmp being made to it causing an access violation error.
I did set an ip
after the jmp
instruction
The executable loads dll's
and loads an audio from a remote server and plays it reading the flag out loud!
We are given a jar file running it we see it requires a password
1. Decompiling
First, I opened the JAR file in jadx-gui
and found the XOR function used to decrypt a URL. Here’s the relevant function:
1
2
3
4
5
6
7
private static byte[] decryptURL(byte[] bArr, String str) {
byte[] bArr2 = new byte[bArr.length];
for (int i = 0; i < bArr.length; i++) {
bArr2[i] = (byte) (bArr[i] ^ str.charAt(i % str.length()));
}
return bArr2;
}
The XOR function loops through
encryptedURL
, applying a password in a repeating cycle.
The challenge required us to decrypt an encrypted URL using a password. While the password wasn’t explicitly stated, the decryption function provided a key insight: it checks if the decrypted URL starts with "https."
This led me to deduce that the password itself might be derived from the string format of the URL. Given that “https” is a common prefix for secure web addresses, it became a logical choice for the password to successfully decrypt the byte array.
By using “https” as the key, we were able to reverse the XOR operation, revealing the full URL.. Since XOR is a reversible operation, I crafted a quick Python script to perform the decryption:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
encrypted_url = [42, 6, 68, 64, 7, 120, 93, 31, 83, 17, 48, 23, 81, 92, 90, 46, 11, 68, 68, 27, 44, 30, 81, 82, 7, 108, 29, 66, 87, 91, 33, 23, 66, 85, 21, 46, 1, 31, 86, 6, 45, 29, 68, 82, 6, 45, 29, 68, 30, 30, 50, 23, 87]
Known plaintext prefix
prefix = "https"
Derive key from prefix and first few bytes of encrypted_url
key = [encrypted_url[i] ^ ord(prefix[i]) for i in range(len(prefix))]
Extend key to match the length of encrypted_url
full_key = (key * (len(encrypted_url) // len(key) + 1))[:len(encrypted_url)]
Decrypt the URL
decrypted_url = ''.join(chr(b ^ k) for b, k in zip(encrypted_url, full_key))
print("Derived Password (Key):", ''.join(chr(k) for k in key))
print("Decrypted URL:", decrypted_url)