baby’s first crackme
I made it with a certain password (you would know it if you get it), but there are many passwords that work
- Category: rev
- Challenge author: mystic_rust
- Challenge link: crackmes.one
Solution:
1. Use IDA to decompile the binary
After decompile, we just have observe three functions: main, encode_input and check_key
main function:
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int v4; // [rsp+14h] [rbp-Ch]
char *s; // [rsp+18h] [rbp-8h]
if ( argc > 2 )
{
s = (char *)argv[1];
v4 = atoi(argv[2]);
if ( strlen(s) == 12 && v4 < 0x33 )
{
s[strcspn(s, "\n")] = 0;
check_key((__int64)s, v4);
return 0;
}
else
{
puts("Access denied!");
return -1;
}
}
else
{
puts("usage: ./rust-1 <key> <number>");
return -1;
}
}
encode_input function:
unsigned __int64 __fastcall check_key(__int64 a1, char a2)
{
int v3; // [rsp+14h] [rbp-5Ch]
int i; // [rsp+18h] [rbp-58h]
char s[56]; // [rsp+20h] [rbp-50h] BYREF
unsigned __int64 v6; // [rsp+58h] [rbp-18h]
v6 = __readfsqword(0x28u);
encode_input(a1, a2, (__int64)s);
for ( i = 0; i < strlen(s); ++i )
v3 = s[i];
if ( v3 == 124 )
puts("Access granted!");
else
puts("Access denied!");
return v6 - __readfsqword(0x28u);
}
check_key function:
unsigned __int64 __fastcall check_key(__int64 a1, char a2)
{
int v3; // [rsp+14h] [rbp-5Ch]
int i; // [rsp+18h] [rbp-58h]
char s[56]; // [rsp+20h] [rbp-50h] BYREF
unsigned __int64 v6; // [rsp+58h] [rbp-18h]
v6 = __readfsqword(0x28u);
encode_input(a1, a2, (__int64)s);
for ( i = 0; i < strlen(s); ++i )
v3 = s[i];
if ( v3 == 124 )
puts("Access granted!");
else
puts("Access denied!");
return v6 - __readfsqword(0x28u);
}
Here is how to code execution flow:
mainfunction: takes two input, first one is string with 12 characters and second one will be decoded into ascii, the condition of the second argument should less than0x33, also means less than51in ASCII. If all conditions satisfy, it will past it tocheck_key.check_key: Here it does postitional shifting by with the second argument. For each byte in the input, if the byte value is odd, it subtract2 * a2, if is even, it addsa2to byte.
For better understanding:
| Position (i) | Character Value | Position Check (Odd/Even) | Value Check (Odd/Even) | Transformation Applied | Result in Encoded Output (i + a3) |
|---|---|---|---|---|---|
| Odd | Odd | Odd | Odd | Subtract 2 * a2 from byte |
encoded[i] = input[i] - 2 * a2 |
| Odd | Even | Odd | Even | Add 2 * a2 to byte |
encoded[i] = input[i] + 2 * a2 |
| Even | Odd | Even | Odd | Add a2 to byte |
encoded[i] = input[i] + a2 |
| Even | Even | Even | Even | Subtract a2 from byte |
encoded[i] = input[i] - a2 |
encode_input: After the shifting, it check that it should end with byte124which is|.
Python script to bruteforce
import string
from itertools import product
def encode_input(s, a2):
encoded = []
for i in range(len(s)):
char = ord(s[i]) # Get ASCII value of character
if i % 2 == 1: # Odd index
if char % 2 == 1: # Odd character value
encoded_char = char - 2 * a2
else: # Even character value
encoded_char = char + 2 * a2
else: # Even index
if char % 2 == 1: # Odd character value
encoded_char = char + a2
else: # Even character value
encoded_char = char - a2
encoded.append(encoded_char % 256) # Ensure byte range (0-255)
return encoded
# Brute force with a limited character set to manage search space
def find_valid_key():
# Character set (smaller subset to make search feasible)
charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
for a2 in range(1, 51): # Test all values for a2 from 1 to 50
# Generate combinations of 12-character strings from charset
for s_tuple in product(charset, repeat=12):
s = ''.join(s_tuple)
# Encode the input
encoded = encode_input(s, a2)
# Check if the last encoded byte equals 124 ('|')
if encoded[-1] == 124:
print(f"Potential key found! s: '{s}', a2: {a2}")
return
print("No valid key found in the current search space.")
# Run the search
find_valid_key()
The correct password is AAAAAAAAAAAz 1