basicbuster 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- basicbuster-0.1.2/PKG-INFO +108 -0
- basicbuster-0.1.2/README.md +78 -0
- {basicbuster-0.1.0 → basicbuster-0.1.2}/basicbuster/__init__.py +1 -1
- {basicbuster-0.1.0 → basicbuster-0.1.2}/basicbuster/cli.py +15 -1
- basicbuster-0.1.2/basicbuster/core.py +195 -0
- basicbuster-0.1.2/basicbuster.egg-info/PKG-INFO +108 -0
- {basicbuster-0.1.0 → basicbuster-0.1.2}/pyproject.toml +1 -1
- basicbuster-0.1.0/PKG-INFO +0 -133
- basicbuster-0.1.0/README.md +0 -103
- basicbuster-0.1.0/basicbuster/core.py +0 -191
- basicbuster-0.1.0/basicbuster.egg-info/PKG-INFO +0 -133
- {basicbuster-0.1.0 → basicbuster-0.1.2}/LICENSE +0 -0
- {basicbuster-0.1.0 → basicbuster-0.1.2}/basicbuster.egg-info/SOURCES.txt +0 -0
- {basicbuster-0.1.0 → basicbuster-0.1.2}/basicbuster.egg-info/dependency_links.txt +0 -0
- {basicbuster-0.1.0 → basicbuster-0.1.2}/basicbuster.egg-info/entry_points.txt +0 -0
- {basicbuster-0.1.0 → basicbuster-0.1.2}/basicbuster.egg-info/requires.txt +0 -0
- {basicbuster-0.1.0 → basicbuster-0.1.2}/basicbuster.egg-info/top_level.txt +0 -0
- {basicbuster-0.1.0 → basicbuster-0.1.2}/setup.cfg +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: basicbuster
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: CLI tool for HTTP Basic Authentication testing with wordlists.
|
|
5
|
+
Author: dhina016
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dhina016/basicbuster
|
|
8
|
+
Project-URL: Repository, https://github.com/dhina016/basicbuster
|
|
9
|
+
Project-URL: Issues, https://github.com/dhina016/basicbuster/issues
|
|
10
|
+
Keywords: security,http,basic-auth,pentest,cli
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: requests>=2.28.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# basicbuster
|
|
32
|
+
|
|
33
|
+
**basicbuster** is a command-line tool for testing HTTP Basic Authentication with username and password wordlists. It detects Basic Auth on a target URL, runs pitchfork or clusterbomb-style attempts, and reports credentials that return HTTP `200`.
|
|
34
|
+
|
|
35
|
+
## Disclaimer
|
|
36
|
+
|
|
37
|
+
Use **only** on systems and applications you are **explicitly authorized** to test. Unauthorized access to computer systems is illegal. The authors are not responsible for misuse.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install basicbuster
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
basicbuster --url URL (--username FILE --password FILE | --userpass FILE) [OPTIONS]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Options
|
|
52
|
+
|
|
53
|
+
| Flag | Description |
|
|
54
|
+
|------|-------------|
|
|
55
|
+
| `--url` | Target URL protected by HTTP Basic Auth |
|
|
56
|
+
| `--username FILE` | Path to username wordlist (one per line) |
|
|
57
|
+
| `--password FILE` | Path to password wordlist (one per line) |
|
|
58
|
+
| `--userpass FILE` | Combined `user:pass` file (one per line) |
|
|
59
|
+
| `--mode` | `clusterbomb` (all combinations, default) or `pitchfork` (zip by index) |
|
|
60
|
+
| `-t, --threads` | Number of concurrent threads (default: 10) |
|
|
61
|
+
| `--timeout` | Per-request timeout in seconds (default: 10) |
|
|
62
|
+
| `--proxy URL` | Route traffic through a proxy (e.g. `http://127.0.0.1:8080`) |
|
|
63
|
+
| `-v, --show-attempts` | Print every attempt with HTTP status |
|
|
64
|
+
| `--quiet` | Minimal output |
|
|
65
|
+
|
|
66
|
+
### Examples
|
|
67
|
+
|
|
68
|
+
Clusterbomb (default — all username x password combinations):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt -v
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Pitchfork (pair line-by-line):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt --mode pitchfork -v
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Combined `user:pass` file:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
basicbuster --url https://example.com/secret --userpass combo.txt -v
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
With proxy and 20 threads:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
basicbuster --url https://example.com/secret --userpass combo.txt --proxy http://127.0.0.1:8080 -t 20 -v
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Output
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
[*] Target appears to use HTTP Basic Auth: https://example.com/secret
|
|
96
|
+
[*] Testing 400 credential pair(s) with 10 thread(s)…
|
|
97
|
+
|
|
98
|
+
[1/400] [-] admin:password → HTTP 401
|
|
99
|
+
[2/400] [-] admin:123456 → HTTP 401
|
|
100
|
+
[3/400] [+] admin:secret → HTTP 200 ✓ SUCCESS
|
|
101
|
+
...
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Sample wordlists (20 common entries each) are included in [`samples/`](samples/).
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# basicbuster
|
|
2
|
+
|
|
3
|
+
**basicbuster** is a command-line tool for testing HTTP Basic Authentication with username and password wordlists. It detects Basic Auth on a target URL, runs pitchfork or clusterbomb-style attempts, and reports credentials that return HTTP `200`.
|
|
4
|
+
|
|
5
|
+
## Disclaimer
|
|
6
|
+
|
|
7
|
+
Use **only** on systems and applications you are **explicitly authorized** to test. Unauthorized access to computer systems is illegal. The authors are not responsible for misuse.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install basicbuster
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
basicbuster --url URL (--username FILE --password FILE | --userpass FILE) [OPTIONS]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Options
|
|
22
|
+
|
|
23
|
+
| Flag | Description |
|
|
24
|
+
|------|-------------|
|
|
25
|
+
| `--url` | Target URL protected by HTTP Basic Auth |
|
|
26
|
+
| `--username FILE` | Path to username wordlist (one per line) |
|
|
27
|
+
| `--password FILE` | Path to password wordlist (one per line) |
|
|
28
|
+
| `--userpass FILE` | Combined `user:pass` file (one per line) |
|
|
29
|
+
| `--mode` | `clusterbomb` (all combinations, default) or `pitchfork` (zip by index) |
|
|
30
|
+
| `-t, --threads` | Number of concurrent threads (default: 10) |
|
|
31
|
+
| `--timeout` | Per-request timeout in seconds (default: 10) |
|
|
32
|
+
| `--proxy URL` | Route traffic through a proxy (e.g. `http://127.0.0.1:8080`) |
|
|
33
|
+
| `-v, --show-attempts` | Print every attempt with HTTP status |
|
|
34
|
+
| `--quiet` | Minimal output |
|
|
35
|
+
|
|
36
|
+
### Examples
|
|
37
|
+
|
|
38
|
+
Clusterbomb (default — all username x password combinations):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt -v
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Pitchfork (pair line-by-line):
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt --mode pitchfork -v
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Combined `user:pass` file:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
basicbuster --url https://example.com/secret --userpass combo.txt -v
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
With proxy and 20 threads:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
basicbuster --url https://example.com/secret --userpass combo.txt --proxy http://127.0.0.1:8080 -t 20 -v
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Output
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
[*] Target appears to use HTTP Basic Auth: https://example.com/secret
|
|
66
|
+
[*] Testing 400 credential pair(s) with 10 thread(s)…
|
|
67
|
+
|
|
68
|
+
[1/400] [-] admin:password → HTTP 401
|
|
69
|
+
[2/400] [-] admin:123456 → HTTP 401
|
|
70
|
+
[3/400] [+] admin:secret → HTTP 200 ✓ SUCCESS
|
|
71
|
+
...
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Sample wordlists (20 common entries each) are included in [`samples/`](samples/).
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -4,7 +4,7 @@ import argparse
|
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
6
|
from basicbuster import __version__
|
|
7
|
-
from basicbuster.core import DEFAULT_TIMEOUT, run_attack
|
|
7
|
+
from basicbuster.core import DEFAULT_THREADS, DEFAULT_TIMEOUT, run_attack
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -51,6 +51,20 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
51
51
|
action="store_true",
|
|
52
52
|
help="Less verbose output (no probe line, no summary).",
|
|
53
53
|
)
|
|
54
|
+
p.add_argument(
|
|
55
|
+
"-v",
|
|
56
|
+
"--show-attempts",
|
|
57
|
+
dest="show_attempts",
|
|
58
|
+
action="store_true",
|
|
59
|
+
help="Print each user:pass attempt with HTTP status or error.",
|
|
60
|
+
)
|
|
61
|
+
p.add_argument(
|
|
62
|
+
"-t",
|
|
63
|
+
"--threads",
|
|
64
|
+
type=int,
|
|
65
|
+
default=DEFAULT_THREADS,
|
|
66
|
+
help=f"Number of concurrent threads (default: {DEFAULT_THREADS}).",
|
|
67
|
+
)
|
|
54
68
|
return p
|
|
55
69
|
|
|
56
70
|
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
import sys
|
|
5
|
+
import threading
|
|
6
|
+
from argparse import Namespace
|
|
7
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Iterable
|
|
10
|
+
|
|
11
|
+
import requests
|
|
12
|
+
from requests.auth import HTTPBasicAuth
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
DEFAULT_TIMEOUT = 10.0
|
|
16
|
+
DEFAULT_THREADS = 10
|
|
17
|
+
|
|
18
|
+
_print_lock = threading.Lock()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _safe_print(msg: str) -> None:
|
|
22
|
+
with _print_lock:
|
|
23
|
+
print(msg, flush=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _proxy_dict(proxy: str | None) -> dict[str, str] | None:
|
|
27
|
+
if not proxy:
|
|
28
|
+
return None
|
|
29
|
+
return {"http": proxy, "https": proxy}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _read_lines(path: Path) -> list[str]:
|
|
33
|
+
text = path.read_text(encoding="utf-8", errors="replace")
|
|
34
|
+
return [line.strip() for line in text.splitlines() if line.strip()]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def check_basic_auth(
|
|
38
|
+
url: str,
|
|
39
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
40
|
+
proxies: dict[str, str] | None = None,
|
|
41
|
+
) -> bool:
|
|
42
|
+
try:
|
|
43
|
+
r = requests.get(url, timeout=timeout, allow_redirects=False, proxies=proxies)
|
|
44
|
+
except requests.RequestException:
|
|
45
|
+
return False
|
|
46
|
+
if r.status_code != 401:
|
|
47
|
+
return False
|
|
48
|
+
www_auth = r.headers.get("WWW-Authenticate") or ""
|
|
49
|
+
return "basic" in www_auth.lower()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def try_login(
|
|
53
|
+
url: str,
|
|
54
|
+
username: str,
|
|
55
|
+
password: str,
|
|
56
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
57
|
+
proxies: dict[str, str] | None = None,
|
|
58
|
+
) -> tuple[str, str, int | None, str | None]:
|
|
59
|
+
try:
|
|
60
|
+
r = requests.get(
|
|
61
|
+
url,
|
|
62
|
+
auth=HTTPBasicAuth(username, password),
|
|
63
|
+
timeout=timeout,
|
|
64
|
+
allow_redirects=False,
|
|
65
|
+
proxies=proxies,
|
|
66
|
+
)
|
|
67
|
+
return (username, password, r.status_code, None)
|
|
68
|
+
except requests.RequestException as e:
|
|
69
|
+
return (username, password, None, str(e))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _build_pairs(
|
|
73
|
+
usernames: Iterable[str],
|
|
74
|
+
passwords: Iterable[str],
|
|
75
|
+
mode: str,
|
|
76
|
+
) -> list[tuple[str, str]]:
|
|
77
|
+
u = list(usernames)
|
|
78
|
+
p = list(passwords)
|
|
79
|
+
if not u or not p:
|
|
80
|
+
return []
|
|
81
|
+
if mode == "pitchfork":
|
|
82
|
+
return list(zip(u, p))
|
|
83
|
+
return list(itertools.product(u, p))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _run_pairs(
|
|
87
|
+
url: str,
|
|
88
|
+
pairs: list[tuple[str, str]],
|
|
89
|
+
*,
|
|
90
|
+
timeout: float,
|
|
91
|
+
proxies: dict[str, str] | None,
|
|
92
|
+
threads: int,
|
|
93
|
+
show_attempts: bool,
|
|
94
|
+
verbose: bool,
|
|
95
|
+
) -> bool:
|
|
96
|
+
if not pairs:
|
|
97
|
+
_safe_print("[!] No credential pairs to test.")
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
total = len(pairs)
|
|
101
|
+
_safe_print(f"[*] Testing {total} credential pair(s) with {threads} thread(s)…\n")
|
|
102
|
+
|
|
103
|
+
found = False
|
|
104
|
+
done = 0
|
|
105
|
+
|
|
106
|
+
def _on_result(user: str, pw: str, code: int | None, err: str | None) -> None:
|
|
107
|
+
nonlocal found, done
|
|
108
|
+
done += 1
|
|
109
|
+
tag = f"[{done}/{total}]"
|
|
110
|
+
|
|
111
|
+
if code is None:
|
|
112
|
+
if show_attempts or verbose:
|
|
113
|
+
_safe_print(f"{tag} [-] {user}:{pw} → error ({err})")
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
if code == 200:
|
|
117
|
+
found = True
|
|
118
|
+
_safe_print(f"{tag} [+] {user}:{pw} → HTTP {code} ✓ SUCCESS")
|
|
119
|
+
elif show_attempts:
|
|
120
|
+
_safe_print(f"{tag} [-] {user}:{pw} → HTTP {code}")
|
|
121
|
+
|
|
122
|
+
with ThreadPoolExecutor(max_workers=threads) as pool:
|
|
123
|
+
futures = {
|
|
124
|
+
pool.submit(try_login, url, user, pw, timeout, proxies): (user, pw)
|
|
125
|
+
for user, pw in pairs
|
|
126
|
+
}
|
|
127
|
+
for future in as_completed(futures):
|
|
128
|
+
user, pw, code, err = future.result()
|
|
129
|
+
_on_result(user, pw, code, err)
|
|
130
|
+
|
|
131
|
+
return found
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def run_attack(args: Namespace) -> int:
|
|
135
|
+
url: str = args.url
|
|
136
|
+
timeout = float(args.timeout)
|
|
137
|
+
verbose = not args.quiet
|
|
138
|
+
show_attempts = bool(getattr(args, "show_attempts", False))
|
|
139
|
+
threads = int(getattr(args, "threads", DEFAULT_THREADS))
|
|
140
|
+
proxies = _proxy_dict(args.proxy)
|
|
141
|
+
|
|
142
|
+
uses_basic = check_basic_auth(url, timeout=timeout, proxies=proxies)
|
|
143
|
+
if verbose:
|
|
144
|
+
if uses_basic:
|
|
145
|
+
_safe_print(f"[*] Target appears to use HTTP Basic Auth: {url}")
|
|
146
|
+
else:
|
|
147
|
+
_safe_print(f"[!] Target may not require HTTP Basic Auth (or unreachable): {url}")
|
|
148
|
+
|
|
149
|
+
pairs: list[tuple[str, str]]
|
|
150
|
+
|
|
151
|
+
if args.userpass:
|
|
152
|
+
path = Path(args.userpass)
|
|
153
|
+
if not path.is_file():
|
|
154
|
+
_safe_print(f"[!] File not found: {path}")
|
|
155
|
+
return 2
|
|
156
|
+
lines = _read_lines(path)
|
|
157
|
+
combos: list[tuple[str, str]] = []
|
|
158
|
+
for line in lines:
|
|
159
|
+
if ":" not in line:
|
|
160
|
+
_safe_print(f"[!] Invalid line (expected user:pass): {line!r}")
|
|
161
|
+
return 2
|
|
162
|
+
user, _, rest = line.partition(":")
|
|
163
|
+
combos.append((user, rest))
|
|
164
|
+
pairs = _build_pairs(
|
|
165
|
+
[u for u, _ in combos],
|
|
166
|
+
[p for _, p in combos],
|
|
167
|
+
args.mode,
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
if not args.username or not args.password:
|
|
171
|
+
_safe_print("[!] Provide --userpass or both --username and --password files.")
|
|
172
|
+
return 2
|
|
173
|
+
up = Path(args.username)
|
|
174
|
+
pp = Path(args.password)
|
|
175
|
+
if not up.is_file() or not pp.is_file():
|
|
176
|
+
_safe_print("[!] Username or password file not found.")
|
|
177
|
+
return 2
|
|
178
|
+
users = _read_lines(up)
|
|
179
|
+
pws = _read_lines(pp)
|
|
180
|
+
pairs = _build_pairs(users, pws, args.mode)
|
|
181
|
+
|
|
182
|
+
found = _run_pairs(
|
|
183
|
+
url,
|
|
184
|
+
pairs,
|
|
185
|
+
timeout=timeout,
|
|
186
|
+
proxies=proxies,
|
|
187
|
+
threads=threads,
|
|
188
|
+
show_attempts=show_attempts,
|
|
189
|
+
verbose=verbose,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
if not found and verbose:
|
|
193
|
+
_safe_print("\n[*] No credentials returned HTTP 200.")
|
|
194
|
+
|
|
195
|
+
return 0 if found else 1
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: basicbuster
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: CLI tool for HTTP Basic Authentication testing with wordlists.
|
|
5
|
+
Author: dhina016
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dhina016/basicbuster
|
|
8
|
+
Project-URL: Repository, https://github.com/dhina016/basicbuster
|
|
9
|
+
Project-URL: Issues, https://github.com/dhina016/basicbuster/issues
|
|
10
|
+
Keywords: security,http,basic-auth,pentest,cli
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: requests>=2.28.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# basicbuster
|
|
32
|
+
|
|
33
|
+
**basicbuster** is a command-line tool for testing HTTP Basic Authentication with username and password wordlists. It detects Basic Auth on a target URL, runs pitchfork or clusterbomb-style attempts, and reports credentials that return HTTP `200`.
|
|
34
|
+
|
|
35
|
+
## Disclaimer
|
|
36
|
+
|
|
37
|
+
Use **only** on systems and applications you are **explicitly authorized** to test. Unauthorized access to computer systems is illegal. The authors are not responsible for misuse.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install basicbuster
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
basicbuster --url URL (--username FILE --password FILE | --userpass FILE) [OPTIONS]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Options
|
|
52
|
+
|
|
53
|
+
| Flag | Description |
|
|
54
|
+
|------|-------------|
|
|
55
|
+
| `--url` | Target URL protected by HTTP Basic Auth |
|
|
56
|
+
| `--username FILE` | Path to username wordlist (one per line) |
|
|
57
|
+
| `--password FILE` | Path to password wordlist (one per line) |
|
|
58
|
+
| `--userpass FILE` | Combined `user:pass` file (one per line) |
|
|
59
|
+
| `--mode` | `clusterbomb` (all combinations, default) or `pitchfork` (zip by index) |
|
|
60
|
+
| `-t, --threads` | Number of concurrent threads (default: 10) |
|
|
61
|
+
| `--timeout` | Per-request timeout in seconds (default: 10) |
|
|
62
|
+
| `--proxy URL` | Route traffic through a proxy (e.g. `http://127.0.0.1:8080`) |
|
|
63
|
+
| `-v, --show-attempts` | Print every attempt with HTTP status |
|
|
64
|
+
| `--quiet` | Minimal output |
|
|
65
|
+
|
|
66
|
+
### Examples
|
|
67
|
+
|
|
68
|
+
Clusterbomb (default — all username x password combinations):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt -v
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Pitchfork (pair line-by-line):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt --mode pitchfork -v
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Combined `user:pass` file:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
basicbuster --url https://example.com/secret --userpass combo.txt -v
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
With proxy and 20 threads:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
basicbuster --url https://example.com/secret --userpass combo.txt --proxy http://127.0.0.1:8080 -t 20 -v
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Output
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
[*] Target appears to use HTTP Basic Auth: https://example.com/secret
|
|
96
|
+
[*] Testing 400 credential pair(s) with 10 thread(s)…
|
|
97
|
+
|
|
98
|
+
[1/400] [-] admin:password → HTTP 401
|
|
99
|
+
[2/400] [-] admin:123456 → HTTP 401
|
|
100
|
+
[3/400] [+] admin:secret → HTTP 200 ✓ SUCCESS
|
|
101
|
+
...
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Sample wordlists (20 common entries each) are included in [`samples/`](samples/).
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT — see [LICENSE](LICENSE).
|
basicbuster-0.1.0/PKG-INFO
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: basicbuster
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: CLI tool for HTTP Basic Authentication testing with wordlists.
|
|
5
|
-
Author: dhina016
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/dhina016/basicbuster
|
|
8
|
-
Project-URL: Repository, https://github.com/dhina016/basicbuster
|
|
9
|
-
Project-URL: Issues, https://github.com/dhina016/basicbuster/issues
|
|
10
|
-
Keywords: security,http,basic-auth,pentest,cli
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: Intended Audience :: Information Technology
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
-
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
-
Classifier: Topic :: Security
|
|
22
|
-
Requires-Python: >=3.10
|
|
23
|
-
Description-Content-Type: text/markdown
|
|
24
|
-
License-File: LICENSE
|
|
25
|
-
Requires-Dist: requests>=2.28.0
|
|
26
|
-
Provides-Extra: dev
|
|
27
|
-
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
28
|
-
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
29
|
-
Dynamic: license-file
|
|
30
|
-
|
|
31
|
-
# basicbuster
|
|
32
|
-
|
|
33
|
-
**basicbuster** is a command-line tool for testing HTTP Basic Authentication with username and password wordlists. It can detect Basic Auth on a target URL, run pitchfork or clusterbomb-style attempts, and report credentials that return HTTP `200`.
|
|
34
|
-
|
|
35
|
-
## Disclaimer
|
|
36
|
-
|
|
37
|
-
Use **only** on systems and applications you are **explicitly authorized** to test. Unauthorized access to computer systems is illegal. The authors are not responsible for misuse.
|
|
38
|
-
|
|
39
|
-
## Requirements
|
|
40
|
-
|
|
41
|
-
- Python 3.10+
|
|
42
|
-
|
|
43
|
-
## Installation
|
|
44
|
-
|
|
45
|
-
After the package is [published on PyPI](https://pypi.org/project/basicbuster/), install from any machine:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
pip install basicbuster
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
From a git checkout (repository root):
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
pip install .
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Editable install while developing:
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
pip install -e .
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Publishing to PyPI (maintainers)
|
|
64
|
-
|
|
65
|
-
The name **basicbuster** is available on PyPI. Two common options:
|
|
66
|
-
|
|
67
|
-
**A — GitHub Release (recommended)**
|
|
68
|
-
After you [enable trusted publishing](https://docs.pypi.org/trusted-publishers/) for this repo on PyPI:
|
|
69
|
-
|
|
70
|
-
1. In PyPI: your account → **Publishing** → add a pending publisher for `dhina016/basicbuster`, workflow file `publish.yml`.
|
|
71
|
-
2. On GitHub: create a **Release** (tag `v0.1.0` or similar) and publish it. The **Publish to PyPI** workflow uploads the built sdist and wheel automatically.
|
|
72
|
-
|
|
73
|
-
**B — Manual upload**
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
pip install build twine
|
|
77
|
-
python -m build
|
|
78
|
-
twine upload dist/*
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Use a [PyPI API token](https://pypi.org/help/#apitoken) when `twine` prompts for credentials (or set `TWINE_USERNAME=__token__` and `TWINE_PASSWORD=pypi-...`).
|
|
82
|
-
|
|
83
|
-
## Usage
|
|
84
|
-
|
|
85
|
-
```text
|
|
86
|
-
basicbuster --url URL (--username FILE --password FILE | --userpass FILE) [--mode MODE] [--timeout SECONDS] [--proxy URL]
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
- **`--url`**: Target URL (resource protected by HTTP Basic Auth).
|
|
90
|
-
- **`--username` / `--password`**: Separate wordlists, one value per line.
|
|
91
|
-
- **`--userpass`**: Combined file with `user:pass` per line (password may contain `:`; only the first `:` separates user and password).
|
|
92
|
-
- **`--mode`**:
|
|
93
|
-
- `pitchfork` — pair `username[i]` with `password[i]`.
|
|
94
|
-
- `clusterbomb` — try every username with every password (default).
|
|
95
|
-
- **`--timeout`**: Request timeout in seconds (default: `15`).
|
|
96
|
-
- **`--quiet`**: Minimal output.
|
|
97
|
-
- **`--proxy`**: Forward all traffic through an HTTP(S) proxy (e.g. Burp: `http://127.0.0.1:8080`).
|
|
98
|
-
|
|
99
|
-
Successful guesses print as:
|
|
100
|
-
|
|
101
|
-
```text
|
|
102
|
-
[+] SUCCESS user:pass (HTTP 200)
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Examples
|
|
106
|
-
|
|
107
|
-
Separate wordlists, clusterbomb (default):
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Pitchfork (zip by line index):
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt --mode pitchfork
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Combined user:pass file:
|
|
120
|
-
|
|
121
|
-
```bash
|
|
122
|
-
basicbuster --url https://example.com/secret --userpass combo.txt
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Through a local intercepting proxy:
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
basicbuster --url https://example.com/secret --userpass combo.txt --proxy http://127.0.0.1:8080
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## License
|
|
132
|
-
|
|
133
|
-
MIT — see [LICENSE](LICENSE).
|
basicbuster-0.1.0/README.md
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# basicbuster
|
|
2
|
-
|
|
3
|
-
**basicbuster** is a command-line tool for testing HTTP Basic Authentication with username and password wordlists. It can detect Basic Auth on a target URL, run pitchfork or clusterbomb-style attempts, and report credentials that return HTTP `200`.
|
|
4
|
-
|
|
5
|
-
## Disclaimer
|
|
6
|
-
|
|
7
|
-
Use **only** on systems and applications you are **explicitly authorized** to test. Unauthorized access to computer systems is illegal. The authors are not responsible for misuse.
|
|
8
|
-
|
|
9
|
-
## Requirements
|
|
10
|
-
|
|
11
|
-
- Python 3.10+
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
After the package is [published on PyPI](https://pypi.org/project/basicbuster/), install from any machine:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
pip install basicbuster
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
From a git checkout (repository root):
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
pip install .
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Editable install while developing:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
pip install -e .
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Publishing to PyPI (maintainers)
|
|
34
|
-
|
|
35
|
-
The name **basicbuster** is available on PyPI. Two common options:
|
|
36
|
-
|
|
37
|
-
**A — GitHub Release (recommended)**
|
|
38
|
-
After you [enable trusted publishing](https://docs.pypi.org/trusted-publishers/) for this repo on PyPI:
|
|
39
|
-
|
|
40
|
-
1. In PyPI: your account → **Publishing** → add a pending publisher for `dhina016/basicbuster`, workflow file `publish.yml`.
|
|
41
|
-
2. On GitHub: create a **Release** (tag `v0.1.0` or similar) and publish it. The **Publish to PyPI** workflow uploads the built sdist and wheel automatically.
|
|
42
|
-
|
|
43
|
-
**B — Manual upload**
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
pip install build twine
|
|
47
|
-
python -m build
|
|
48
|
-
twine upload dist/*
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Use a [PyPI API token](https://pypi.org/help/#apitoken) when `twine` prompts for credentials (or set `TWINE_USERNAME=__token__` and `TWINE_PASSWORD=pypi-...`).
|
|
52
|
-
|
|
53
|
-
## Usage
|
|
54
|
-
|
|
55
|
-
```text
|
|
56
|
-
basicbuster --url URL (--username FILE --password FILE | --userpass FILE) [--mode MODE] [--timeout SECONDS] [--proxy URL]
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
- **`--url`**: Target URL (resource protected by HTTP Basic Auth).
|
|
60
|
-
- **`--username` / `--password`**: Separate wordlists, one value per line.
|
|
61
|
-
- **`--userpass`**: Combined file with `user:pass` per line (password may contain `:`; only the first `:` separates user and password).
|
|
62
|
-
- **`--mode`**:
|
|
63
|
-
- `pitchfork` — pair `username[i]` with `password[i]`.
|
|
64
|
-
- `clusterbomb` — try every username with every password (default).
|
|
65
|
-
- **`--timeout`**: Request timeout in seconds (default: `15`).
|
|
66
|
-
- **`--quiet`**: Minimal output.
|
|
67
|
-
- **`--proxy`**: Forward all traffic through an HTTP(S) proxy (e.g. Burp: `http://127.0.0.1:8080`).
|
|
68
|
-
|
|
69
|
-
Successful guesses print as:
|
|
70
|
-
|
|
71
|
-
```text
|
|
72
|
-
[+] SUCCESS user:pass (HTTP 200)
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Examples
|
|
76
|
-
|
|
77
|
-
Separate wordlists, clusterbomb (default):
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Pitchfork (zip by line index):
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt --mode pitchfork
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Combined user:pass file:
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
basicbuster --url https://example.com/secret --userpass combo.txt
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Through a local intercepting proxy:
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
basicbuster --url https://example.com/secret --userpass combo.txt --proxy http://127.0.0.1:8080
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## License
|
|
102
|
-
|
|
103
|
-
MIT — see [LICENSE](LICENSE).
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import itertools
|
|
4
|
-
from argparse import Namespace
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Iterable
|
|
7
|
-
|
|
8
|
-
import requests
|
|
9
|
-
from requests import Response
|
|
10
|
-
from requests.auth import HTTPBasicAuth
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
DEFAULT_TIMEOUT = 15.0
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def _proxy_dict(proxy: str | None) -> dict[str, str] | None:
|
|
17
|
-
if not proxy:
|
|
18
|
-
return None
|
|
19
|
-
return {"http": proxy, "https": proxy}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _read_lines(path: Path) -> list[str]:
|
|
23
|
-
text = path.read_text(encoding="utf-8", errors="replace")
|
|
24
|
-
return [line.strip() for line in text.splitlines() if line.strip()]
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def check_basic_auth(
|
|
28
|
-
url: str,
|
|
29
|
-
timeout: float = DEFAULT_TIMEOUT,
|
|
30
|
-
proxies: dict[str, str] | None = None,
|
|
31
|
-
) -> bool:
|
|
32
|
-
try:
|
|
33
|
-
r = requests.get(
|
|
34
|
-
url,
|
|
35
|
-
timeout=timeout,
|
|
36
|
-
allow_redirects=False,
|
|
37
|
-
proxies=proxies,
|
|
38
|
-
)
|
|
39
|
-
except requests.RequestException:
|
|
40
|
-
return False
|
|
41
|
-
if r.status_code != 401:
|
|
42
|
-
return False
|
|
43
|
-
www_auth = r.headers.get("WWW-Authenticate") or ""
|
|
44
|
-
return "basic" in www_auth.lower()
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def try_login(
|
|
48
|
-
url: str,
|
|
49
|
-
username: str,
|
|
50
|
-
password: str,
|
|
51
|
-
timeout: float = DEFAULT_TIMEOUT,
|
|
52
|
-
proxies: dict[str, str] | None = None,
|
|
53
|
-
) -> requests.Response:
|
|
54
|
-
return requests.get(
|
|
55
|
-
url,
|
|
56
|
-
auth=HTTPBasicAuth(username, password),
|
|
57
|
-
timeout=timeout,
|
|
58
|
-
allow_redirects=False,
|
|
59
|
-
proxies=proxies,
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def pitchfork(
|
|
64
|
-
url: str,
|
|
65
|
-
usernames: Iterable[str],
|
|
66
|
-
passwords: Iterable[str],
|
|
67
|
-
timeout: float = DEFAULT_TIMEOUT,
|
|
68
|
-
proxies: dict[str, str] | None = None,
|
|
69
|
-
) -> list[tuple[str, str, Response | None, str | None]]:
|
|
70
|
-
u = list(usernames)
|
|
71
|
-
p = list(passwords)
|
|
72
|
-
if not u or not p:
|
|
73
|
-
return []
|
|
74
|
-
n = min(len(u), len(p))
|
|
75
|
-
results: list[tuple[str, str, Response | None, str | None]] = []
|
|
76
|
-
for i in range(n):
|
|
77
|
-
try:
|
|
78
|
-
resp = try_login(url, u[i], p[i], timeout=timeout, proxies=proxies)
|
|
79
|
-
results.append((u[i], p[i], resp, None))
|
|
80
|
-
except requests.RequestException as e:
|
|
81
|
-
results.append((u[i], p[i], None, str(e)))
|
|
82
|
-
return results
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def clusterbomb(
|
|
86
|
-
url: str,
|
|
87
|
-
usernames: Iterable[str],
|
|
88
|
-
passwords: Iterable[str],
|
|
89
|
-
timeout: float = DEFAULT_TIMEOUT,
|
|
90
|
-
proxies: dict[str, str] | None = None,
|
|
91
|
-
) -> list[tuple[str, str, Response | None, str | None]]:
|
|
92
|
-
results: list[tuple[str, str, Response | None, str | None]] = []
|
|
93
|
-
for user, pw in itertools.product(usernames, passwords):
|
|
94
|
-
try:
|
|
95
|
-
resp = try_login(url, user, pw, timeout=timeout, proxies=proxies)
|
|
96
|
-
results.append((user, pw, resp, None))
|
|
97
|
-
except requests.RequestException as e:
|
|
98
|
-
results.append((user, pw, None, str(e)))
|
|
99
|
-
return results
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def userpass_mode(
|
|
103
|
-
url: str,
|
|
104
|
-
combos: Iterable[tuple[str, str]],
|
|
105
|
-
mode: str,
|
|
106
|
-
timeout: float = DEFAULT_TIMEOUT,
|
|
107
|
-
proxies: dict[str, str] | None = None,
|
|
108
|
-
) -> list[tuple[str, str, Response | None, str | None]]:
|
|
109
|
-
pairs = list(combos)
|
|
110
|
-
if not pairs:
|
|
111
|
-
return []
|
|
112
|
-
if mode == "pitchfork":
|
|
113
|
-
users = [u for u, _ in pairs]
|
|
114
|
-
pws = [p for _, p in pairs]
|
|
115
|
-
return pitchfork(url, users, pws, timeout=timeout, proxies=proxies)
|
|
116
|
-
users = [u for u, _ in pairs]
|
|
117
|
-
pws = [p for _, p in pairs]
|
|
118
|
-
return clusterbomb(url, users, pws, timeout=timeout, proxies=proxies)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def _emit_success(user: str, pw: str, status: int) -> None:
|
|
122
|
-
print(f"[+] SUCCESS {user}:{pw} (HTTP {status})")
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def _emit_probe(ok: bool, url: str) -> None:
|
|
126
|
-
if ok:
|
|
127
|
-
print(f"[*] Target appears to use HTTP Basic Auth: {url}")
|
|
128
|
-
else:
|
|
129
|
-
print(f"[!] Target may not require HTTP Basic Auth (or unreachable): {url}")
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def run_attack(args: Namespace) -> int:
|
|
133
|
-
url = args.url
|
|
134
|
-
timeout = float(args.timeout)
|
|
135
|
-
verbose = not args.quiet
|
|
136
|
-
proxies = _proxy_dict(args.proxy)
|
|
137
|
-
|
|
138
|
-
uses_basic = check_basic_auth(url, timeout=timeout, proxies=proxies)
|
|
139
|
-
if verbose:
|
|
140
|
-
_emit_probe(uses_basic, url)
|
|
141
|
-
|
|
142
|
-
results: list[tuple[str, str, Response | None, str | None]]
|
|
143
|
-
|
|
144
|
-
if args.userpass:
|
|
145
|
-
path = Path(args.userpass)
|
|
146
|
-
if not path.is_file():
|
|
147
|
-
print(f"[!] File not found: {path}")
|
|
148
|
-
return 2
|
|
149
|
-
lines = _read_lines(path)
|
|
150
|
-
combos: list[tuple[str, str]] = []
|
|
151
|
-
for line in lines:
|
|
152
|
-
if ":" not in line:
|
|
153
|
-
print(f"[!] Invalid line (expected user:pass): {line!r}")
|
|
154
|
-
return 2
|
|
155
|
-
user, _, rest = line.partition(":")
|
|
156
|
-
combos.append((user, rest))
|
|
157
|
-
results = userpass_mode(
|
|
158
|
-
url, combos, args.mode, timeout=timeout, proxies=proxies
|
|
159
|
-
)
|
|
160
|
-
else:
|
|
161
|
-
if not args.username or not args.password:
|
|
162
|
-
print("[!] Provide --userpass or both --username and --password files.")
|
|
163
|
-
return 2
|
|
164
|
-
up = Path(args.username)
|
|
165
|
-
pp = Path(args.password)
|
|
166
|
-
if not up.is_file() or not pp.is_file():
|
|
167
|
-
print("[!] Username or password file not found.")
|
|
168
|
-
return 2
|
|
169
|
-
users = _read_lines(up)
|
|
170
|
-
pws = _read_lines(pp)
|
|
171
|
-
if args.mode == "pitchfork":
|
|
172
|
-
results = pitchfork(url, users, pws, timeout=timeout, proxies=proxies)
|
|
173
|
-
else:
|
|
174
|
-
results = clusterbomb(
|
|
175
|
-
url, users, pws, timeout=timeout, proxies=proxies
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
found = False
|
|
179
|
-
for user, pw, resp, err in results:
|
|
180
|
-
if resp is None:
|
|
181
|
-
if verbose and err:
|
|
182
|
-
print(f"[-] request failed for {user}:{pw} — {err}")
|
|
183
|
-
continue
|
|
184
|
-
if resp.status_code == 200:
|
|
185
|
-
_emit_success(user, pw, resp.status_code)
|
|
186
|
-
found = True
|
|
187
|
-
|
|
188
|
-
if not found and verbose:
|
|
189
|
-
print("[*] No credentials returned HTTP 200.")
|
|
190
|
-
|
|
191
|
-
return 0 if found else 1
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: basicbuster
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: CLI tool for HTTP Basic Authentication testing with wordlists.
|
|
5
|
-
Author: dhina016
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/dhina016/basicbuster
|
|
8
|
-
Project-URL: Repository, https://github.com/dhina016/basicbuster
|
|
9
|
-
Project-URL: Issues, https://github.com/dhina016/basicbuster/issues
|
|
10
|
-
Keywords: security,http,basic-auth,pentest,cli
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: Intended Audience :: Information Technology
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
-
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
-
Classifier: Topic :: Security
|
|
22
|
-
Requires-Python: >=3.10
|
|
23
|
-
Description-Content-Type: text/markdown
|
|
24
|
-
License-File: LICENSE
|
|
25
|
-
Requires-Dist: requests>=2.28.0
|
|
26
|
-
Provides-Extra: dev
|
|
27
|
-
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
28
|
-
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
29
|
-
Dynamic: license-file
|
|
30
|
-
|
|
31
|
-
# basicbuster
|
|
32
|
-
|
|
33
|
-
**basicbuster** is a command-line tool for testing HTTP Basic Authentication with username and password wordlists. It can detect Basic Auth on a target URL, run pitchfork or clusterbomb-style attempts, and report credentials that return HTTP `200`.
|
|
34
|
-
|
|
35
|
-
## Disclaimer
|
|
36
|
-
|
|
37
|
-
Use **only** on systems and applications you are **explicitly authorized** to test. Unauthorized access to computer systems is illegal. The authors are not responsible for misuse.
|
|
38
|
-
|
|
39
|
-
## Requirements
|
|
40
|
-
|
|
41
|
-
- Python 3.10+
|
|
42
|
-
|
|
43
|
-
## Installation
|
|
44
|
-
|
|
45
|
-
After the package is [published on PyPI](https://pypi.org/project/basicbuster/), install from any machine:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
pip install basicbuster
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
From a git checkout (repository root):
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
pip install .
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Editable install while developing:
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
pip install -e .
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Publishing to PyPI (maintainers)
|
|
64
|
-
|
|
65
|
-
The name **basicbuster** is available on PyPI. Two common options:
|
|
66
|
-
|
|
67
|
-
**A — GitHub Release (recommended)**
|
|
68
|
-
After you [enable trusted publishing](https://docs.pypi.org/trusted-publishers/) for this repo on PyPI:
|
|
69
|
-
|
|
70
|
-
1. In PyPI: your account → **Publishing** → add a pending publisher for `dhina016/basicbuster`, workflow file `publish.yml`.
|
|
71
|
-
2. On GitHub: create a **Release** (tag `v0.1.0` or similar) and publish it. The **Publish to PyPI** workflow uploads the built sdist and wheel automatically.
|
|
72
|
-
|
|
73
|
-
**B — Manual upload**
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
pip install build twine
|
|
77
|
-
python -m build
|
|
78
|
-
twine upload dist/*
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Use a [PyPI API token](https://pypi.org/help/#apitoken) when `twine` prompts for credentials (or set `TWINE_USERNAME=__token__` and `TWINE_PASSWORD=pypi-...`).
|
|
82
|
-
|
|
83
|
-
## Usage
|
|
84
|
-
|
|
85
|
-
```text
|
|
86
|
-
basicbuster --url URL (--username FILE --password FILE | --userpass FILE) [--mode MODE] [--timeout SECONDS] [--proxy URL]
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
- **`--url`**: Target URL (resource protected by HTTP Basic Auth).
|
|
90
|
-
- **`--username` / `--password`**: Separate wordlists, one value per line.
|
|
91
|
-
- **`--userpass`**: Combined file with `user:pass` per line (password may contain `:`; only the first `:` separates user and password).
|
|
92
|
-
- **`--mode`**:
|
|
93
|
-
- `pitchfork` — pair `username[i]` with `password[i]`.
|
|
94
|
-
- `clusterbomb` — try every username with every password (default).
|
|
95
|
-
- **`--timeout`**: Request timeout in seconds (default: `15`).
|
|
96
|
-
- **`--quiet`**: Minimal output.
|
|
97
|
-
- **`--proxy`**: Forward all traffic through an HTTP(S) proxy (e.g. Burp: `http://127.0.0.1:8080`).
|
|
98
|
-
|
|
99
|
-
Successful guesses print as:
|
|
100
|
-
|
|
101
|
-
```text
|
|
102
|
-
[+] SUCCESS user:pass (HTTP 200)
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Examples
|
|
106
|
-
|
|
107
|
-
Separate wordlists, clusterbomb (default):
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Pitchfork (zip by line index):
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
basicbuster --url https://example.com/secret --username users.txt --password passwords.txt --mode pitchfork
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Combined user:pass file:
|
|
120
|
-
|
|
121
|
-
```bash
|
|
122
|
-
basicbuster --url https://example.com/secret --userpass combo.txt
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Through a local intercepting proxy:
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
basicbuster --url https://example.com/secret --userpass combo.txt --proxy http://127.0.0.1:8080
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## License
|
|
132
|
-
|
|
133
|
-
MIT — see [LICENSE](LICENSE).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|