← Back to Writeups
CUSTOMEasyReversing

Credentials

XESXOR9/2/20264 min read
#reversing#custom#easy#custom_string_hash_reversal#format_string_identification#multiplier_overflow_analysis#static_disassembly

Credentials

Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: custom_string_hash_reversal, format_string_identification, multiplier_overflow_analysis, static_disassembly

Summary

Walkthrough of the CUSTOM Reversing Credentials. Task: 32-bit MSVC console crackme that hashes the username and compares it to an integer password. Solution: reverse the custom hash function, notice the per-char multiplier overflows after 4 bytes so

Description

Flag is the password for admin user

A small 32-bit Windows console crackme (Credentials.exe). It asks for a username and a numeric password, runs a custom hash over the username, and grants access only if the hash equals the entered number. The goal is to find the password that authorizes the admin user — and that password value is the flag.

Recon

$ file Credentials.exe
Credentials.exe: PE32 executable (console) Intel 80386, for MS Windows

A ~9.7 KB MSVC-built PE32 (Intel 80386, 32-bit). Interesting strings: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

$ strings Credentials.exe
This is secure app, you need to be authorized
username:
password:
You are welcome! Now you can use this app.
Go out of here!
%s
%d
C:\Users\dsnos\Desktop\CTF\binary05\Tasks\Release\Tasks.pdb

The PDB path (...\binary05\Tasks\...\Tasks.pdb) confirms a small hand-written crackme. The %s / %d format strings tell us the username is read as a string and the password as a signed decimal integer.

Disassembly — main (0x401100)

The control flow of main:

  1. Print the banner "This is secure app, you need to be authorized" and the prompt "username: ", then scanf("%s", &username) into a stack buffer at [ebp-0x104].

  2. Print "password: ", then scanf("%d", &password) into [ebp-0x108] (a signed 32-bit int).

  3. Call the custom hash routine at 0x401080 with the username pointer (passed in a register, fastcall-style).

  4. Compare the returned hash in eax against the entered password: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

    asm
    0x401157: cmp eax, dword ptr [ebp-0x108]
  5. On equality push 0x402158 → "You are welcome! Now you can use this app."; otherwise push 0x402184 → "Go out of here!".

So we need an integer password equal to hash(username).

Disassembly — the hash function (0x401080)

Reconstructed logic (input = username string, base pointer in ebx):

asm
; edi = accumulator, esi = multiplier
mov  edi, 0x111b25      ; accumulator seed
mov  esi, 1             ; multiplier = 1

; reject usernames longer than 6 chars
... strlen(username) -> edx
cmp  edx, 0x6
jbe  ...                ; if len > 6 -> return 0
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

; per-character loop
loop:
  movsx eax, byte [edx+ebx] ; sign-extended char c
  imul  eax, esi            ; eax = c * multiplier
  shl   esi, 8              ; multiplier <<= 8 (one byte higher each step)
  xor   edi, eax            ; accumulator ^= c * multiplier
  ...
ret                         ; return edi

In C this is roughly:

c
unsigned int hash(const char *s) {
    if (strlen(s) > 6) return 0;        // length gate
    unsigned int acc = 0x111b25;
    unsigned int mul = 1;
    for (; *s; ++s) {
        acc ^= (unsigned int)((signed char)*s * mul);
        mul <<= 8;
    }
    return acc;
}

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The key insight: the multiplier overflows

The multiplier esi is shifted left by 8 bits (one byte) every iteration:

char indexmultiplier (esi)
00x00000001
10x00000100
20x00010000
30x01000000
40x00000000 ← overflow!
5+0x00000000

After the 4th character, 1 << 32 truncates to 0 in a 32-bit register, so c * multiplier = 0 and xor edi, 0 is a no-op. Only the first 4 characters of the username contribute to the hash. The length-≤-6 gate just ensures admin (5 chars) is accepted at all. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Deriving the flag

For username admin, only 'a', 'd', 'm', 'i' matter (the trailing 'n' lands on multiplier 0):

h = 0x111b25
    ^ ('a' * 0x00000001)   ; 0x61
    ^ ('d' * 0x00000100)   ; 0x6400
    ^ ('m' * 0x00010000)   ; 0x6d0000
    ^ ('i' * 0x01000000)   ; 0x69000000
  = 0x697c7f44
  = REDACTED  (decimal)

Since the password is read with %d (signed), and 0x697c7f44 = REDACTED is positive and fits in a signed 32-bit int, we simply enter the decimal value.

Python reimplementation used to derive and verify the answer: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

python
#!/usr/bin/env python3
def h(s):
    edi = 0x111b25
    esi = 1
    for ch in s:
        c = ord(ch)
        if c >= 128:           # movsx -> signed char
            c -= 256
        edi ^= (c * esi) & 0xFFFFFFFF
        edi &= 0xFFFFFFFF
        esi = (esi << 8) & 0xFFFFFFFF
    return edi

print(h("admin"))   # REDACTED
$ python3 solve.py
REDACTED

Solution

Run the binary (on Windows / Wine), then:

username: admin
password: REDACTED
You are welcome! Now you can use this app.

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The flag is the password value itself. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering custom_string_hash_reversal, format_string_identification, multiplier_overflow_analysis, static_disassembly was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-06-24 • CUSTOM • Credentials • Difficulty: Easy