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:
main
function: 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 than51
in 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 addsa2
to 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 byte124
which 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