From d44eacd72f7fc1acbdee7edaea4320b998637687 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 22 May 2020 17:54:38 +0700 Subject: [PATCH 01/18] Temporarily fixes --- hash.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hash.py b/hash.py index 1c66909..c35cfc3 100644 --- a/hash.py +++ b/hash.py @@ -48,11 +48,11 @@ def beta(hashvalue, hashtype): return False def gamma(hashvalue, hashtype): - response = requests.get('https://www.nitrxgen.net/md5db/' + hashvalue, verify=False).text - if response: - return response - else: - return False + # response = requests.get('https://www.nitrxgen.net/md5db/' + hashvalue, verify=False).text + # if response: + # return response + # else: + return False def delta(hashvalue, hashtype): #data = {'auth':'8272hgt', 'hash':hashvalue, 'string':'','Submit':'Submit'} From e14dbd2ef4715ee8d509061c9a1856a68363babf Mon Sep 17 00:00:00 2001 From: 0x01code Date: Tue, 10 Jan 2023 14:59:24 +0700 Subject: [PATCH 02/18] Update gamma and theta --- hash.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hash.py b/hash.py index 2a81292..249687a 100644 --- a/hash.py +++ b/hash.py @@ -48,11 +48,11 @@ def beta(hashvalue, hashtype): return False def gamma(hashvalue, hashtype): - # response = requests.get('https://www.nitrxgen.net/md5db/' + hashvalue, verify=False).text - # if response: - # return response - # else: - return False + response = requests.get('https://www.nitrxgen.net/md5db/' + hashvalue).text + if response: + return response + else: + return False def delta(hashvalue, hashtype): #data = {'auth':'8272hgt', 'hash':hashvalue, 'string':'','Submit':'Submit'} @@ -64,7 +64,7 @@ def delta(hashvalue, hashtype): return False def theta(hashvalue, hashtype): - response = requests.get('https://md5decrypt.net/Api/api.php?hash=%s&hash_type=%s&email=deanna_abshire@proxymail.eu&code=1152464b80a61728' % (hashvalue, hashtype)).text + response = requests.get('https://md5decrypt.net/Api/api.php?hash=%s&hash_type=%s&email=hogad85544@themesw.com&code=85d8d439297a8764' % (hashvalue, hashtype)).text if len(response) != 0: return response else: From f316878855518d674265d89b9598b99f8e6ce912 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Mon, 4 Mar 2024 20:27:41 +0700 Subject: [PATCH 03/18] Update Database --- database.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ hash.py | 46 +++++---------------------------- requirements.txt | 1 + 3 files changed, 74 insertions(+), 40 deletions(-) create mode 100644 database.py create mode 100644 requirements.txt diff --git a/database.py b/database.py new file mode 100644 index 0000000..2c1a3e4 --- /dev/null +++ b/database.py @@ -0,0 +1,67 @@ +import requests +import re + +class Database: + + def md5decrypt(hashvalue, hashtype): + """ + md4, md5, sha1, sha256, sha384, sha512 + """ + response = requests.get('https://md5decrypt.net/Api/api.php?hash=%s&hash_type=%s&email=hogad85544@themesw.com&code=85d8d439297a8764' % (hashvalue, hashtype)).text + if len(response) != 0: + return response + else: + return False + + def nitrxgen(hashvalue, hashtype): + """ + md5 + """ + response = requests.get('https://www.nitrxgen.net/md5db/' + hashvalue).text + if response: + return response + else: + return False + + def my_addr(hashvalue, hashtype): + """ + md5 + """ + response = requests.post('http://md5.my-addr.com/md5_decrypt-md5_cracker_online/md5_decoder_tool.php', { + 'md5': hashvalue + }) + result = re.findall(r"Hashed string:\s(.+?)", response.text) + if result: + return result[0] + else: + return False + + def gromweb(hashvalue, hashtype): + """ + md5, sha1 + """ + if hashtype == 'md5': + mode = 'md5' + else: + mode = 'hash' + response = requests.get(f'https://{hashtype}.gromweb.com/?{mode}={hashvalue}').text + if response.find('successfully reversed into the string') > 0: + plain = re.findall(r'(.*?)<\/a>', response)[0] + return plain + else: + return False + + def gromweb(hashvalue, hashtype): + """ + md5, sha1 + """ + if hashtype == 'md5': + mode = 'md5' + else: + mode = 'hash' + response = requests.get(f'https://md5hashing.net/hash/sha256/{hashvalue}').text + if response.find('successfully reversed into the string') > 0: + plain = re.findall(r'(.*?)<\/a>', response)[0] + return plain + else: + return False \ No newline at end of file diff --git a/hash.py b/hash.py index 249687a..bf28a60 100644 --- a/hash.py +++ b/hash.py @@ -2,9 +2,9 @@ import re import os -import requests import argparse import concurrent.futures +from database import Database parser = argparse.ArgumentParser() parser.add_argument('-s', help='hash', dest='hash') @@ -36,49 +36,15 @@ if directory[-1] == '/': directory = directory[:-1] -def alpha(hashvalue, hashtype): - return False - -def beta(hashvalue, hashtype): - response = requests.get('https://hashtoolkit.com/reverse-hash/?hash=' + hashvalue).text - match = re.search(r'/generate-hash/\?text=(.*?)"', response) - if match: - return match.group(1) - else: - return False - -def gamma(hashvalue, hashtype): - response = requests.get('https://www.nitrxgen.net/md5db/' + hashvalue).text - if response: - return response - else: - return False - -def delta(hashvalue, hashtype): - #data = {'auth':'8272hgt', 'hash':hashvalue, 'string':'','Submit':'Submit'} - #response = requests.post('http://hashcrack.com/index.php' , data).text - #match = re.search(r'(.*?)', response) - #if match: - # return match.group(1) - #else: - return False - -def theta(hashvalue, hashtype): - response = requests.get('https://md5decrypt.net/Api/api.php?hash=%s&hash_type=%s&email=hogad85544@themesw.com&code=85d8d439297a8764' % (hashvalue, hashtype)).text - if len(response) != 0: - return response - else: - return False - print ('''\033[1;97m_ _ ____ ____ _ _ ___ _ _ ____ ___ ____ ____ |__| |__| [__ |__| |__] | | [__ | |___ |__/ | | | | ___] | | |__] |__| ___] | |___ | \ %sv3.0\033[0m\n''' % red) -md5 = [gamma, alpha, beta, theta, delta] -sha1 = [alpha, beta, theta, delta] -sha256 = [alpha, beta, theta] -sha384 = [alpha, beta, theta] -sha512 = [alpha, beta, theta] +md5 = [Database.md5decrypt, Database.nitrxgen, Database.my_addr, Database.gromweb] +sha1 = [Database.md5decrypt, Database.gromweb] +sha256 = [Database.md5decrypt] +sha384 = [Database.md5decrypt] +sha512 = [Database.md5decrypt] def crack(hashvalue): result = False diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..663bd1f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +requests \ No newline at end of file From 553be4ff4a31ad2e6f1d02110de02cf0d47d0d13 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Tue, 5 Mar 2024 08:10:37 +0700 Subject: [PATCH 04/18] fix database gromweb --- database.py | 8 ++------ hash.py | 7 +++---- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/database.py b/database.py index 2c1a3e4..27dd951 100644 --- a/database.py +++ b/database.py @@ -53,13 +53,9 @@ def gromweb(hashvalue, hashtype): def gromweb(hashvalue, hashtype): """ - md5, sha1 + Md2, Md4, Md5, Sha1, Sha224, Sha256, Sha384, Sha512, Ripemd128, Ripemd160, Ripemd256, Ripemd320, Whirlpool, Snefru, Snefru256, Gost, Adler32, Crc32, Crc32b, Crc32b php, Fnv132, Fnv164, Fnv1a32, Fnv1a52, Fnv1a64, Fnv1a128, Fnv1a512, Fnv1a1024, Joaat php, Joaat, Murmur3, Djb2, Sdbm, Loselose, Pearson, Farm hash fingerprint32, Farm hash fingerprint64, Haval128,3, Haval160,3, Haval192,3, Haval224,3, Haval256,3, Haval128,4, Haval160,4, Haval192,4, Haval224,4, Haval256,4, Haval128,5, Haval160,5, Haval192,5, Haval224,5, Haval256,5, Tiger128, Tiger160, Tiger192, Tiger160,3 php, Tiger128,4, Tiger160,4, Tiger192,4, Base64, Tiger128,3, Tiger160,3, Tiger192,3, Md5x2, Md5x3, Md5x4, Md5x5 """ - if hashtype == 'md5': - mode = 'md5' - else: - mode = 'hash' - response = requests.get(f'https://md5hashing.net/hash/sha256/{hashvalue}').text + response = requests.get(f'https://md5hashing.net/hash/{hashtype}/{hashvalue}').text if response.find('successfully reversed into the string') > 0: plain = re.findall(r'(.*?)<\/a>', response)[0] return plain diff --git a/hash.py b/hash.py index bf28a60..3293bc0 100644 --- a/hash.py +++ b/hash.py @@ -42,12 +42,11 @@ md5 = [Database.md5decrypt, Database.nitrxgen, Database.my_addr, Database.gromweb] sha1 = [Database.md5decrypt, Database.gromweb] -sha256 = [Database.md5decrypt] -sha384 = [Database.md5decrypt] -sha512 = [Database.md5decrypt] +sha256 = [Database.md5decrypt, Database.gromweb] +sha384 = [Database.md5decrypt, Database.gromweb] +sha512 = [Database.md5decrypt, Database.gromweb] def crack(hashvalue): - result = False if len(hashvalue) == 32: if not file: print ('%s Hash function : MD5' % info) From 32cfbcacdd1a9d369d3bbc9441aaa6d3209114fa Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 16 Aug 2024 15:38:35 +0700 Subject: [PATCH 05/18] fix SyntaxWarning --- hash.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hash.py b/hash.py index 3293bc0..050e9f8 100644 --- a/hash.py +++ b/hash.py @@ -38,7 +38,7 @@ print ('''\033[1;97m_ _ ____ ____ _ _ ___ _ _ ____ ___ ____ ____ |__| |__| [__ |__| |__] | | [__ | |___ |__/ -| | | | ___] | | |__] |__| ___] | |___ | \ %sv3.0\033[0m\n''' % red) +| | | | ___] | | |__] |__| ___] | |___ | \\ %sv3.0\033[0m\n''' % red) md5 = [Database.md5decrypt, Database.nitrxgen, Database.my_addr, Database.gromweb] sha1 = [Database.md5decrypt, Database.gromweb] @@ -98,7 +98,7 @@ def threaded(hashvalue): result[hashvalue] = resp def grepper(directory): - os.system('''grep -Pr "[a-f0-9]{128}|[a-f0-9]{96}|[a-f0-9]{64}|[a-f0-9]{40}|[a-f0-9]{32}" %s --exclude=\*.{png,jpg,jpeg,mp3,mp4,zip,gz} | + os.system('''grep -Pr "[a-f0-9]{128}|[a-f0-9]{96}|[a-f0-9]{64}|[a-f0-9]{40}|[a-f0-9]{32}" %s --exclude=\\*.{png,jpg,jpeg,mp3,mp4,zip,gz} | grep -Po "[a-f0-9]{128}|[a-f0-9]{96}|[a-f0-9]{64}|[a-f0-9]{40}|[a-f0-9]{32}" >> %s/%s.txt''' % (directory, cwd, directory.split('/')[-1])) print ('%s Results saved in %s.txt' % (info, directory.split('/')[-1])) From 6a72681521122b481319f0dd9368c5c2e4639c6d Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 16 Aug 2024 15:38:42 +0700 Subject: [PATCH 06/18] add gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed8ebf5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ \ No newline at end of file From 4ddad54e65882cd3f7245921c9091be38b7c17d5 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 16 Aug 2024 15:40:28 +0700 Subject: [PATCH 07/18] update makefile --- makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makefile b/makefile index cf151db..0e0fbc1 100644 --- a/makefile +++ b/makefile @@ -2,9 +2,11 @@ DESTDIR ?= /usr/local/bin install: @sudo cp hash.py $(DESTDIR)/buster + @sudo cp database.py $(DESTDIR)/database.py @sudo chmod +x $(DESTDIR)/buster @echo "Installation Successful!" uninstall: @sudo rm -f $(DESTDIR)/buster + @sudo rm -f $(DESTDIR)/database.py @echo "Hash-Buster has been removed" From c87ed2e534f91df543ce6a448143dfce5a351294 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 16 Aug 2024 15:49:23 +0700 Subject: [PATCH 08/18] add md5hashing --- database.py | 2 +- hash.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/database.py b/database.py index 27dd951..2a44b4f 100644 --- a/database.py +++ b/database.py @@ -51,7 +51,7 @@ def gromweb(hashvalue, hashtype): else: return False - def gromweb(hashvalue, hashtype): + def md5hashing(hashvalue, hashtype): """ Md2, Md4, Md5, Sha1, Sha224, Sha256, Sha384, Sha512, Ripemd128, Ripemd160, Ripemd256, Ripemd320, Whirlpool, Snefru, Snefru256, Gost, Adler32, Crc32, Crc32b, Crc32b php, Fnv132, Fnv164, Fnv1a32, Fnv1a52, Fnv1a64, Fnv1a128, Fnv1a512, Fnv1a1024, Joaat php, Joaat, Murmur3, Djb2, Sdbm, Loselose, Pearson, Farm hash fingerprint32, Farm hash fingerprint64, Haval128,3, Haval160,3, Haval192,3, Haval224,3, Haval256,3, Haval128,4, Haval160,4, Haval192,4, Haval224,4, Haval256,4, Haval128,5, Haval160,5, Haval192,5, Haval224,5, Haval256,5, Tiger128, Tiger160, Tiger192, Tiger160,3 php, Tiger128,4, Tiger160,4, Tiger192,4, Base64, Tiger128,3, Tiger160,3, Tiger192,3, Md5x2, Md5x3, Md5x4, Md5x5 """ diff --git a/hash.py b/hash.py index 050e9f8..103618b 100644 --- a/hash.py +++ b/hash.py @@ -40,11 +40,11 @@ |__| |__| [__ |__| |__] | | [__ | |___ |__/ | | | | ___] | | |__] |__| ___] | |___ | \\ %sv3.0\033[0m\n''' % red) -md5 = [Database.md5decrypt, Database.nitrxgen, Database.my_addr, Database.gromweb] -sha1 = [Database.md5decrypt, Database.gromweb] -sha256 = [Database.md5decrypt, Database.gromweb] -sha384 = [Database.md5decrypt, Database.gromweb] -sha512 = [Database.md5decrypt, Database.gromweb] +md5 = [Database.md5decrypt, Database.nitrxgen, Database.my_addr, Database.gromweb, Database.md5hashing] +sha1 = [Database.md5decrypt, Database.gromweb, Database.md5hashing] +sha256 = [Database.md5decrypt, Database.gromweb, Database.md5hashing] +sha384 = [Database.md5decrypt, Database.gromweb, Database.md5hashing] +sha512 = [Database.md5decrypt, Database.gromweb, Database.md5hashing] def crack(hashvalue): if len(hashvalue) == 32: From 7c901bf28a8ee46ce05a782b78a97f768091ada8 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Thu, 29 Aug 2024 16:08:52 +0700 Subject: [PATCH 09/18] check limit api md5decrypt --- database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database.py b/database.py index 2a44b4f..c5c952f 100644 --- a/database.py +++ b/database.py @@ -8,7 +8,7 @@ def md5decrypt(hashvalue, hashtype): md4, md5, sha1, sha256, sha384, sha512 """ response = requests.get('https://md5decrypt.net/Api/api.php?hash=%s&hash_type=%s&email=hogad85544@themesw.com&code=85d8d439297a8764' % (hashvalue, hashtype)).text - if len(response) != 0: + if 'CODE ERREUR' not in response: return response else: return False From d93e41ddf0dda660a622d9544154ca9bd85f2482 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Thu, 11 Dec 2025 10:36:23 +0700 Subject: [PATCH 10/18] update database --- database.py | 26 ++++++++++++++++---------- hash.py | 8 ++++---- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/database.py b/database.py index c5c952f..502c699 100644 --- a/database.py +++ b/database.py @@ -12,16 +12,6 @@ def md5decrypt(hashvalue, hashtype): return response else: return False - - def nitrxgen(hashvalue, hashtype): - """ - md5 - """ - response = requests.get('https://www.nitrxgen.net/md5db/' + hashvalue).text - if response: - return response - else: - return False def my_addr(hashvalue, hashtype): """ @@ -59,5 +49,21 @@ def md5hashing(hashvalue, hashtype): if response.find('successfully reversed into the string') > 0: plain = re.findall(r'(.*?)<\/a>', response)[0] return plain + else: + return False + + def cmd5(hashvalue, hashtype): + """ + md5, sha1, sha256, sha512 + """ + payload = f'__EVENTTARGET=&ctl00%24ContentPlaceHolder1%24TextBoxInput={hashvalue}&ctl00%24ContentPlaceHolder1%24InputHashType=md5&ctl00%24ContentPlaceHolder1%24Button1=decrypt' + headers = { + 'referer': 'https://www.cmd5.org/', + 'Content-Type': 'application/x-www-form-urlencoded', + } + response = requests.post('https://www.cmd5.org/', data=payload, headers=headers).text + plain = re.findall(r'onmouseover="toggle\(\);">(.*?)<\/span>', response) + if plain != '': + return plain else: return False \ No newline at end of file diff --git a/hash.py b/hash.py index 103618b..34da112 100644 --- a/hash.py +++ b/hash.py @@ -40,11 +40,11 @@ |__| |__| [__ |__| |__] | | [__ | |___ |__/ | | | | ___] | | |__] |__| ___] | |___ | \\ %sv3.0\033[0m\n''' % red) -md5 = [Database.md5decrypt, Database.nitrxgen, Database.my_addr, Database.gromweb, Database.md5hashing] -sha1 = [Database.md5decrypt, Database.gromweb, Database.md5hashing] -sha256 = [Database.md5decrypt, Database.gromweb, Database.md5hashing] +md5 = [Database.md5decrypt, Database.my_addr, Database.gromweb, Database.md5hashing, Database.cmd5] +sha1 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.cmd5] +sha256 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.cmd5] sha384 = [Database.md5decrypt, Database.gromweb, Database.md5hashing] -sha512 = [Database.md5decrypt, Database.gromweb, Database.md5hashing] +sha512 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.cmd5] def crack(hashvalue): if len(hashvalue) == 32: From 6766d40096be6441f83c34e68ebda4ca7e649b40 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Thu, 20 Aug 2026 09:25:44 +0700 Subject: [PATCH 11/18] refactor md5decrypt method to use new API endpoint and handle response --- database.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/database.py b/database.py index 502c699..3c81d85 100644 --- a/database.py +++ b/database.py @@ -7,9 +7,16 @@ def md5decrypt(hashvalue, hashtype): """ md4, md5, sha1, sha256, sha384, sha512 """ - response = requests.get('https://md5decrypt.net/Api/api.php?hash=%s&hash_type=%s&email=hogad85544@themesw.com&code=85d8d439297a8764' % (hashvalue, hashtype)).text - if 'CODE ERREUR' not in response: - return response + response = requests.post('https://api.md5decrypt.net/', json={ + "action": "lookup", + "hashes": [ + hashvalue + ] + }, headers={ + 'Authorization': 'Bearer md5d_live_e4453e32_hGr9mR0aETTHqWra6eaz2Q_B6SOYnVvCkLVz1NeRxo0' + }) + if response.status_code == 200: + return response.json()['results'][0]['plaintext'] else: return False From 2288f253b5fb6c3e06a764aabc6c5e052b121268 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Thu, 20 Aug 2026 12:42:25 +0700 Subject: [PATCH 12/18] implement websocket-based md5hashing method, update crackcrypt service, and add liveness tests --- database.py | 71 ++++++++++++++++++++++++++--------- hash.py | 10 ++--- pytest.ini | 3 ++ requirements.txt | 4 +- test_database.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 24 deletions(-) create mode 100644 pytest.ini create mode 100644 test_database.py diff --git a/database.py b/database.py index 3c81d85..e6af889 100644 --- a/database.py +++ b/database.py @@ -1,5 +1,10 @@ import requests import re +import json +import random +import string +import time +from websocket import create_connection class Database: @@ -50,27 +55,57 @@ def gromweb(hashvalue, hashtype): def md5hashing(hashvalue, hashtype): """ - Md2, Md4, Md5, Sha1, Sha224, Sha256, Sha384, Sha512, Ripemd128, Ripemd160, Ripemd256, Ripemd320, Whirlpool, Snefru, Snefru256, Gost, Adler32, Crc32, Crc32b, Crc32b php, Fnv132, Fnv164, Fnv1a32, Fnv1a52, Fnv1a64, Fnv1a128, Fnv1a512, Fnv1a1024, Joaat php, Joaat, Murmur3, Djb2, Sdbm, Loselose, Pearson, Farm hash fingerprint32, Farm hash fingerprint64, Haval128,3, Haval160,3, Haval192,3, Haval224,3, Haval256,3, Haval128,4, Haval160,4, Haval192,4, Haval224,4, Haval256,4, Haval128,5, Haval160,5, Haval192,5, Haval224,5, Haval256,5, Tiger128, Tiger160, Tiger192, Tiger160,3 php, Tiger128,4, Tiger160,4, Tiger192,4, Base64, Tiger128,3, Tiger160,3, Tiger192,3, Md5x2, Md5x3, Md5x4, Md5x5 + md5hashing.net via its Meteor DDP-over-SockJS websocket. + Supported: md2, md4, md5, sha1, sha224, sha256, sha384, sha512, + ripemd128/160/256/320, whirlpool, gost, tiger, and more. """ - response = requests.get(f'https://md5hashing.net/hash/{hashtype}/{hashvalue}').text - if response.find('successfully reversed into the string') > 0: - plain = re.findall(r'(.*?)<\/a>', response)[0] - return plain - else: + deadline = time.monotonic() + 20 # md5hashing.net can be slow to answer + server = random.randint(100, 999) + session = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8)) + url = f'wss://md5hashing.net/sockjs/{server}/{session}/websocket' + try: + ws = create_connection(url, timeout=20, + origin='https://md5hashing.net', + header=['User-Agent: Mozilla/5.0']) + except Exception: + return False + try: + ws.send(json.dumps(['{"msg":"connect","version":"1","support":["1","pre2","pre1"]}'])) + ws.send(json.dumps([json.dumps({ + 'msg': 'method', 'method': 'hash.get', + 'params': [hashtype, hashvalue], 'id': '1', + })])) + while True: + remaining = deadline - time.monotonic() + if remaining <= 0: + return False + ws.settimeout(remaining) + frame = ws.recv() + if not frame or frame[0] == 'c': # SockJS close / stream end + break + if frame[0] != 'a': # 'o' open / 'h' heartbeat + continue + for raw in json.loads(frame[1:]): + data = json.loads(raw) + if data.get('msg') == 'result' and data.get('id') == '1': + return (data.get('result') or {}).get('value') or False + if data.get('msg') == 'error': + return False + except Exception: return False + finally: + ws.close() + return False - def cmd5(hashvalue, hashtype): + + def crackcrypt(hashvalue, hashtype): """ md5, sha1, sha256, sha512 """ - payload = f'__EVENTTARGET=&ctl00%24ContentPlaceHolder1%24TextBoxInput={hashvalue}&ctl00%24ContentPlaceHolder1%24InputHashType=md5&ctl00%24ContentPlaceHolder1%24Button1=decrypt' - headers = { - 'referer': 'https://www.cmd5.org/', - 'Content-Type': 'application/x-www-form-urlencoded', - } - response = requests.post('https://www.cmd5.org/', data=payload, headers=headers).text - plain = re.findall(r'onmouseover="toggle\(\);">(.*?)<\/span>', response) - if plain != '': - return plain - else: - return False \ No newline at end of file + response = requests.post('https://crackcrypt.com/api/v1/lookup', + json={'hash': hashvalue, 'alg': hashtype}, timeout=15) + if response.status_code == 200: + data = response.json() + if data.get('found'): + return data.get('plaintext') + return False diff --git a/hash.py b/hash.py index 34da112..a196dd3 100644 --- a/hash.py +++ b/hash.py @@ -40,11 +40,11 @@ |__| |__| [__ |__| |__] | | [__ | |___ |__/ | | | | ___] | | |__] |__| ___] | |___ | \\ %sv3.0\033[0m\n''' % red) -md5 = [Database.md5decrypt, Database.my_addr, Database.gromweb, Database.md5hashing, Database.cmd5] -sha1 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.cmd5] -sha256 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.cmd5] -sha384 = [Database.md5decrypt, Database.gromweb, Database.md5hashing] -sha512 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.cmd5] +md5 = [Database.md5decrypt, Database.my_addr, Database.gromweb, Database.md5hashing, Database.crackcrypt] +sha1 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.crackcrypt] +sha256 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt] +sha384 = [Database.md5decrypt, Database.md5hashing] +sha512 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt] def crack(hashvalue): if len(hashvalue) == 32: diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..ded686f --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +markers = + network: test performs live network calls to third-party hash-lookup services diff --git a/requirements.txt b/requirements.txt index 663bd1f..60c2f31 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ -requests \ No newline at end of file +requests +pytest +websocket-client diff --git a/test_database.py b/test_database.py new file mode 100644 index 0000000..402b5ba --- /dev/null +++ b/test_database.py @@ -0,0 +1,96 @@ +""" +Liveness / integration tests for Hash-Buster online services. + +Each test submits a known hash of the common password "password" to a service +and checks that the correct plaintext comes back. A FAIL means that service (or +that hash type on that service) is currently NOT working -- it is down, changed +its API/HTML, rate-limited us, or the hash is no longer in its database. + +These tests hit the real network. Run only the liveness suite with: + + python -m pytest test_database.py -v + python -m pytest test_database.py -v -m network # explicit + python -m pytest test_database.py -v -m "not network" # skip (offline) +""" +import concurrent.futures +import time + +import pytest + +from database import Database + +# Known hashes of the plaintext "password" for each supported algorithm. +HASHES = { + 'md5': '5f4dcc3b5aa765d61d8327deb882cf99', + 'sha1': '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8', + 'sha256': '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', + 'sha384': 'a8b64babd0aca91a59bdbb7761b421d4f2bb38280d3a75ba0f21f2bebc45583d4' + '46c598660c94ce680c47d19c30783a7', + 'sha512': 'b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb98' + '0b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86', +} +EXPECTED = 'password' +CALL_TIMEOUT = 20 # seconds; a service that hangs longer counts as down +RETRY_DELAY = 2 # seconds; retry once after a delay to ride out rate limits + +# Each service -> the hash types it claims to support (docstring / mapping). +# Covers both the original 5 services and the 2 new ones so the suite reports +# liveness for every website in the project. +SERVICES = { + Database.md5decrypt: ['md5', 'sha1', 'sha256', 'sha384', 'sha512'], + Database.my_addr: ['md5'], + Database.gromweb: ['md5', 'sha1'], + Database.md5hashing: ['md5', 'sha1', 'sha256', 'sha384', 'sha512'], + Database.crackcrypt: ['md5', 'sha1', 'sha256', 'sha512'], +} + +# Flatten into (service, hashtype) test cases. +CASES = [(svc, ht) for svc, hts in SERVICES.items() for ht in hts] + + +def _call(service, hashvalue, hashtype): + """Run a service call under a hard timeout so a dead host can't hang pytest.""" + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit(service, hashvalue, hashtype) + return future.result(timeout=CALL_TIMEOUT) + + +@pytest.mark.network +@pytest.mark.parametrize( + 'service,hashtype', + CASES, + ids=[f'{svc.__name__}-{ht}' for svc, ht in CASES], +) +def test_service_liveness(service, hashtype): + name = service.__name__ + result = None + # Retry once on empty/error: some services (e.g. crackcrypt: 1 req/s) rate-limit us. + for attempt in range(2): + try: + result = _call(service, HASHES[hashtype], hashtype) + except concurrent.futures.TimeoutError: + if attempt == 0: + time.sleep(RETRY_DELAY) + continue + pytest.fail(f'{name} ({hashtype}): DOWN - no response within {CALL_TIMEOUT}s') + except Exception as exc: # network error, bad JSON, etc. + if attempt == 0: + time.sleep(RETRY_DELAY) + continue + pytest.fail(f'{name} ({hashtype}): DOWN - {type(exc).__name__}: {exc}') + if result: + break + if attempt == 0: + time.sleep(RETRY_DELAY) + + # Services return False (or empty) when they cannot crack / are broken. + if not result: + pytest.fail(f'{name} ({hashtype}): returned no plaintext (not working / hash not in DB)') + + # Some services return a list from re.findall; normalise before comparing. + if isinstance(result, (list, tuple)): + result = result[0] if result else '' + + assert str(result).strip() == EXPECTED, ( + f'{name} ({hashtype}): expected {EXPECTED!r}, got {result!r}' + ) From f4d4f72407aaecb7baaececc682211957e53e79c Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 21 Aug 2026 08:13:44 +0700 Subject: [PATCH 13/18] add weakpass service for hash type detection and update test cases --- database.py | 16 ++++++++++++++++ hash.py | 6 +++--- test_database.py | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/database.py b/database.py index e6af889..e1a6035 100644 --- a/database.py +++ b/database.py @@ -109,3 +109,19 @@ def crackcrypt(hashvalue, hashtype): if data.get('found'): return data.get('plaintext') return False + + def weakpass(hashvalue, hashtype): + """ + weakpass.com precomputed lookup. Auto-detects the hash type, so + hashtype is unused. Supported: md5, ntlm, sha1, sha256. + """ + response = requests.get(f'https://weakpass.com/api/v1/search/{hashvalue}.json', + timeout=15) + if response.status_code == 200: + data = response.json() + # The API may return a single object or a list of matches. + if isinstance(data, list): + data = data[0] if data else {} + if isinstance(data, dict): + return data.get('pass') or False + return False diff --git a/hash.py b/hash.py index a196dd3..7daec9f 100644 --- a/hash.py +++ b/hash.py @@ -40,9 +40,9 @@ |__| |__| [__ |__| |__] | | [__ | |___ |__/ | | | | ___] | | |__] |__| ___] | |___ | \\ %sv3.0\033[0m\n''' % red) -md5 = [Database.md5decrypt, Database.my_addr, Database.gromweb, Database.md5hashing, Database.crackcrypt] -sha1 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.crackcrypt] -sha256 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt] +md5 = [Database.md5decrypt, Database.my_addr, Database.gromweb, Database.md5hashing, Database.crackcrypt, Database.weakpass] +sha1 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.crackcrypt, Database.weakpass] +sha256 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt, Database.weakpass] sha384 = [Database.md5decrypt, Database.md5hashing] sha512 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt] diff --git a/test_database.py b/test_database.py index 402b5ba..d8c32a2 100644 --- a/test_database.py +++ b/test_database.py @@ -42,6 +42,7 @@ Database.gromweb: ['md5', 'sha1'], Database.md5hashing: ['md5', 'sha1', 'sha256', 'sha384', 'sha512'], Database.crackcrypt: ['md5', 'sha1', 'sha256', 'sha512'], + Database.weakpass: ['md5', 'sha1', 'sha256'], } # Flatten into (service, hashtype) test cases. From 054846ef1baa13ea31fdd2977da5d69e1029ae2f Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 21 Aug 2026 08:41:45 +0700 Subject: [PATCH 14/18] update README and database to support NTLM hash type detection; add tests for NTLM hashes --- README.md | 12 +++++- database.py | 21 +++++++--- hash.py | 99 ++++++++++++++++++++++++++++++------------------ test_database.py | 5 ++- 4 files changed, 92 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 5ea6d01..0ac5f1d 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ## Features - Automatic hash type identification -- Supports MD5, SHA1, SHA256, SHA384, SHA512 +- Supports MD5, NTLM, SHA1, SHA256, SHA384, SHA512 - Can extract & crack hashes from a file - Can find hashes from a directory, recursively - Multi-threading @@ -40,6 +40,16 @@ After the installation, you will be able to access it with `buster` command. You don't need to specify the hash type. Hash Buster will identify and *crack* it under 3 seconds. **Usage:** `buster -s ` + +> **NTLM:** NTLM hashes are 32 characters long, exactly like MD5, so they can't be told apart automatically. For any 32-char hash, Hash Buster looks it up as **both** MD5 and NTLM and reports both results: +> ``` +> [!] Hash function : MD5 / NTLM +> MD5 : (not found) +> NTLM : password +> ``` +> You can also force a single type with `-T` (md5, ntlm, sha1, sha256, sha384, sha512): +> `buster -s -T ntlm` + ### Finding hashes from a directory Yep, just specify a directory and Hash Buster will go through all the files and directories present in it, looking for hashes. diff --git a/database.py b/database.py index e1a6035..b124f7f 100644 --- a/database.py +++ b/database.py @@ -21,7 +21,13 @@ def md5decrypt(hashvalue, hashtype): 'Authorization': 'Bearer md5d_live_e4453e32_hGr9mR0aETTHqWra6eaz2Q_B6SOYnVvCkLVz1NeRxo0' }) if response.status_code == 200: - return response.json()['results'][0]['plaintext'] + res = response.json()['results'][0] + # The API auto-detects the hash type; keep only a match whose + # detected type equals the requested one so md5 and ntlm (both + # 32 chars) don't cross over. + if hashtype and res.get('type') and res.get('type') != hashtype: + return False + return res.get('plaintext') or False else: return False @@ -112,16 +118,21 @@ def crackcrypt(hashvalue, hashtype): def weakpass(hashvalue, hashtype): """ - weakpass.com precomputed lookup. Auto-detects the hash type, so - hashtype is unused. Supported: md5, ntlm, sha1, sha256. + weakpass.com precomputed lookup. The API auto-detects the hash type and + reports it; we only accept a match whose detected type equals the + requested one, so md5 and ntlm (both 32 chars) don't cross over. + Supported: md5, ntlm, sha1, sha256. """ - response = requests.get(f'https://weakpass.com/api/v1/search/{hashvalue}.json', + # The API only matches lowercase hex; NTLM hashes are often uppercase. + response = requests.get(f'https://weakpass.com/api/v1/search/{hashvalue.lower()}.json', timeout=15) if response.status_code == 200: data = response.json() # The API may return a single object or a list of matches. if isinstance(data, list): data = data[0] if data else {} - if isinstance(data, dict): + if isinstance(data, dict) and data: + if hashtype and data.get('type') and data.get('type') != hashtype: + return False return data.get('pass') or False return False diff --git a/hash.py b/hash.py index 7daec9f..a9a41a8 100644 --- a/hash.py +++ b/hash.py @@ -11,6 +11,8 @@ parser.add_argument('-f', help='file containing hashes', dest='file') parser.add_argument('-d', help='directory containing hashes', dest='dir') parser.add_argument('-t', help='number of threads', dest='threads', type=int) +parser.add_argument('-T', help='force hash type (md5, ntlm, sha1, sha256, sha384, sha512)', + dest='type', choices=['md5', 'ntlm', 'sha1', 'sha256', 'sha384', 'sha512']) args = parser.parse_args() #Colors and shit like that @@ -41,61 +43,84 @@ | | | | ___] | | |__] |__| ___] | |___ | \\ %sv3.0\033[0m\n''' % red) md5 = [Database.md5decrypt, Database.my_addr, Database.gromweb, Database.md5hashing, Database.crackcrypt, Database.weakpass] +ntlm = [Database.md5decrypt, Database.weakpass] sha1 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.crackcrypt, Database.weakpass] sha256 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt, Database.weakpass] sha384 = [Database.md5decrypt, Database.md5hashing] sha512 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt] +# Maps a forced hash type (-T) to its service list. NTLM and MD5 share the +# same 32-char length, so it cannot be auto-detected -- use -T ntlm to force it. +apis = { + 'md5': md5, 'ntlm': ntlm, 'sha1': sha1, + 'sha256': sha256, 'sha384': sha384, 'sha512': sha512, +} +LABELS = {'md5': 'MD5', 'ntlm': 'NTLM', 'sha1': 'SHA1', + 'sha256': 'SHA-256', 'sha384': 'SHA-384', 'sha512': 'SHA-512'} + +def lookup(api_list, hashvalue, hashtype): + """Run each service for a hash type, returning the first plaintext found.""" + for api in api_list: + r = api(hashvalue, hashtype) + if r: + return r + return False + def crack(hashvalue): - if len(hashvalue) == 32: - if not file: - print ('%s Hash function : MD5' % info) - for api in md5: - r = api(hashvalue, 'md5') - if r: - return r - elif len(hashvalue) == 40: + """ + Return a dict mapping a hash-type label -> cracked plaintext for every + match found, or False if nothing was cracked. A 32-char hash is looked up + as BOTH md5 and ntlm (same length, indistinguishable) so both results are + reported. -T forces a single type. + """ + # -T forces the hash type (e.g. ntlm, which is 32 chars like md5). + if args.type: if not file: - print ('%s Hash function : SHA1' % info) - for api in sha1: - r = api(hashvalue, 'sha1') - if r: - return r - elif len(hashvalue) == 64: - if not file: - print ('%s Hash function : SHA-256' % info) - for api in sha256: - r = api(hashvalue, 'sha256') - if r: - return r - elif len(hashvalue) == 96: + print ('%s Hash function : %s (forced)' % (info, LABELS[args.type])) + r = lookup(apis[args.type], hashvalue, args.type) + return {LABELS[args.type]: r} if r else False + + length = len(hashvalue) + if length == 32: if not file: - print ('%s Hash function : SHA-384' % info) - for api in sha384: - r = api(hashvalue, 'sha384') - if r: - return r - elif len(hashvalue) == 128: + print ('%s Hash function : MD5 / NTLM' % info) + # 32-char hashes are ambiguous (md5 vs ntlm) -- report both attempts. + results = {LABELS[ht]: lookup(apis[ht], hashvalue, ht) for ht in ('md5', 'ntlm')} + return results if any(results.values()) else False + elif length in (40, 64, 96, 128): + ht = {40: 'sha1', 64: 'sha256', 96: 'sha384', 128: 'sha512'}[length] if not file: - print ('%s Hash function : SHA-512' % info) - for api in sha512: - r = api(hashvalue, 'sha512') - if r: - return r + print ('%s Hash function : %s' % (info, LABELS[ht])) + r = lookup(apis[ht], hashvalue, ht) + return {LABELS[ht]: r} if r else False else: if not file: print ('%s This hash type is not supported.' % bad) quit() - else: - return False + return False + +def display_result(res): + """Multi-line rendering for a single hash on screen (shows every attempt).""" + if len(res) == 1: + return next(iter(res.values())) + return '\n'.join('%s : %s' % (label, plain if plain else '(not found)') + for label, plain in res.items()) + +def inline_result(res): + """One-line rendering for file output / multi-hash mode (found results only).""" + found = [(label, plain) for label, plain in res.items() if plain] + if len(res) == 1: + return found[0][1] if found else '' + return ', '.join('%s (%s)' % (plain, label) for label, plain in found) result = {} def threaded(hashvalue): resp = crack(hashvalue) if resp: - print (hashvalue + ' : ' + resp) - result[hashvalue] = resp + line = inline_result(resp) + print (hashvalue + ' : ' + line) + result[hashvalue] = line def grepper(directory): os.system('''grep -Pr "[a-f0-9]{128}|[a-f0-9]{96}|[a-f0-9]{64}|[a-f0-9]{40}|[a-f0-9]{32}" %s --exclude=\\*.{png,jpg,jpeg,mp3,mp4,zip,gz} | @@ -123,7 +148,7 @@ def miner(file): def single(args): result = crack(args.hash) if result: - print (result) + print (display_result(result)) else: print ('%s Hash was not found in any database.' % bad) diff --git a/test_database.py b/test_database.py index d8c32a2..ef598a2 100644 --- a/test_database.py +++ b/test_database.py @@ -22,6 +22,7 @@ # Known hashes of the plaintext "password" for each supported algorithm. HASHES = { 'md5': '5f4dcc3b5aa765d61d8327deb882cf99', + 'ntlm': '8846f7eaee8fb117ad06bdd830b7586c', 'sha1': '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8', 'sha256': '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'sha384': 'a8b64babd0aca91a59bdbb7761b421d4f2bb38280d3a75ba0f21f2bebc45583d4' @@ -37,12 +38,12 @@ # Covers both the original 5 services and the 2 new ones so the suite reports # liveness for every website in the project. SERVICES = { - Database.md5decrypt: ['md5', 'sha1', 'sha256', 'sha384', 'sha512'], + Database.md5decrypt: ['md5', 'ntlm', 'sha1', 'sha256', 'sha384', 'sha512'], Database.my_addr: ['md5'], Database.gromweb: ['md5', 'sha1'], Database.md5hashing: ['md5', 'sha1', 'sha256', 'sha384', 'sha512'], Database.crackcrypt: ['md5', 'sha1', 'sha256', 'sha512'], - Database.weakpass: ['md5', 'sha1', 'sha256'], + Database.weakpass: ['md5', 'ntlm', 'sha1', 'sha256'], } # Flatten into (service, hashtype) test cases. From 1dfea6b6ca93ded6ae8be279f9825257972a8e04 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 21 Aug 2026 08:44:35 +0700 Subject: [PATCH 15/18] refactor hash type handling to prioritize weakpass service; update method order for consistency --- database.py | 6 +++--- hash.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/database.py b/database.py index b124f7f..4503e9f 100644 --- a/database.py +++ b/database.py @@ -19,7 +19,7 @@ def md5decrypt(hashvalue, hashtype): ] }, headers={ 'Authorization': 'Bearer md5d_live_e4453e32_hGr9mR0aETTHqWra6eaz2Q_B6SOYnVvCkLVz1NeRxo0' - }) + }, timeout=15) if response.status_code == 200: res = response.json()['results'][0] # The API auto-detects the hash type; keep only a match whose @@ -37,7 +37,7 @@ def my_addr(hashvalue, hashtype): """ response = requests.post('http://md5.my-addr.com/md5_decrypt-md5_cracker_online/md5_decoder_tool.php', { 'md5': hashvalue - }) + }, timeout=15) result = re.findall(r"Hashed string:\s(.+?)", response.text) if result: return result[0] @@ -52,7 +52,7 @@ def gromweb(hashvalue, hashtype): mode = 'md5' else: mode = 'hash' - response = requests.get(f'https://{hashtype}.gromweb.com/?{mode}={hashvalue}').text + response = requests.get(f'https://{hashtype}.gromweb.com/?{mode}={hashvalue}', timeout=15).text if response.find('successfully reversed into the string') > 0: plain = re.findall(r'(.*?)<\/a>', response)[0] return plain diff --git a/hash.py b/hash.py index a9a41a8..284fb5b 100644 --- a/hash.py +++ b/hash.py @@ -42,10 +42,10 @@ |__| |__| [__ |__| |__] | | [__ | |___ |__/ | | | | ___] | | |__] |__| ___] | |___ | \\ %sv3.0\033[0m\n''' % red) -md5 = [Database.md5decrypt, Database.my_addr, Database.gromweb, Database.md5hashing, Database.crackcrypt, Database.weakpass] -ntlm = [Database.md5decrypt, Database.weakpass] -sha1 = [Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.crackcrypt, Database.weakpass] -sha256 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt, Database.weakpass] +md5 = [Database.weakpass, Database.md5decrypt, Database.my_addr, Database.gromweb, Database.md5hashing, Database.crackcrypt] +ntlm = [Database.weakpass, Database.md5decrypt] +sha1 = [Database.weakpass, Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.crackcrypt] +sha256 = [Database.weakpass, Database.md5decrypt, Database.md5hashing, Database.crackcrypt] sha384 = [Database.md5decrypt, Database.md5hashing] sha512 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt] From 0e19a25fdf4f81547c01002a3b4425cd23fa6bdb Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 21 Aug 2026 09:01:51 +0700 Subject: [PATCH 16/18] fix gromweb method to include User-Agent header in requests for improved response handling --- database.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database.py b/database.py index 4503e9f..7dbae2b 100644 --- a/database.py +++ b/database.py @@ -52,9 +52,9 @@ def gromweb(hashvalue, hashtype): mode = 'md5' else: mode = 'hash' - response = requests.get(f'https://{hashtype}.gromweb.com/?{mode}={hashvalue}', timeout=15).text - if response.find('successfully reversed into the string') > 0: - plain = re.findall(r'(.*?)<\/a>', response)[0] + response = requests.get(f'https://{hashtype}.gromweb.com/?{mode}={hashvalue}', headers={'User-Agent': 'Mozilla/5.0'}, timeout=15) + if response.text.find('successfully reversed into the string') > 0: + plain = re.findall(r'(.*?)<\/a>', response.text)[0] return plain else: return False From bcd85a232402f48504cc25e3c012ee70870e56c7 Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 21 Aug 2026 09:06:30 +0700 Subject: [PATCH 17/18] remove my_addr method from Database class and update hash method list accordingly --- database.py | 13 ------------- hash.py | 2 +- test_database.py | 1 - 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/database.py b/database.py index 7dbae2b..45444c2 100644 --- a/database.py +++ b/database.py @@ -31,19 +31,6 @@ def md5decrypt(hashvalue, hashtype): else: return False - def my_addr(hashvalue, hashtype): - """ - md5 - """ - response = requests.post('http://md5.my-addr.com/md5_decrypt-md5_cracker_online/md5_decoder_tool.php', { - 'md5': hashvalue - }, timeout=15) - result = re.findall(r"Hashed string:\s(.+?)", response.text) - if result: - return result[0] - else: - return False - def gromweb(hashvalue, hashtype): """ md5, sha1 diff --git a/hash.py b/hash.py index 284fb5b..38370ec 100644 --- a/hash.py +++ b/hash.py @@ -42,7 +42,7 @@ |__| |__| [__ |__| |__] | | [__ | |___ |__/ | | | | ___] | | |__] |__| ___] | |___ | \\ %sv3.0\033[0m\n''' % red) -md5 = [Database.weakpass, Database.md5decrypt, Database.my_addr, Database.gromweb, Database.md5hashing, Database.crackcrypt] +md5 = [Database.weakpass, Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.crackcrypt] ntlm = [Database.weakpass, Database.md5decrypt] sha1 = [Database.weakpass, Database.md5decrypt, Database.gromweb, Database.md5hashing, Database.crackcrypt] sha256 = [Database.weakpass, Database.md5decrypt, Database.md5hashing, Database.crackcrypt] diff --git a/test_database.py b/test_database.py index ef598a2..92896ff 100644 --- a/test_database.py +++ b/test_database.py @@ -39,7 +39,6 @@ # liveness for every website in the project. SERVICES = { Database.md5decrypt: ['md5', 'ntlm', 'sha1', 'sha256', 'sha384', 'sha512'], - Database.my_addr: ['md5'], Database.gromweb: ['md5', 'sha1'], Database.md5hashing: ['md5', 'sha1', 'sha256', 'sha384', 'sha512'], Database.crackcrypt: ['md5', 'sha1', 'sha256', 'sha512'], From 5a5988ac7e4a6f8b7ccabb12a47d772a71d0d48d Mon Sep 17 00:00:00 2001 From: 0x01code Date: Fri, 21 Aug 2026 09:11:32 +0700 Subject: [PATCH 18/18] update README and hash.py to change force hash type option from -T to -m --- README.md | 4 ++-- hash.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0ac5f1d..d548fdd 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,8 @@ You don't need to specify the hash type. Hash Buster will identify and *crack* i > MD5 : (not found) > NTLM : password > ``` -> You can also force a single type with `-T` (md5, ntlm, sha1, sha256, sha384, sha512): -> `buster -s -T ntlm` +> You can also force a single type with `-m` (md5, ntlm, sha1, sha256, sha384, sha512): +> `buster -s -m ntlm` ### Finding hashes from a directory diff --git a/hash.py b/hash.py index 38370ec..baa3df8 100644 --- a/hash.py +++ b/hash.py @@ -11,7 +11,7 @@ parser.add_argument('-f', help='file containing hashes', dest='file') parser.add_argument('-d', help='directory containing hashes', dest='dir') parser.add_argument('-t', help='number of threads', dest='threads', type=int) -parser.add_argument('-T', help='force hash type (md5, ntlm, sha1, sha256, sha384, sha512)', +parser.add_argument('-m', help='force hash type (md5, ntlm, sha1, sha256, sha384, sha512)', dest='type', choices=['md5', 'ntlm', 'sha1', 'sha256', 'sha384', 'sha512']) args = parser.parse_args() @@ -49,8 +49,8 @@ sha384 = [Database.md5decrypt, Database.md5hashing] sha512 = [Database.md5decrypt, Database.md5hashing, Database.crackcrypt] -# Maps a forced hash type (-T) to its service list. NTLM and MD5 share the -# same 32-char length, so it cannot be auto-detected -- use -T ntlm to force it. +# Maps a forced hash type (-m) to its service list. NTLM and MD5 share the +# same 32-char length, so it cannot be auto-detected -- use -m ntlm to force it. apis = { 'md5': md5, 'ntlm': ntlm, 'sha1': sha1, 'sha256': sha256, 'sha384': sha384, 'sha512': sha512, @@ -71,9 +71,9 @@ def crack(hashvalue): Return a dict mapping a hash-type label -> cracked plaintext for every match found, or False if nothing was cracked. A 32-char hash is looked up as BOTH md5 and ntlm (same length, indistinguishable) so both results are - reported. -T forces a single type. + reported. -m forces a single type. """ - # -T forces the hash type (e.g. ntlm, which is 32 chars like md5). + # -m forces the hash type (e.g. ntlm, which is 32 chars like md5). if args.type: if not file: print ('%s Hash function : %s (forced)' % (info, LABELS[args.type]))