#!/usr/bin/env python3
import os
import re
import sys
import zlib
from pathlib import Path
ASCII_ART = r"""
_____ __ ___ ______
/ ___// / / |/_ __/
\__ \/ / / /| | / /
___/ / /___/ ___ |/ /
/____/_____/_/ |_/_/
"""
WEBHOOK_PATTERN = re.compile(
rb'https?://(?:discord(?:app)?\.com)/api/webhooks/\d+/[a-zA-Z0-9_-]+(?:\?.*)?',
re.IGNORECASE
)
def extract_ascii_strings(data, min_len=8):
strings = []
current = bytearray()
for b in data:
if 32 <= b <= 126:
current.append(b)
else:
if len(current) >= min_len:
strings.append(bytes(current))
current = bytearray()
if len(current) >= min_len:
strings.append(bytes(current))
return strings
def find_zlib_streams(data):
results = []
for i in range(len(data) - 2):
if data[i] == 0x78 and (data[i+1] == 0x9C or data[i+1] == 0xDA):
try:
decomp = zlib.decompress(data[i:], wbits=zlib.MAX_WBITS)
for match in WEBHOOK_PATTERN.finditer(decomp):
results.append(match.group().decode('utf-8', errors='ignore'))
except zlib.error:
pass
return results
def extract_pyinstaller_pyz(data):
pyz_marker = b'PYZ-00.pyz'
idx = data.find(pyz_marker)
if idx == -1:
return []
start = idx + len(pyz_marker)
for offset in range(0, 100):
if start + offset + 2 > len(data):
break
if data[start+offset] == 0x78 and (data[start+offset+1] == 0x9C or data[start+offset+1] == 0xDA):
try:
decomp = zlib.decompress(data[start+offset:], wbits=zlib.MAX_WBITS)
results = []
for match in WEBHOOK_PATTERN.finditer(decomp):
results.append(match.group().decode('utf-8', errors='ignore'))
return results
except zlib.error:
pass
return []
def scan_deep(file_path):
results = []
try:
with open(file_path, 'rb') as f:
data = f.read()
except Exception:
return results
for match in WEBHOOK_PATTERN.finditer(data):
results.append((0, match.group().decode('utf-8', errors='ignore')))
for s in extract_ascii_strings(data):
for match in WEBHOOK_PATTERN.finditer(s):
results.append((0, match.group().decode('utf-8', errors='ignore')))
try:
text = data.decode('utf-16', errors='ignore')
for line_num, line in enumerate(text.splitlines(), 1):
for match in WEBHOOK_PATTERN.finditer(line.encode('utf-8', errors='ignore')):
results.append((line_num, match.group().decode('utf-8', errors='ignore')))
except Exception:
pass
for url in find_zlib_streams(data):
results.append((0, url))
for url in extract_pyinstaller_pyz(data):
results.append((0, url))
seen = set()
unique = []
for line_num, url in results:
if url not in seen:
seen.add(url)
unique.append((line_num, url))
return unique
def scan_path(path):
path = Path(path)
if path.is_file():
yield path, scan_deep(path)
elif path.is_dir():
for root, dirs, files in os.walk(path):
for file in files:
file_path = Path(root) / file
yield file_path, scan_deep(file_path)
else:
raise ValueError(f"{path} is not a valid file or directory.")
def clear():
os.system('cls' if os.name == 'nt' else 'clear')
def run_scan():
clear()
print(ASCII_ART)
path = input("\nDrag file or folder here (or type path):\n> ").strip('"')
if not os.path.exists(path):
print(f"Error: '{path}' does not exist.")
input("Press Enter to continue...")
return
clear()
print(ASCII_ART)
print("\nScanning... (this may take a while)\n")
found = False
for file_path, results in scan_path(path):
if results:
found = True
rel_path = os.path.relpath(file_path, start=os.getcwd())
for line_num, url in results:
print(f"{rel_path}:{line_num if line_num else '?'}: {url}")
if not found:
print("No Webhooks Found")
input("\nPress Enter to return to menu...")
def menu():
while True:
clear()
print(ASCII_ART)
print("\n[1] Scan for Discord Webhooks")
print()
choice = input("Select an option: ")
if choice == "1":
run_scan()
if __name__ == "__main__":
menu()