2 min read

crackmes.one "Sexy 1337" Writeup: Ghidra Decompilation and XOR Reversing

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) and encrypt_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 main we spotted the flow: "generate the password first, XOR both sides with the same key, then strcmp"
  • Went into generate_password and computed the plaintext password vh{|466:
  • Used the "same-key XOR is equivalent to comparing the originals" reasoning in encrypt_decrypt to confirm the logic
  • Returned to main and drew the conclusion: just enter vh{|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!