Search the site

Back to all posts
Crackmes

babyFirstCrackme - crackmes.one

A level 1 crackme from crackmes.one to warm up: finding the password of a C binary that checks with strcmp. With strings, ltrace, and Ghidra, three ways to the same place.

Crackmes — babyFirstCrackme - crackmes.one
On this page
  1. First contact
  2. Path 1: strings
  3. Path 2: ltrace
  4. Path 3: Ghidra
  5. The hints bonus
  6. Wrap-up

Sometimes you want to sit down and do some reversing without melting your brain. That’s what difficulty 1 crackmes are for. This one is babyFirstCrackme by idanhajbeko, uploaded to crackmes.one (ID 680d04208f555589f3530fb5): a 64-bit ELF written in C that asks for a password and tells you whether it’s right. We’ll solve it three different ways, from the laziest to the most “proper”.

First contact

Before opening anything heavy, see what you’re dealing with:

file babyFirstCrackme
# babyFirstCrackme: ELF 64-bit LSB pie executable, x86-64, dynamically linked, not stripped

not stripped is a good sign: it keeps the function names. Run it to see the behaviour:

./babyFirstCrackme
# Enter the password:
# test
# Wrong password!   (in red)

Asks for a password, compares it, responds. Classic.

Path 1: strings

The fast lane. If the password is written as-is inside the binary (spoiler: it is), strings pulls it out:

strings babyFirstCrackme | grep -i -E 'pass|w0rd|@|!'

Among the noise, one string screams password: !_-P@$$w0rD-_!. In a level 1 crackme that’s usually enough, but it doesn’t tell you why it’s the password. Let’s confirm.

Path 2: ltrace

ltrace traces library calls. Since the comparison is done with strcmp, you’ll see both arguments side by side:

ltrace ./babyFirstCrackme
# printf("Enter the password:\n")
# ...
# strcmp("test", "!_-P@$$w0rD-_!")   = -1
# puts("Wrong password!")

There it is, served on a plate: the program compares your input against !_-P@$$w0rD-_!. You don’t even need to open a disassembler.

Path 3: Ghidra

To actually understand the flow, open it in Ghidra, import the binary, let it analyse, and jump to main. The decompilation is straightforward:

undefined8 main(void) {
    char local_3f8 [1000];

    printf("Enter the password:\n");
    __isoc99_scanf(&DAT_...,local_3f8);
    if (strcmp(local_3f8,"!_-P@$$w0rD-_!") == 0) {
        getFlag();            // prints the flag in green
    } else {
        // "Wrong password!" in red
    }
    return 0;
}

No XOR, no arithmetic, no obfuscation: a direct comparison against the embedded string. The password is literally !_-P@$$w0rD-_!.

./babyFirstCrackme
# Enter the password:
# !_-P@$$w0rD-_!
# Correct! Here is your flag:   (in green)

The hints bonus

The challenge ships with a second binary holding the hints, and that one does have a little substance: an XOR for you to practise with. The operation is 0x4D2 ^ 0x30 = 0x4E2, which in decimal is 1250. Nothing wild, but it’s the perfect excuse to open the Python calculator and remember that XOR is reversible:

>>> 0x4D2 ^ 0x30
1250

Wrap-up

babyFirstCrackme doesn’t try to be hard, and that’s fine: it’s there to drill the flow file → run → strings/ltrace → Ghidra before you get into binaries that actually fight back. The lesson that carries over to tougher challenges is that a password compared in the clear with strcmp protects absolutely nothing; it lives inside the binary and anyone with five minutes pulls it out. When you start seeing strcmp replaced by byte-by-byte checks, hashes, or decryption routines, that’s when the fun begins.

Sud0pls
Sud0pls / Sud0pls

Writeups, notes, and reversing.