crackmes.one "Sexy 1337" Writeup: Ghidra Decompilation and XOR Reversing
Challenge link: https://crackmes.one/crackme/6715466c9b533b4c22bd18bb
1) Start with main: pick the two functions to chase
From main we can grab three key facts at once:
generate_password(local_48, "sexy1337")
⇒ there's a step that "generates the password from a fixed seed"encrypt_decrypt(local_48, 0xffffffaa)andencrypt_decrypt(local_88, 0xffffffaa)
⇒ the same key (0xAA) is applied to both the "correct password" and the "user input"strcmp(local_88, local_48)
⇒ the final comparison happens after both have been encrypted (XORed).
Conclusion: first look at "how the password is generated" → generate_password; then come back to verify "the effect of a same-key XOR" → encrypt_decrypt.
2) Chase generate_password: work out the "password plaintext"
for (i = 0; i < strlen(seed); i++) {
dst[i] = seed[i] + 3;
}
dst[len] = '\0';
- The seed is a fixed string:
"sexy1337" - Each character is shifted +3 (an ASCII Caesar shift)
Working it out by hand:
- s→v, e→h, x→{, y→|, 1→4, 3→6, 3→6, 7→:
- giving the password plaintext:
vh{|466:
At this point we already know the "un-XORed version of the correct password."
3) Look at encrypt_decrypt: confirm the "same-key XOR" equivalence
for (...) {
buf[i] ^= 0xAA; // 0xffffffaa truncated to its low 8 bits is 0xAA
}
main applies ^ 0xAA to both sides before the strcmp.
Property of XOR: a == b ⇔ (a ^ k) == (b ^ k).
So "XORing both sides with the same key" is equivalent to comparing the originals directly.
Inference: all we need is to type the plaintext we just computed (vh{|466:) into the input box, and it will match the "generated-and-then-XORed password" after XOR.
4) Back to main: converge on the final input
The remaining details in main are just reading the string, stripping the newline, then strcmp.
Since the comparison is equivalent to comparing the originals, our final input is simply:
vh{|466:
Summary
- From
mainwe spotted the flow: "generate the password first, XOR both sides with the same key, then strcmp" - Went into
generate_passwordand computed the plaintext passwordvh{|466: - Used the "same-key XOR is equivalent to comparing the originals" reasoning in
encrypt_decryptto confirm the logic - Returned to
mainand drew the conclusion: just entervh{|466:to beat the challenge
Lessons learned
The code shown above is Ghidra-decompiled code, so we solved this challenge by analyzing the decompiled output!
Member discussion