What are your sources or calcultations about the estimated amount of time for cracking un hash SHA-256? Especially "Seconds/Minutes" for an Antminer S9? If it were true, SHA-256 wouldn't be secured anymore and no one would use it. So I think if the sever seed is generated randomly and is a long string, you won't be able to find it with such method. It could only work if there are some rules, patterns or narrow bounds for generating the sever seed.
You would be right to assume the entire key space is impossible and massive.
but if you take some random seeds:
hashes = [
"3d9d7ac2a7bf7a3755bede703a63a0fa17d0e206a5752555717432267fd9709e",
"04e3b4023148040c4d000ff3493107317dc2eef513fe7bfbfbe706890c376846",
"4e173939c30fccda068c5f959f7648c1d6a8625ba9edc6324dbdf74366d5f158",
"2a108a45ed6ea108dfb8041e6f26df56d4c4b6dbf95184472212d50f8e951914",
"4d4410cda3b52677d0f76087ab222d9fb2c3de8265b32660d6c9be0404938222",
"fe8ff24859844caa772f959747612cbab36fda2d9475189d6f2d5546ec2dbe93"
]
and run analysis on it:
import re
from collections import Counter
import difflib
hashes = [
"3d9d7ac2a7bf7a3755bede703a63a0fa17d0e206a5752555717432267fd9709e",
"04e3b4023148040c4d000ff3493107317dc2eef513fe7bfbfbe706890c376846",
"4e173939c30fccda068c5f959f7648c1d6a8625ba9edc6324dbdf74366d5f158",
"2a108a45ed6ea108dfb8041e6f26df56d4c4b6dbf95184472212d50f8e951914",
"4d4410cda3b52677d0f76087ab222d9fb2c3de8265b32660d6c9be0404938222",
"fe8ff24859844caa772f959747612cbab36fda2d9475189d6f2d5546ec2dbe93"
]
def frequency_analysis(hashes):
combined_hashes = ''.join(hashes)
frequency = Counter(combined_hashes)
return frequency
def find_doubles(hashes):
doubles = []
for h in hashes:
doubles.extend(re.findall(r'(.)\1', h))
return Counter(doubles)
def common_substrings(hashes, min_length=2):
substrings = {}
for i in range(len(hashes)):
for j in range(i + 1, len(hashes)):
matcher = difflib.SequenceMatcher(None, hashes[i], hashes[j])
for block in matcher.get_matching_blocks():
if block.size >= min_length:
substring = hashes[i][block.a:block.a + block.size]
if substring in substrings:
substrings[substring] += 1
else:
substrings[substring] = 1
return {k: v for k, v in substrings.items() if v > 1}
def find_repeated_patterns(hashes):
pattern_counts = Counter()
for h in hashes:
for match in re.findall(r'((.)\2+)', h):
pattern_counts[match[0]] += 1
return pattern_counts
frequency = frequency_analysis(hashes)
doubles = find_doubles(hashes)
common_subs = common_substrings(hashes)
repeated_patterns = find_repeated_patterns(hashes)
print("Frequency Analysis:")
print(frequency)
print("\nDouble Characters:")
print(doubles)
print("\nCommon Substrings:")
print(common_subs)
print("\nRepeated Patterns:")
print(repeated_patterns)
you will see something like this:
Frequency Analysis:
Counter({'d': 30, '2': 30, '4': 30, '7': 29, '6': 28, 'f': 27, '0': 27, '5': 25, '3': 23, '9': 22, 'e': 20, 'a': 19, 'b': 19, '1': 19, '8': 19, 'c': 17})
Double Characters:
Counter({'2': 4, '5': 3, '4': 3, 'f': 2, '6': 2, '7': 2, '0': 1, 'e': 1, 'c': 1, 'a': 1})
Common Substrings:
{'c2': 2, '0f': 3, 'be': 2, '76': 2, '93': 2, 'd6': 4, 'db': 2}
Repeated Patterns:
Counter({'44': 3, '55': 2, '22': 2, 'ff': 2, '66': 2, '77': 2, '222': 2, '555': 1, '000': 1, 'ee': 1, 'cc': 1, 'aa': 1})
Process finished with exit code 0
with this information you can see that theres bias in the randomness and leverage it like this simple example:
import subprocess
import os
import re
def create_custom_charsets(frequencies, patterns, substrings):
"""Creates custom character set files and a dictionary file.
Args:
frequencies: A list of characters in order of frequency
patterns: A list of characters to create common patterns
substrings: A list of common substrings
Returns: None
"""
# Use frequency analysis to create a character set with the most common characters.
with open("chars_most_freq.txt", "w") as f:
f.write("".join(frequencies))
# Create a file with common patterns:
with open("chars_patterns.txt", "w") as f:
f.write("".join(patterns))
# Create a dictionary file for common substrings:
with open("substrings.txt", "w") as f:
for substring in substrings:
f.write(substring + "\n")
def attack_seed(hashed_seed, frequencies, patterns, substrings):
"""Attempts to crack a single hashed seed using different methods.
Args:
hashed_seed: The server seed hash
frequencies: A list of characters in order of frequency
patterns: A list of characters to create common patterns
substrings: A list of common substrings
Returns:
True if the seed was found, False if not.
"""
print(f"Attempting to crack: {hashed_seed}")
create_custom_charsets(frequencies, patterns, substrings)
# Define seed length
seed_len = 64
length_mask = "?1" * seed_len
# ---- Attack Strategy ----
# --- Try frequency attack first --
print("Trying frequencies...")
command = ["hashcat", "-a", "3", "-m", "0", hashed_seed, length_mask,
"--custom-charset1", "chars_most_freq.txt", "--outfile", "cracked.txt", "--potfile-disable"]
process = subprocess.run(command, capture_output=True, text=True)
if re.search(rf'{hashed_seed}',process.stdout):
print("SUCCESS: Seed found using frequency method.")
return True
# --- Try pattern attack next ---
print("Trying patterns...")
command = ["hashcat", "-a", "3", "-m", "0", hashed_seed, length_mask,
"--custom-charset1", "chars_patterns.txt", "--outfile", "cracked.txt", "--potfile-disable"]
process = subprocess.run(command, capture_output=True, text=True)
if re.search(rf'{hashed_seed}',process.stdout):
print("SUCCESS: Seed found using patterns method.")
return True
# --- Try hybrid with dictionary and frequency ---
print("Trying hybrid with dictionary...")
command = ["hashcat", "-a", "6", "-m", "0", "substrings.txt", length_mask,
"--custom-charset1", "chars_most_freq.txt", "--outfile", "cracked.txt", "--potfile-disable"]
process = subprocess.run(command, capture_output=True, text=True)
if re.search(rf'{hashed_seed}',process.stdout):
print("SUCCESS: Seed found using dictionary method.")
return True
print("Seed not found with current strategy. Expanding seed length and trying again.")
#Iterate over seed lengths, up to the actual length of 64.
for i in range(16, seed_len + 1, 16):
length_mask = "?1" * i
command = ["hashcat", "-a", "3", "-m", "0", hashed_seed, length_mask, "--custom-charset1",
"chars_most_freq.txt", "--outfile", "cracked.txt", "--potfile-disable"]
process = subprocess.run(command, capture_output=True, text=True)
if re.search(rf'{hashed_seed}',process.stdout):
print(f"SUCCESS: Seed found at length {i} using frequency method.")
return True
for i in range(16, seed_len + 1, 16):
length_mask = "?1" * i
command = ["hashcat", "-a", "3", "-m", "0", hashed_seed, length_mask, "--custom-charset1",
"chars_patterns.txt", "--outfile", "cracked.txt", "--potfile-disable"]
process = subprocess.run(command, capture_output=True, text=True)
if re.search(rf'{hashed_seed}',process.stdout):
print(f"SUCCESS: Seed found at length {i} using patterns method.")
return True
for i in range(16, seed_len + 1, 16):
length_mask = "?1" * i
command = ["hashcat", "-a", "6", "-m", "0", "substrings.txt", length_mask, "--custom-charset1",
"chars_most_freq.txt", "--outfile", "cracked.txt", "--potfile-disable"]
process = subprocess.run(command, capture_output=True, text=True)
if re.search(rf'{hashed_seed}',process.stdout):
print(f"SUCCESS: Seed found at length {i} using dictionary method.")
return True
print("FAILURE: Seed not found.")
return False
def main():
"""Main function to perform seed cracking."""
print("Starting crack...")
# Define the seed hashes
hashed_seeds = [
"de48f83cf2ae077ac4a988a28cffd94d9c1ed8253a8e850562a615b93e38329d",
"824df4f901cc4ff2e80f0cfa9ae1413257457ec5fb744ad16d84d59e12b46a94",
"8490c745afd237b5d79529af6a428d96315027daf34963440b363ccffa4f0cc4"
]
#Define patterns and substrings
frequencies = "d2476f0539eab18c"
patterns = "d2476f0539eab18c4422ff6677"
substrings = ["d6", "db", "c2", "0f"]
#Attempt to crack the seeds
for hashed_seed in hashed_seeds:
attack_seed(hashed_seed, frequencies, patterns, substrings)
#Cleanup created files.
os.remove("chars_most_freq.txt")
os.remove("chars_patterns.txt")
os.remove("substrings.txt")
if os.path.exists("cracked.txt"):
os.remove("cracked.txt")
if __name__ == "__main__":
main()
what does it reduce?
method is about 100,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 times smaller or 1 followed by 48 zeros
this is a simple version of how we are not trying the full keyspace.
you could take it further
--- Validation ---
Hashes validated as hexadecimal strings of length 64.
--- Detailed Frequency Analysis ---
Overall Character Frequency (Counts):
Counter({'d': 30, '2': 30, '4': 30, '7': 29, '6': 28, 'f': 27, '0': 27, '5': 25, '3': 23, '9': 22, 'e': 20, 'a': 19, 'b': 19, '1': 19, '8': 19, 'c': 17})
Overall Character Frequency (Percentages):
{'3': '5.99%', 'd': '7.81%', '9': '5.73%', '7': '7.55%', 'a': '4.95%', 'c': '4.43%', '2': '7.81%', 'b': '4.95%', 'f': '7.03%', '5': '6.51%', 'e': '5.21%', '0': '7.03%', '6': '7.29%', '1': '4.95%', '4': '7.81%', '8': '4.95%'}
Positional Character Frequency (Position: {Character: Count}):
Position 0: Counter({'4': 2, '3': 1, '0': 1, '2': 1, 'f': 1})
Position 1: Counter({'d': 2, 'e': 2, '4': 1, 'a': 1})
Position 2: Counter({'1': 2, '9': 1, 'e': 1, '4': 1, '8': 1})
Position 3: Counter({'d': 1, '3': 1, '7': 1, '0': 1, '4': 1, 'f': 1})
Position 4: Counter({'7': 1, 'b': 1, '3': 1, '8': 1, '1': 1, 'f': 1})
Position 5: Counter({'a': 2, '4': 1, '9': 1, '0': 1, '2': 1})
Position 6: Counter({'c': 2, '4': 2, '0': 1, '3': 1})
Position 7: Counter({'2': 2, '9': 1, '5': 1, 'd': 1, '8': 1})
Position 8: Counter({'a': 2, '3': 1, 'c': 1, 'e': 1, '5': 1})
Position 9: Counter({'3': 2, '7': 1, '1': 1, 'd': 1, '9': 1})
Position 10: Counter({'b': 2, '4': 1, '0': 1, '6': 1, '8': 1})
Position 11: Counter({'f': 2, '8': 1, 'e': 1, '5': 1, '4': 1})
Position 12: Counter({'7': 1, '0': 1, 'c': 1, 'a': 1, '2': 1, '4': 1})
Position 13: Counter({'c': 2, 'a': 1, '4': 1, '1': 1, '6': 1})
Position 14: Counter({'0': 2, '3': 1, 'd': 1, '7': 1, 'a': 1})
Position 15: Counter({'7': 2, 'a': 2, 'c': 1, '8': 1})
Position 16: Counter({'d': 2, '5': 1, '4': 1, '0': 1, '7': 1})
Position 17: Counter({'5': 1, 'd': 1, '6': 1, 'f': 1, '0': 1, '7': 1})
Position 18: Counter({'b': 2, '0': 1, '8': 1, 'f': 1, '2': 1})
Position 19: Counter({'e': 1, '0': 1, 'c': 1, '8': 1, '7': 1, 'f': 1})
Position 20: Counter({'0': 2, 'd': 1, '5': 1, '6': 1, '9': 1})
Position 21: Counter({'f': 2, 'e': 1, '4': 1, '0': 1, '5': 1})
Position 22: Counter({'9': 2, '7': 1, 'f': 1, '1': 1, '8': 1})
Position 23: Counter({'7': 2, '0': 1, '3': 1, '5': 1, 'e': 1})
Position 24: Counter({'4': 2, '3': 1, '9': 1, '6': 1, 'a': 1})
Position 25: Counter({'f': 2, 'a': 1, '9': 1, 'b': 1, '7': 1})
Position 26: Counter({'6': 2, '2': 2, '3': 1, '7': 1})
Position 27: Counter({'1': 2, '6': 2, '3': 1, '2': 1})
Position 28: Counter({'2': 2, 'a': 1, '0': 1, '4': 1, 'd': 1})
Position 29: Counter({'0': 1, '7': 1, '8': 1, 'f': 1, 'd': 1, 'c': 1})
Position 30: Counter({'f': 1, '3': 1, 'c': 1, '5': 1, '9': 1, 'b': 1})
Position 31: Counter({'a': 2, '1': 2, '6': 1, 'f': 1})
Position 32: Counter({'d': 2, 'b': 2, '1': 1, '7': 1})
Position 33: Counter({'7': 1, 'd': 1, '6': 1, '4': 1, '2': 1, '3': 1})
Position 34: Counter({'c': 3, 'd': 1, 'a': 1, '6': 1})
Position 35: Counter({'0': 1, '2': 1, '8': 1, '4': 1, '3': 1, 'f': 1})
Position 36: Counter({'e': 2, 'd': 2, '6': 1, 'b': 1})
Position 37: Counter({'2': 2, 'e': 2, '6': 1, 'a': 1})
Position 38: Counter({'0': 1, 'f': 1, '5': 1, 'd': 1, '8': 1, '2': 1})
Position 39: Counter({'b': 2, '6': 1, '5': 1, '2': 1, 'd': 1})
Position 40: Counter({'a': 2, '1': 1, 'f': 1, '6': 1, '9': 1})
Position 41: Counter({'5': 2, '9': 2, '3': 1, '4': 1})
Position 42: Counter({'7': 2, 'f': 1, 'e': 1, '5': 1, 'b': 1})
Position 43: Counter({'5': 2, 'e': 1, 'd': 1, '1': 1, '3': 1})
Position 44: Counter({'2': 2, '7': 1, 'c': 1, '8': 1, '1': 1})
Position 45: Counter({'6': 2, '5': 1, 'b': 1, '4': 1, '8': 1})
Position 46: Counter({'5': 1, 'f': 1, '3': 1, '4': 1, '6': 1, '9': 1})
Position 47: Counter({'5': 1, 'b': 1, '2': 1, '7': 1, '0': 1, 'd': 1})
Position 48: Counter({'7': 1, 'f': 1, '4': 1, '2': 1, 'd': 1, '6': 1})
Position 49: Counter({'1': 1, 'b': 1, 'd': 1, '2': 1, '6': 1, 'f': 1})
Position 50: Counter({'7': 1, 'e': 1, 'b': 1, '1': 1, 'c': 1, '2': 1})
Position 51: Counter({'d': 2, '4': 1, '7': 1, '2': 1, '9': 1})
Position 52: Counter({'3': 1, '0': 1, 'f': 1, 'd': 1, 'b': 1, '5': 1})
Position 53: Counter({'5': 2, '2': 1, '6': 1, '7': 1, 'e': 1})
Position 54: Counter({'4': 2, '0': 2, '2': 1, '8': 1})
Position 55: Counter({'6': 2, '9': 1, '3': 1, 'f': 1, '4': 1})
Position 56: Counter({'0': 2, '7': 1, '6': 1, '8': 1, 'e': 1})
Position 57: Counter({'c': 2, 'f': 1, '6': 1, 'e': 1, '4': 1})
Position 58: Counter({'d': 2, '9': 2, '3': 1, '2': 1})
Position 59: Counter({'5': 2, '9': 1, '7': 1, '3': 1, 'd': 1})
Position 60: Counter({'7': 1, '6': 1, 'f': 1, '1': 1, '8': 1, 'b': 1})
Position 61: Counter({'0': 1, '8': 1, '1': 1, '9': 1, '2': 1, 'e': 1})
Position 62: Counter({'9': 2, '4': 1, '5': 1, '1': 1, '2': 1})
Position 63: Counter({'e': 1, '6': 1, '8': 1, '4': 1, '2': 1, '3': 1})
--- Detailed Double Character Analysis ---
Double Character Counts:
Counter({'22': 4, '55': 3, '44': 3, 'ff': 2, '66': 2, '77': 2, '00': 1, 'ee': 1, 'cc': 1, 'aa': 1})
Double Character Positions (Double Char: {Position: Count}):
Double '55': Counter({16: 1, 45: 1, 52: 1})
Double '22': Counter({53: 1, 48: 1, 26: 1, 61: 1})
Double '00': Counter({18: 1})
Double 'ff': Counter({21: 1, 3: 1})
Double 'ee': Counter({36: 1})
Double 'cc': Counter({12: 1})
Double '66': Counter({56: 1, 45: 1})
Double '44': Counter({45: 1, 2: 1, 11: 1})
Double '77': Counter({14: 1, 16: 1})
Double 'aa': Counter({14: 1})
--- Top Common Substrings (min_length=3) ---
{'7bf': 1, 'e70': 1, '743': 1, '7d0': 1, '068': 1, '804': 1, '040': 1, '493': 1, 'f95': 1, 'cda': 1}
--- Top Repeated Patterns (min_length=2) ---
{'bf': 1, 'fb': 1, '39': 1, '04': 1}
--- Entropy ---
Shannon Entropy: 3.9733 bits per character
--- Trigram (3-gram) Analysis (Top 10) ---
{'f95': 3, '7bf': 2, 'e70': 2, '17d': 2, '7d0': 2, '743': 2, '267': 2, '804': 2, '040': 2, '493': 2}
--- Run Length Encoding (RLE) Analysis ---
Run Length Counts (lengths > 1):
Counter({2: 16, 3: 4})
--- Character Type Distribution by Position ---
Character Type Counts per Position (Position: {Type: Count}):
Position 0: Counter({'digit': 5, 'letter': 1})
Position 1: Counter({'letter': 5, 'digit': 1})
Position 2: Counter({'digit': 5, 'letter': 1})
Position 3: Counter({'digit': 4, 'letter': 2})
Position 4: Counter({'digit': 4, 'letter': 2})
Position 5: Counter({'digit': 4, 'letter': 2})
Position 6: Counter({'digit': 4, 'letter': 2})
Position 7: Counter({'digit': 5, 'letter': 1})
Position 8: Counter({'letter': 4, 'digit': 2})
Position 9: Counter({'digit': 5, 'letter': 1})
Position 10: Counter({'digit': 4, 'letter': 2})
Position 11: Counter({'letter': 3, 'digit': 3})
Position 12: Counter({'digit': 4, 'letter': 2})
Position 13: Counter({'letter': 3, 'digit': 3})
Position 14: Counter({'digit': 4, 'letter': 2})
Position 15: Counter({'digit': 3, 'letter': 3})
Position 16: Counter({'digit': 4, 'letter': 2})
Position 17: Counter({'digit': 4, 'letter': 2})
Position 18: Counter({'letter': 3, 'digit': 3})
Position 19: Counter({'letter': 3, 'digit': 3})
Position 20: Counter({'digit': 5, 'letter': 1})
Position 21: Counter({'letter': 3, 'digit': 3})
Position 22: Counter({'digit': 5, 'letter': 1})
Position 23: Counter({'digit': 5, 'letter': 1})
Position 24: Counter({'digit': 5, 'letter': 1})
Position 25: Counter({'letter': 4, 'digit': 2})
Position 26: Counter({'digit': 6})
Position 27: Counter({'digit': 6})
Position 28: Counter({'digit': 4, 'letter': 2})
Position 29: Counter({'digit': 3, 'letter': 3})
Position 30: Counter({'letter': 3, 'digit': 3})
Position 31: Counter({'letter': 3, 'digit': 3})
Position 32: Counter({'letter': 4, 'digit': 2})
Position 33: Counter({'digit': 5, 'letter': 1})
Position 34: Counter({'letter': 5, 'digit': 1})
Position 35: Counter({'digit': 5, 'letter': 1})
Position 36: Counter({'letter': 5, 'digit': 1})
Position 37: Counter({'digit': 3, 'letter': 3})
Position 38: Counter({'digit': 4, 'letter': 2})
Position 39: Counter({'digit': 3, 'letter': 3})
Position 40: Counter({'letter': 3, 'digit': 3})
Position 41: Counter({'digit': 6})
Position 42: Counter({'digit': 3, 'letter': 3})
Position 43: Counter({'digit': 4, 'letter': 2})
Position 44: Counter({'digit': 5, 'letter': 1})
Position 45: Counter({'digit': 5, 'letter': 1})
Position 46: Counter({'digit': 5, 'letter': 1})
Position 47: Counter({'digit': 4, 'letter': 2})
Position 48: Counter({'digit': 4, 'letter': 2})
Position 49: Counter({'digit': 3, 'letter': 3})
Position 50: Counter({'digit': 3, 'letter': 3})
Position 51: Counter({'digit': 4, 'letter': 2})
Position 52: Counter({'digit': 3, 'letter': 3})
Position 53: Counter({'digit': 5, 'letter': 1})
Position 54: Counter({'digit': 6})
Position 55: Counter({'digit': 5, 'letter': 1})
Position 56: Counter({'digit': 5, 'letter': 1})
Position 57: Counter({'letter': 4, 'digit': 2})
Position 58: Counter({'digit': 4, 'letter': 2})
Position 59: Counter({'digit': 5, 'letter': 1})
Position 60: Counter({'digit': 4, 'letter': 2})
Position 61: Counter({'digit': 5, 'letter': 1})
Position 62: Counter({'digit': 6})
Position 63: Counter({'digit': 5, 'letter': 1})
--- Average Hamming Distance (Intra-Set) ---
Average Hamming Distance between hashes in the set: 60.07
--- Positional Character Set Diversity ---
Number of Unique Characters at Each Position (Position: Count):
{0: 5, 1: 4, 2: 5, 3: 6, 4: 6, 5: 5, 6: 4, 7: 5, 8: 5, 9: 5, 10: 5, 11: 5, 12: 6, 13: 5, 14: 5, 15: 4, 16: 5, 17: 6, 18: 5, 19: 6, 20: 5, 21: 5, 22: 5, 23: 5, 24: 5, 25: 5, 26: 4, 27: 4, 28: 5, 29: 6, 30: 6, 31: 4, 32: 4, 33: 6, 34: 4, 35: 6, 36: 4, 37: 4, 38: 6, 39: 5, 40: 5, 41: 4, 42: 5, 43: 5, 44: 5, 45: 5, 46: 6, 47: 6, 48: 6, 49: 6, 50: 6, 51: 5, 52: 6, 53: 5, 54: 4, 55: 5, 56: 5, 57: 5, 58: 4, 59: 5, 60: 6, 61: 6, 62: 5, 63: 6}
--- Matrix Representation (First 3 rows, for illustration) ---
Numerical Matrix Representation (Hex to Integer):
[[ 3 13 9 13 7 10 12 2 10 7 11 15 7 10 3 7 5 5 11 14 13 14 7 0
3 10 6 3 10 0 15 10 1 7 13 0 14 2 0 6 10 5 7 5 2 5 5 5
7 1 7 4 3 2 2 6 7 15 13 9 7 0 9 14]
[ 0 4 14 3 11 4 0 2 3 1 4 8 0 4 0 12 4 13 0 0 0 15 15 3
4 9 3 1 0 7 3 1 7 13 12 2 14 14 15 5 1 3 15 14 7 11 15 11
15 11 14 7 0 6 8 9 0 12 3 7 6 8 4 6]
[ 4 14 1 7 3 9 3 9 12 3 0 15 12 12 13 10 0 6 8 12 5 15 9 5
9 15 7 6 4 8 12 1 13 6 10 8 6 2 5 11 10 9 14 13 12 6 3 2
4 13 11 13 15 7 4 3 6 6 13 5 15 1 5 8]]
------------------- End of Detailed Analytics -------------------
The point of my firmware is not doing this type of work for you its harnessing the massive power of the ASIC but its depending on you understanding how to reduce a keyspace