gyntoolkit 2.1.1__tar.gz → 2.3.0__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.
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/PKG-INFO +75 -9
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/README.md +74 -8
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/__init__.py +22 -5
- gyntoolkit-2.3.0/gyntoolkit/audit.py +162 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/brute.py +6 -2
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/cli.py +154 -6
- gyntoolkit-2.3.0/gyntoolkit/commands.py +358 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/config.py +2 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/core.py +1 -1
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/export.py +90 -1
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/i18n.py +79 -3
- gyntoolkit-2.3.0/gyntoolkit/mailsec.py +159 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/recon.py +101 -0
- gyntoolkit-2.3.0/gyntoolkit/scan.py +480 -0
- gyntoolkit-2.3.0/gyntoolkit/web.py +125 -0
- gyntoolkit-2.3.0/gyntoolkit/wordlist.py +103 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit.egg-info/PKG-INFO +75 -9
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit.egg-info/SOURCES.txt +15 -1
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/pyproject.toml +1 -1
- gyntoolkit-2.3.0/tests/test_audit.py +94 -0
- gyntoolkit-2.3.0/tests/test_brute.py +30 -0
- gyntoolkit-2.3.0/tests/test_commands.py +56 -0
- gyntoolkit-2.3.0/tests/test_cve_enrich.py +116 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/tests/test_export.py +35 -0
- gyntoolkit-2.3.0/tests/test_mailsec.py +109 -0
- gyntoolkit-2.3.0/tests/test_scan_parse.py +47 -0
- gyntoolkit-2.3.0/tests/test_shodan.py +44 -0
- gyntoolkit-2.3.0/tests/test_web.py +67 -0
- gyntoolkit-2.3.0/tests/test_wordlist.py +69 -0
- gyntoolkit-2.1.1/gyntoolkit/scan.py +0 -150
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/LICENSE +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/__main__.py +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/ui.py +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit/utils.py +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit.egg-info/dependency_links.txt +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit.egg-info/entry_points.txt +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit.egg-info/requires.txt +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/gyntoolkit.egg-info/top_level.txt +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/setup.cfg +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/tests/test_config.py +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/tests/test_i18n.py +0 -0
- {gyntoolkit-2.1.1 → gyntoolkit-2.3.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gyntoolkit
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Canivete suíço de pentest — recon, port scan, CVE lookup e brute force.
|
|
5
5
|
Author: PH, Fl4vs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -49,7 +49,7 @@ Dynamic: license-file
|
|
|
49
49
|
██████╗██╗ ██╗███╗ ██╗ ████████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗██╗████████╗
|
|
50
50
|
██╔════╝╚██╗ ██╔╝████╗ ██║ ╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██║ ██╔╝██║╚══██╔══╝
|
|
51
51
|
██║ ███╗╚████╔╝ ██╔██╗ ██║ ██║ ██║ ██║██║ ██║██║ █████╔╝ ██║ ██║
|
|
52
|
-
██║ ██║ ╚██╔╝ ██║╚██╗██║ ██║ ██║ ██║██║ ██║██║ ██╔═██╗ ██║ ██║ v2.
|
|
52
|
+
██║ ██║ ╚██╔╝ ██║╚██╗██║ ██║ ██║ ██║██║ ██║██║ ██╔═██╗ ██║ ██║ v2.3
|
|
53
53
|
╚██████╔╝ ██║ ██║ ╚████║ ██║ ╚██████╔╝╚██████╔╝███████╗██║ ██╗██║ ██║ by: PH,Fl4vs
|
|
54
54
|
╚═════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
55
55
|
```
|
|
@@ -124,23 +124,34 @@ Dynamic: license-file
|
|
|
124
124
|
| 9 | **HIBP Breach Check** | Domain → known breaches via Have I Been Pwned |
|
|
125
125
|
| 10 | **MAC Vendor Lookup** | `api.macvendors.com` — OUI → vendor |
|
|
126
126
|
| 11 | **Traceroute (TCP)** | `scapy` — hops + RTT (needs privilege) |
|
|
127
|
+
| 12 | **DNS Zone Transfer (AXFR)** | `dnspython` — tries AXFR against each authoritative NS |
|
|
128
|
+
| 13 | **Web Content Discovery** | `aiohttp` — robots.txt / sitemap / security.txt + built-in path wordlist |
|
|
129
|
+
| 14 | **Shodan Host** | Full `api.shodan.io` host lookup (ports, CPEs, CVEs, tags) — needs `api_keys.shodan`; falls back to InternetDB hint without a key |
|
|
130
|
+
| 15 | **Email Security** | `dnspython` — SPF, DKIM, DMARC, DNSSEC and CAA analyzer with per-item verdict (`ok`/`weak`/`missing`) |
|
|
127
131
|
|
|
128
132
|
### `[2]` Brute Force
|
|
129
133
|
|
|
130
134
|
| # | Module | Details |
|
|
131
135
|
|---|--------|---------|
|
|
132
|
-
| 1 | **
|
|
133
|
-
| 2 | **
|
|
134
|
-
| 3 | **
|
|
136
|
+
| 1 | **Native wordlist** | Built-in CUPP-style generator (offline, no external clone) — case/leet variants, years, suffixes, term combos |
|
|
137
|
+
| 2 | **CUPP wordlist** | Optional wrapper for `cupp.py -i` |
|
|
138
|
+
| 3 | **SSH brute** | `paramiko`, async via `asyncio.to_thread`, concurrency `Semaphore`, delay |
|
|
139
|
+
| 4 | **HTTP brute** | `aiohttp` — Basic Auth or form POST with configurable `fail_signature` |
|
|
135
140
|
|
|
136
|
-
> Every attack requires typing
|
|
141
|
+
> Every attack requires typing the authorization word (`AUTHORIZE` / `AUTORIZO`)
|
|
142
|
+
> to confirm — no silent bypass.
|
|
137
143
|
|
|
138
144
|
### `[3]` Advanced Scanning
|
|
139
145
|
|
|
140
146
|
- **Port scan** — fast (Top 21 common ports) or full (1–65535)
|
|
141
147
|
- **SYN scan** (stealth) when privileged, automatic fallback to **TCP connect**
|
|
142
148
|
- **Banner grabbing** — async, parallel
|
|
143
|
-
- **CVE analysis** per service via **NVD API v2.0**
|
|
149
|
+
- **CVE analysis** per service via **NVD API v2.0** — parses product + version
|
|
150
|
+
from the banner and matches by **CPE** (`virtualMatchString`), falling back to
|
|
151
|
+
keyword search; rate-limited and deduplicated per banner
|
|
152
|
+
- **Risk scoring** — each CVE is enriched with **CVSS** base score/severity,
|
|
153
|
+
**EPSS** exploitation probability and a **CISA KEV** flag; host risk is the
|
|
154
|
+
highest severity found (KEV forces critical). Filter with `--min-cvss` / `--kev-only`
|
|
144
155
|
- **Host discovery** (ARP scan) by CIDR — e.g. `192.168.0.0/24`
|
|
145
156
|
- Automatic risk classification based on CVEs found
|
|
146
157
|
|
|
@@ -154,6 +165,12 @@ Dynamic: license-file
|
|
|
154
165
|
| 4 | **Base64 decode** | base64 → UTF-8 with auto-padding |
|
|
155
166
|
| 5 | **JWT decode** | Header + payload without signature verification |
|
|
156
167
|
|
|
168
|
+
### `[5]` Audit — one target, consolidated profile
|
|
169
|
+
|
|
170
|
+
Orchestrates the recon modules (and, with `--active --authorize`, the port scan)
|
|
171
|
+
into a **single report** with an executive summary. Passive by default; stage
|
|
172
|
+
selection via `--only` / `--skip`. See the [CLI examples](#non-interactive-cli-scriptable).
|
|
173
|
+
|
|
157
174
|
---
|
|
158
175
|
|
|
159
176
|
## Installation
|
|
@@ -203,6 +220,55 @@ An interactive dark CLI opens with a numbered menu. Commands are entered at the
|
|
|
203
220
|
|
|
204
221
|
---
|
|
205
222
|
|
|
223
|
+
## Non-interactive CLI (scriptable)
|
|
224
|
+
|
|
225
|
+
Pass a subcommand to run a single action and print the result as JSON on stdout
|
|
226
|
+
(pipe-friendly) — no menu. Omit the subcommand to open the interactive menu.
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
gyntoolkit recon dns example.com --type MX # DNS lookup
|
|
230
|
+
gyntoolkit recon axfr example.com # zone transfer attempt
|
|
231
|
+
gyntoolkit recon webscan http://example.com # web content discovery
|
|
232
|
+
gyntoolkit recon shodan 1.1.1.1 # Shodan host (needs api_keys.shodan)
|
|
233
|
+
gyntoolkit recon ssl example.com --port 443 # TLS cert
|
|
234
|
+
gyntoolkit scan 127.0.0.1 --type fast # port scan + CVEs (CVSS/EPSS/KEV)
|
|
235
|
+
gyntoolkit scan 10.0.0.5 --min-cvss 7.0 --kev-only # only high-risk / actively exploited
|
|
236
|
+
gyntoolkit scan 192.168.0.0/24 # host discovery (CIDR)
|
|
237
|
+
gyntoolkit audit example.com # full passive profile → one report
|
|
238
|
+
gyntoolkit audit 10.0.0.5 --active --authorize # + active port scan (authorized)
|
|
239
|
+
gyntoolkit utils hash "text" --algo sha1
|
|
240
|
+
gyntoolkit utils jwt <token>
|
|
241
|
+
gyntoolkit wordlist --terms alice,fluffy,acme --years 1990,2020 --leet --save wl.txt
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`audit` runs a pipeline over one target and consolidates everything into a single
|
|
245
|
+
report with an executive summary. It is **passive by default** (WHOIS, DNS, geo,
|
|
246
|
+
subdomains, TLS, HTTP fingerprint, InternetDB, HIBP, email security); the active
|
|
247
|
+
port scan only runs with `--active --authorize`. Narrow it with `--only a,b` or
|
|
248
|
+
`--skip a,b`.
|
|
249
|
+
|
|
250
|
+
Global flags (place after the subcommand): `-o/--output DIR` saves a report,
|
|
251
|
+
`-f/--format {json,html,csv,md}` picks its format, `-q/--quiet` silences stdout,
|
|
252
|
+
`--lang {en,pt}` overrides the language.
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
gyntoolkit scan scanme.example.com -o reports -f md # also save a Markdown report
|
|
256
|
+
gyntoolkit recon geo 1.1.1.1 -o reports -f csv -q # CSV only, no stdout
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Active attacks are available but require explicit authorization:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
gyntoolkit brute ssh HOST --users users.txt --passwords pass.txt --authorize
|
|
263
|
+
gyntoolkit brute http URL --users admin --passwords pass.txt --mode form \
|
|
264
|
+
--user-field user --pass-field pass --fail-signature "Invalid" --authorize
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Without `--authorize`, brute subcommands abort — you confirm you have **written
|
|
268
|
+
authorization** for the target.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
206
272
|
## Example
|
|
207
273
|
|
|
208
274
|
Target: the **local security lab** (never a third-party system).
|
|
@@ -256,8 +322,8 @@ Full walkthrough: [`docs/security-lab.md`](docs/security-lab.md) and
|
|
|
256
322
|
|
|
257
323
|
## Reports
|
|
258
324
|
|
|
259
|
-
After scans and recon lookups, the CLI offers to export results as **JSON
|
|
260
|
-
dark-themed **HTML report**. Files are written to
|
|
325
|
+
After scans and recon lookups, the CLI offers to export results as **JSON**, a
|
|
326
|
+
dark-themed **HTML report**, **CSV** or **Markdown**. Files are written to
|
|
261
327
|
`reports/<module>-<timestamp>.<fmt>` (configurable via `export.dir`; set
|
|
262
328
|
`export.auto: true` to export without prompting). Details in
|
|
263
329
|
[`docs/reports.md`](docs/reports.md).
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
██████╗██╗ ██╗███╗ ██╗ ████████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗██╗████████╗
|
|
7
7
|
██╔════╝╚██╗ ██╔╝████╗ ██║ ╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██║ ██╔╝██║╚══██╔══╝
|
|
8
8
|
██║ ███╗╚████╔╝ ██╔██╗ ██║ ██║ ██║ ██║██║ ██║██║ █████╔╝ ██║ ██║
|
|
9
|
-
██║ ██║ ╚██╔╝ ██║╚██╗██║ ██║ ██║ ██║██║ ██║██║ ██╔═██╗ ██║ ██║ v2.
|
|
9
|
+
██║ ██║ ╚██╔╝ ██║╚██╗██║ ██║ ██║ ██║██║ ██║██║ ██╔═██╗ ██║ ██║ v2.3
|
|
10
10
|
╚██████╔╝ ██║ ██║ ╚████║ ██║ ╚██████╔╝╚██████╔╝███████╗██║ ██╗██║ ██║ by: PH,Fl4vs
|
|
11
11
|
╚═════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
12
12
|
```
|
|
@@ -81,23 +81,34 @@
|
|
|
81
81
|
| 9 | **HIBP Breach Check** | Domain → known breaches via Have I Been Pwned |
|
|
82
82
|
| 10 | **MAC Vendor Lookup** | `api.macvendors.com` — OUI → vendor |
|
|
83
83
|
| 11 | **Traceroute (TCP)** | `scapy` — hops + RTT (needs privilege) |
|
|
84
|
+
| 12 | **DNS Zone Transfer (AXFR)** | `dnspython` — tries AXFR against each authoritative NS |
|
|
85
|
+
| 13 | **Web Content Discovery** | `aiohttp` — robots.txt / sitemap / security.txt + built-in path wordlist |
|
|
86
|
+
| 14 | **Shodan Host** | Full `api.shodan.io` host lookup (ports, CPEs, CVEs, tags) — needs `api_keys.shodan`; falls back to InternetDB hint without a key |
|
|
87
|
+
| 15 | **Email Security** | `dnspython` — SPF, DKIM, DMARC, DNSSEC and CAA analyzer with per-item verdict (`ok`/`weak`/`missing`) |
|
|
84
88
|
|
|
85
89
|
### `[2]` Brute Force
|
|
86
90
|
|
|
87
91
|
| # | Module | Details |
|
|
88
92
|
|---|--------|---------|
|
|
89
|
-
| 1 | **
|
|
90
|
-
| 2 | **
|
|
91
|
-
| 3 | **
|
|
93
|
+
| 1 | **Native wordlist** | Built-in CUPP-style generator (offline, no external clone) — case/leet variants, years, suffixes, term combos |
|
|
94
|
+
| 2 | **CUPP wordlist** | Optional wrapper for `cupp.py -i` |
|
|
95
|
+
| 3 | **SSH brute** | `paramiko`, async via `asyncio.to_thread`, concurrency `Semaphore`, delay |
|
|
96
|
+
| 4 | **HTTP brute** | `aiohttp` — Basic Auth or form POST with configurable `fail_signature` |
|
|
92
97
|
|
|
93
|
-
> Every attack requires typing
|
|
98
|
+
> Every attack requires typing the authorization word (`AUTHORIZE` / `AUTORIZO`)
|
|
99
|
+
> to confirm — no silent bypass.
|
|
94
100
|
|
|
95
101
|
### `[3]` Advanced Scanning
|
|
96
102
|
|
|
97
103
|
- **Port scan** — fast (Top 21 common ports) or full (1–65535)
|
|
98
104
|
- **SYN scan** (stealth) when privileged, automatic fallback to **TCP connect**
|
|
99
105
|
- **Banner grabbing** — async, parallel
|
|
100
|
-
- **CVE analysis** per service via **NVD API v2.0**
|
|
106
|
+
- **CVE analysis** per service via **NVD API v2.0** — parses product + version
|
|
107
|
+
from the banner and matches by **CPE** (`virtualMatchString`), falling back to
|
|
108
|
+
keyword search; rate-limited and deduplicated per banner
|
|
109
|
+
- **Risk scoring** — each CVE is enriched with **CVSS** base score/severity,
|
|
110
|
+
**EPSS** exploitation probability and a **CISA KEV** flag; host risk is the
|
|
111
|
+
highest severity found (KEV forces critical). Filter with `--min-cvss` / `--kev-only`
|
|
101
112
|
- **Host discovery** (ARP scan) by CIDR — e.g. `192.168.0.0/24`
|
|
102
113
|
- Automatic risk classification based on CVEs found
|
|
103
114
|
|
|
@@ -111,6 +122,12 @@
|
|
|
111
122
|
| 4 | **Base64 decode** | base64 → UTF-8 with auto-padding |
|
|
112
123
|
| 5 | **JWT decode** | Header + payload without signature verification |
|
|
113
124
|
|
|
125
|
+
### `[5]` Audit — one target, consolidated profile
|
|
126
|
+
|
|
127
|
+
Orchestrates the recon modules (and, with `--active --authorize`, the port scan)
|
|
128
|
+
into a **single report** with an executive summary. Passive by default; stage
|
|
129
|
+
selection via `--only` / `--skip`. See the [CLI examples](#non-interactive-cli-scriptable).
|
|
130
|
+
|
|
114
131
|
---
|
|
115
132
|
|
|
116
133
|
## Installation
|
|
@@ -160,6 +177,55 @@ An interactive dark CLI opens with a numbered menu. Commands are entered at the
|
|
|
160
177
|
|
|
161
178
|
---
|
|
162
179
|
|
|
180
|
+
## Non-interactive CLI (scriptable)
|
|
181
|
+
|
|
182
|
+
Pass a subcommand to run a single action and print the result as JSON on stdout
|
|
183
|
+
(pipe-friendly) — no menu. Omit the subcommand to open the interactive menu.
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
gyntoolkit recon dns example.com --type MX # DNS lookup
|
|
187
|
+
gyntoolkit recon axfr example.com # zone transfer attempt
|
|
188
|
+
gyntoolkit recon webscan http://example.com # web content discovery
|
|
189
|
+
gyntoolkit recon shodan 1.1.1.1 # Shodan host (needs api_keys.shodan)
|
|
190
|
+
gyntoolkit recon ssl example.com --port 443 # TLS cert
|
|
191
|
+
gyntoolkit scan 127.0.0.1 --type fast # port scan + CVEs (CVSS/EPSS/KEV)
|
|
192
|
+
gyntoolkit scan 10.0.0.5 --min-cvss 7.0 --kev-only # only high-risk / actively exploited
|
|
193
|
+
gyntoolkit scan 192.168.0.0/24 # host discovery (CIDR)
|
|
194
|
+
gyntoolkit audit example.com # full passive profile → one report
|
|
195
|
+
gyntoolkit audit 10.0.0.5 --active --authorize # + active port scan (authorized)
|
|
196
|
+
gyntoolkit utils hash "text" --algo sha1
|
|
197
|
+
gyntoolkit utils jwt <token>
|
|
198
|
+
gyntoolkit wordlist --terms alice,fluffy,acme --years 1990,2020 --leet --save wl.txt
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`audit` runs a pipeline over one target and consolidates everything into a single
|
|
202
|
+
report with an executive summary. It is **passive by default** (WHOIS, DNS, geo,
|
|
203
|
+
subdomains, TLS, HTTP fingerprint, InternetDB, HIBP, email security); the active
|
|
204
|
+
port scan only runs with `--active --authorize`. Narrow it with `--only a,b` or
|
|
205
|
+
`--skip a,b`.
|
|
206
|
+
|
|
207
|
+
Global flags (place after the subcommand): `-o/--output DIR` saves a report,
|
|
208
|
+
`-f/--format {json,html,csv,md}` picks its format, `-q/--quiet` silences stdout,
|
|
209
|
+
`--lang {en,pt}` overrides the language.
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
gyntoolkit scan scanme.example.com -o reports -f md # also save a Markdown report
|
|
213
|
+
gyntoolkit recon geo 1.1.1.1 -o reports -f csv -q # CSV only, no stdout
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Active attacks are available but require explicit authorization:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
gyntoolkit brute ssh HOST --users users.txt --passwords pass.txt --authorize
|
|
220
|
+
gyntoolkit brute http URL --users admin --passwords pass.txt --mode form \
|
|
221
|
+
--user-field user --pass-field pass --fail-signature "Invalid" --authorize
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Without `--authorize`, brute subcommands abort — you confirm you have **written
|
|
225
|
+
authorization** for the target.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
163
229
|
## Example
|
|
164
230
|
|
|
165
231
|
Target: the **local security lab** (never a third-party system).
|
|
@@ -213,8 +279,8 @@ Full walkthrough: [`docs/security-lab.md`](docs/security-lab.md) and
|
|
|
213
279
|
|
|
214
280
|
## Reports
|
|
215
281
|
|
|
216
|
-
After scans and recon lookups, the CLI offers to export results as **JSON
|
|
217
|
-
dark-themed **HTML report**. Files are written to
|
|
282
|
+
After scans and recon lookups, the CLI offers to export results as **JSON**, a
|
|
283
|
+
dark-themed **HTML report**, **CSV** or **Markdown**. Files are written to
|
|
218
284
|
`reports/<module>-<timestamp>.<fmt>` (configurable via `export.dir`; set
|
|
219
285
|
`export.auto: true` to export without prompting). Details in
|
|
220
286
|
[`docs/reports.md`](docs/reports.md).
|
|
@@ -5,9 +5,10 @@ Este ``__init__`` re-exporta a API pública para manter compatibilidade com
|
|
|
5
5
|
``import gyntoolkit as g`` (usado pelo lab E2E) e pelo entry point do pyproject.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = "2.
|
|
8
|
+
__version__ = "2.3.0"
|
|
9
9
|
|
|
10
10
|
from . import i18n, ui
|
|
11
|
+
from .audit import run_audit
|
|
11
12
|
from .brute import (
|
|
12
13
|
cupp_generate,
|
|
13
14
|
http_bruteforce,
|
|
@@ -29,7 +30,8 @@ from .core import (
|
|
|
29
30
|
sanitize_input,
|
|
30
31
|
show_menu,
|
|
31
32
|
)
|
|
32
|
-
from .export import export_html, export_json, save_report
|
|
33
|
+
from .export import export_csv, export_html, export_json, export_md, save_report
|
|
34
|
+
from .mailsec import DEFAULT_DKIM_SELECTORS, mailsec_report
|
|
33
35
|
from .recon import (
|
|
34
36
|
DNS_TYPES,
|
|
35
37
|
TECH_SIGNATURES,
|
|
@@ -41,20 +43,25 @@ from .recon import (
|
|
|
41
43
|
internetdb_lookup,
|
|
42
44
|
mac_vendor,
|
|
43
45
|
reverse_dns,
|
|
46
|
+
shodan_host,
|
|
44
47
|
ssl_inspect,
|
|
45
48
|
subdomain_enum,
|
|
46
49
|
traceroute,
|
|
47
50
|
whois_lookup,
|
|
51
|
+
zone_transfer,
|
|
48
52
|
)
|
|
49
53
|
from .scan import (
|
|
50
54
|
check_vulnerabilities,
|
|
51
55
|
connect_scan,
|
|
52
56
|
get_banner,
|
|
53
57
|
network_discovery,
|
|
58
|
+
parse_service,
|
|
54
59
|
perform_scan,
|
|
55
60
|
syn_scan,
|
|
56
61
|
)
|
|
57
62
|
from .utils import b64_decode, b64_encode, hash_file, hash_text, jwt_decode
|
|
63
|
+
from .web import COMMON_PATHS, web_discovery
|
|
64
|
+
from .wordlist import generate_wordlist
|
|
58
65
|
|
|
59
66
|
__all__ = [
|
|
60
67
|
"__version__",
|
|
@@ -63,18 +70,28 @@ __all__ = [
|
|
|
63
70
|
"clear_screen", "is_admin_windows", "sanitize_input", "show_menu",
|
|
64
71
|
# scan
|
|
65
72
|
"syn_scan", "connect_scan", "get_banner", "check_vulnerabilities",
|
|
66
|
-
"network_discovery", "perform_scan",
|
|
73
|
+
"parse_service", "network_discovery", "perform_scan",
|
|
67
74
|
# recon
|
|
68
75
|
"whois_lookup", "dns_lookup", "geo_ip", "reverse_dns", "subdomain_enum",
|
|
69
76
|
"ssl_inspect", "http_fingerprint", "internetdb_lookup", "hibp_breaches",
|
|
70
|
-
"mac_vendor", "traceroute", "
|
|
77
|
+
"mac_vendor", "traceroute", "zone_transfer", "shodan_host",
|
|
78
|
+
"escolher_tipo_dns", "DNS_TYPES", "TECH_SIGNATURES",
|
|
79
|
+
# web discovery
|
|
80
|
+
"web_discovery", "COMMON_PATHS",
|
|
81
|
+
# mail security
|
|
82
|
+
"mailsec_report", "DEFAULT_DKIM_SELECTORS",
|
|
83
|
+
# audit
|
|
84
|
+
"run_audit",
|
|
85
|
+
# wordlist
|
|
86
|
+
"generate_wordlist",
|
|
71
87
|
# brute
|
|
72
88
|
"print_ethical_warning", "load_wordlist", "cupp_generate",
|
|
73
89
|
"ssh_bruteforce", "http_bruteforce",
|
|
74
90
|
# utils
|
|
75
91
|
"hash_text", "hash_file", "b64_encode", "b64_decode", "jwt_decode",
|
|
76
92
|
# config / export / ui
|
|
77
|
-
"CONFIG", "DEFAULTS", "load_config", "save_report",
|
|
93
|
+
"CONFIG", "DEFAULTS", "load_config", "save_report",
|
|
94
|
+
"export_json", "export_html", "export_csv", "export_md", "ui", "i18n",
|
|
78
95
|
# cli
|
|
79
96
|
"main_flow", "main_entry",
|
|
80
97
|
]
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Audit: orquestrador de engajamento — 1 alvo, perfil consolidado.
|
|
3
|
+
|
|
4
|
+
Roda um pipeline de etapas (recon passivo por padrão; scan ativo só com
|
|
5
|
+
``active=True`` + ``authorized=True``) e agrega tudo num único relatório com
|
|
6
|
+
resumo executivo. Reaproveita os módulos existentes (recon/scan/mailsec).
|
|
7
|
+
|
|
8
|
+
Etapas são resilientes: a falha de uma registra ``{"error": ...}`` na sua seção
|
|
9
|
+
sem derrubar o resto.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import ipaddress
|
|
14
|
+
import socket
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from .core import log, sanitize_input
|
|
19
|
+
|
|
20
|
+
# Etapas passivas (seguras) e ativas (intrusivas). Ordem define a saída.
|
|
21
|
+
PASSIVE_STAGES = ("whois", "dns", "geo", "subdomains", "ssl", "httpfp",
|
|
22
|
+
"internetdb", "hibp", "mailsec")
|
|
23
|
+
ACTIVE_STAGES = ("scan",)
|
|
24
|
+
ALL_STAGES = PASSIVE_STAGES + ACTIVE_STAGES
|
|
25
|
+
|
|
26
|
+
# Etapas que só fazem sentido p/ domínio (não p/ IP literal).
|
|
27
|
+
_DOMAIN_ONLY = {"whois", "subdomains", "hibp", "mailsec"}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _is_ip(target: str) -> bool:
|
|
31
|
+
try:
|
|
32
|
+
ipaddress.ip_address(target)
|
|
33
|
+
return True
|
|
34
|
+
except ValueError:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _resolve(target: str) -> str | None:
|
|
39
|
+
try:
|
|
40
|
+
return socket.gethostbyname(target)
|
|
41
|
+
except socket.gaierror:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# --------------------------------------------------------------------------
|
|
46
|
+
# Runners por etapa.
|
|
47
|
+
# --------------------------------------------------------------------------
|
|
48
|
+
def _passive_stage(name: str, target: str, ctx: dict) -> Any:
|
|
49
|
+
"""Executa uma etapa passiva (síncrona: requests/dns). Roda em thread."""
|
|
50
|
+
from . import recon
|
|
51
|
+
from .mailsec import mailsec_report
|
|
52
|
+
|
|
53
|
+
is_ip = ctx["is_ip"]
|
|
54
|
+
if name == "whois":
|
|
55
|
+
return {"whois": str(recon.whois_lookup(target))}
|
|
56
|
+
if name == "dns":
|
|
57
|
+
return {t: recon.dns_lookup(target, t) for t in ("A", "MX", "NS", "TXT")}
|
|
58
|
+
if name == "geo":
|
|
59
|
+
return recon.geo_ip(target)
|
|
60
|
+
if name == "subdomains":
|
|
61
|
+
subs = recon.subdomain_enum(target)
|
|
62
|
+
return {"total": len(subs), "subdomains": subs}
|
|
63
|
+
if name == "ssl":
|
|
64
|
+
return recon.ssl_inspect(target, 443)
|
|
65
|
+
if name == "httpfp":
|
|
66
|
+
scheme = "http" if is_ip else "https"
|
|
67
|
+
return recon.http_fingerprint(f"{scheme}://{target}")
|
|
68
|
+
if name == "internetdb":
|
|
69
|
+
return recon.internetdb_lookup(target)
|
|
70
|
+
if name == "hibp":
|
|
71
|
+
return {"breaches": recon.hibp_breaches(target)}
|
|
72
|
+
if name == "mailsec":
|
|
73
|
+
return mailsec_report(target)
|
|
74
|
+
raise ValueError(f"unknown passive stage: {name}")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
async def _run_one(name: str, target: str, ctx: dict) -> tuple[str, Any]:
|
|
78
|
+
"""Executa uma etapa com captura de erro. Passivas rodam em thread."""
|
|
79
|
+
try:
|
|
80
|
+
if name == "scan":
|
|
81
|
+
from .scan import perform_scan
|
|
82
|
+
|
|
83
|
+
ip = target if ctx["is_ip"] else (_resolve(target) or target)
|
|
84
|
+
results = await perform_scan(
|
|
85
|
+
ip, ctx["scan_type"], concurrency=ctx["concurrency"], nvd_api_key=ctx["nvd_key"],
|
|
86
|
+
)
|
|
87
|
+
return (name, {str(port): data for port, data in results.items()})
|
|
88
|
+
return (name, await asyncio.to_thread(_passive_stage, name, target, ctx))
|
|
89
|
+
except Exception as e: # noqa: BLE001 - etapa resiliente
|
|
90
|
+
log.warning("audit stage '%s' falhou: %s", name, e)
|
|
91
|
+
return (name, {"error": str(e)})
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _select_stages(is_ip: bool, active: bool, only: list[str] | None,
|
|
95
|
+
skip: list[str] | None) -> list[str]:
|
|
96
|
+
"""Resolve a lista final de etapas conforme tipo de alvo e flags."""
|
|
97
|
+
stages = list(PASSIVE_STAGES) + (list(ACTIVE_STAGES) if active else [])
|
|
98
|
+
if only:
|
|
99
|
+
wanted = set(only)
|
|
100
|
+
stages = [s for s in ALL_STAGES if s in wanted and (active or s not in ACTIVE_STAGES)]
|
|
101
|
+
if skip:
|
|
102
|
+
stages = [s for s in stages if s not in set(skip)]
|
|
103
|
+
if is_ip:
|
|
104
|
+
stages = [s for s in stages if s not in _DOMAIN_ONLY]
|
|
105
|
+
return stages
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _summarize(stages_out: dict) -> dict:
|
|
109
|
+
"""Resumo executivo: contagens e destaques das seções coletadas."""
|
|
110
|
+
summary: dict[str, Any] = {}
|
|
111
|
+
scan = stages_out.get("scan")
|
|
112
|
+
if isinstance(scan, dict) and "error" not in scan:
|
|
113
|
+
open_ports = list(scan.keys())
|
|
114
|
+
cves = [c for port in scan.values() for c in port.get("vulnerabilidades", [])]
|
|
115
|
+
kev = [c for c in cves if c.get("kev")]
|
|
116
|
+
summary["open_ports"] = len(open_ports)
|
|
117
|
+
summary["cves"] = len(cves)
|
|
118
|
+
summary["kev_cves"] = len(kev)
|
|
119
|
+
subs = stages_out.get("subdomains")
|
|
120
|
+
if isinstance(subs, dict) and "total" in subs:
|
|
121
|
+
summary["subdomains"] = subs["total"]
|
|
122
|
+
mail = stages_out.get("mailsec")
|
|
123
|
+
if isinstance(mail, dict) and "summary" in mail:
|
|
124
|
+
summary["mailsec_weak"] = mail["summary"].get("weak", 0) + mail["summary"].get("missing", 0)
|
|
125
|
+
return summary
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async def run_audit(
|
|
129
|
+
target: str,
|
|
130
|
+
active: bool = False,
|
|
131
|
+
authorized: bool = False,
|
|
132
|
+
only: list[str] | None = None,
|
|
133
|
+
skip: list[str] | None = None,
|
|
134
|
+
concurrency: int = 100,
|
|
135
|
+
nvd_key: str = "",
|
|
136
|
+
scan_type: str = "fast",
|
|
137
|
+
) -> dict:
|
|
138
|
+
"""Roda o audit e devolve o relatório consolidado.
|
|
139
|
+
|
|
140
|
+
Etapas ativas (scan) só rodam com ``active=True`` **e** ``authorized=True`` —
|
|
141
|
+
autorização é exigida p/ qualquer ação intrusiva.
|
|
142
|
+
"""
|
|
143
|
+
target = sanitize_input(target, r"[A-Za-z0-9.:/-]")
|
|
144
|
+
is_ip = _is_ip(target)
|
|
145
|
+
run_active = active and authorized
|
|
146
|
+
ctx = {"is_ip": is_ip, "concurrency": concurrency, "nvd_key": nvd_key, "scan_type": scan_type}
|
|
147
|
+
|
|
148
|
+
stages = _select_stages(is_ip, run_active, only, skip)
|
|
149
|
+
results = await asyncio.gather(*[_run_one(s, target, ctx) for s in stages])
|
|
150
|
+
stages_out = dict(results)
|
|
151
|
+
# reordena conforme ALL_STAGES p/ saída estável
|
|
152
|
+
ordered = {s: stages_out[s] for s in ALL_STAGES if s in stages_out}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
"target": target,
|
|
156
|
+
"is_ip": is_ip,
|
|
157
|
+
"active": run_active,
|
|
158
|
+
"active_requested_without_auth": active and not authorized,
|
|
159
|
+
"generated": datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC"),
|
|
160
|
+
"summary": _summarize(ordered),
|
|
161
|
+
"stages": ordered,
|
|
162
|
+
}
|
|
@@ -21,7 +21,9 @@ def print_ethical_warning(action: str) -> bool:
|
|
|
21
21
|
print(f"{Fore.RED}{i18n.t('brute.warn_crime')}{Style.RESET_ALL}")
|
|
22
22
|
print(f"{Fore.RED}{'=' * 60}{Style.RESET_ALL}")
|
|
23
23
|
confirm = input(f"{Fore.YELLOW}{i18n.t('brute.confirm')}{Style.RESET_ALL}").strip()
|
|
24
|
-
|
|
24
|
+
# Aceita a palavra localizada (en/pt) e o legado "AUTORIZO" como alias.
|
|
25
|
+
accepted = {i18n.t("brute.confirm_word").strip().upper(), "AUTORIZO"}
|
|
26
|
+
return confirm.upper() in accepted
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
def load_wordlist(path: str) -> list[str]:
|
|
@@ -158,7 +160,9 @@ async def http_bruteforce(
|
|
|
158
160
|
if mode == "basic":
|
|
159
161
|
auth = aiohttp.BasicAuth(u, p)
|
|
160
162
|
async with session.get(url, auth=auth) as r:
|
|
161
|
-
|
|
163
|
+
# Basic Auth: sucesso = 2xx. 401/403 = falha; 404/5xx
|
|
164
|
+
# NÃO são credencial válida (evita falso-positivo).
|
|
165
|
+
ok = 200 <= r.status < 300
|
|
162
166
|
else:
|
|
163
167
|
payload = {user_field: u, pass_field: p}
|
|
164
168
|
async with session.post(url, data=payload, allow_redirects=False) as r:
|