Rustsploit implements defence-in-depth throughout the codebase. All contributors must follow these patterns when writing modules or modifying core code.
| File | Constant | Value | Purpose |
|---|---|---|---|
shell.rs |
MAX_INPUT_LENGTH |
4096 | Maximum shell input length |
sanitize.rs |
MAX_TARGET_LENGTH |
2048 | Maximum target string length |
shell.rs |
MAX_URL_LENGTH |
2048 | Maximum URL length |
shell.rs |
MAX_PATH_LENGTH |
4096 | Maximum file path length |
utils.rs |
MAX_FILE_SIZE |
10 MB | Maximum file size to read |
config.rs |
MAX_HOSTNAME_LENGTH |
253 | DNS hostname limit |
api.rs |
MAX_REQUEST_BODY_SIZE |
1 MB | API request body limit |
api.rs |
MAX_TRACKED_IPS |
100,000 | IP tracker limit |
if input.len() > MAX_INPUT_LENGTH {
return Err(anyhow!("Input too long (max {} characters)", MAX_INPUT_LENGTH));
}The sanitize_string_input() function performs multiple layers of cleaning:
- Null byte removal -- inputs containing
\0are rejected outright - Control character filtering -- all control characters (except
\t) are stripped from the input - Length enforcement -- inputs exceeding
MAX_COMMAND_LENGTHare rejected
// Reject null bytes, then filter control characters (except tab)
let sanitized: String = input.chars()
.filter(|c| !c.is_control() || *c == '\t')
.collect();For command-specific validation (validate_command_input), null bytes are stripped and length is enforced against MAX_COMMAND_LENGTH.
If suspicious patterns (bash, sudo, ../) are detected, a warning is printed but the string is still returned unmodified:
[!] Input contains shell/path patterns. Treated as literal text string.
if input.contains("..") || input.contains("//") {
return Err(anyhow!("Path traversal detected"));
}Always use the framework's normalize_target function:
use crate::utils::normalize_target;
let normalized = normalize_target(raw_target)?;
// Handles IPv4, IPv6, hostnames, URLs, CIDR with full validationFor custom character validation:
use regex::Regex;
let valid_chars = Regex::new(r"^[a-zA-Z0-9.\-_:\[\]]+$").unwrap();
if !valid_chars.is_match(target) {
return Err(anyhow!("Invalid characters in target"));
}// Use saturating_add to prevent integer overflow
counter = counter.saturating_add(1);const MAX_ATTEMPTS: u8 = 10;
let mut attempts = 0u8;
loop {
attempts += 1;
if attempts > MAX_ATTEMPTS {
println!("Too many invalid attempts. Using default.");
return Ok(default);
}
// prompt logic
}When reading files:
- Validate path does not contain
.. - Use
canonicalize()to resolve the real path - Check file size before reading (ref:
MAX_FILE_SIZE) - Skip symlinks for security
The API server (api.rs) implements:
RequestBodyLimitLayer— prevents DoS via oversized payloads (1 MB max)- Rate limiting — 3 failed auth attempts → 30 s block per IP
- Auto-cleanup — old entries purged at 100,000 entries
- IP tracking + key rotation — suspicious activity triggers auto-rotation in hardening mode
- Secure defaults — by default, considers
127.0.0.1as the intended private bind - WebSocket limits — max 100 concurrent connections, 1 MiB frame cap, 30s heartbeat
The MCP server (mcp/server.rs) implements:
isolate_protocol_stdout()— redirects fd 1 to /dev/null so moduleprintln!cannot corrupt the JSON-RPC streamMAX_LINE_BYTES— 1 MiB cap on incoming lines to prevent memory exhaustion- Binary-safe reads — uses
read_until()instead ofread_line()for no UTF-8 requirement - Non-UTF-8 error handling — returns proper JSON-RPC error responses for malformed input
The spool system (spool.rs) implements:
O_NOFOLLOW— prevents TOCTOU race conditions on symlinked spool files- Parent symlink check — rejects spool paths with symlinked parent directories
- Lock-first pattern — acquires write lock before creating files to prevent orphaned files
write_line()returnsResult— callers handle write failures instead of silently dropping output
Modules requiring raw sockets call require_root() at startup:
use crate::utils::privilege::require_root;
require_root("ICMP raw socket")?;Returns a descriptive error with the current euid instead of a cryptic "permission denied" from the socket layer. Used by DoS modules, ping sweep, and raw packet scanners.
The framework automatically runs basic_honeypot_check before any module execution when a target is set.
- Scans 200 common ports with a 250 ms timeout each
- If 11 or more ports respond, warns that the target is likely a honeypot
- Runs automatically in the shell's
runandrun_allcommands - Can be called manually from module code:
use crate::utils::basic_honeypot_check;
basic_honeypot_check(&ip).await;Standard across mass-scan capable modules (e.g., camxploit, telnet_hose, telnet_bruteforce, exploit modules with 0.0.0.0/0 support):
| CIDR | Category |
|---|---|
10.0.0.0/8 |
Private |
127.0.0.0/8 |
Loopback |
172.16.0.0/12 |
Private |
192.168.0.0/16 |
Private |
224.0.0.0/4 |
Multicast |
240.0.0.0/4 |
Reserved |
0.0.0.0/8 |
This network |
100.64.0.0/10 |
Carrier-grade NAT |
169.254.0.0/16 |
Link-local |
198.18.0.0/15 |
Benchmarking |
198.51.100.0/24 |
Documentation |
203.0.113.0/24 |
Documentation |
255.255.255.255/32 |
Broadcast |
| Public DNS | 1.1.1.1, 8.8.8.8, etc. |
Uses the ipnetwork crate for proper CIDR matching.
All persistent data uses atomic write-to-temp-then-rename to prevent corruption:
| File | Purpose | Sensitivity |
|---|---|---|
~/.rustsploit/global_options.json |
Global options (setg) | Low — user preferences |
~/.rustsploit/creds.json |
Discovered credentials | High — contains passwords/hashes |
~/.rustsploit/workspaces/<name>.json |
Hosts, services, notes | Medium — engagement data |
~/.rustsploit/loot_index.json |
Loot metadata | Medium |
~/.rustsploit/loot/ |
Loot files | High — may contain sensitive data |
~/.rustsploit/results/ |
Module output files | Medium |
~/.rustsploit/history.txt |
Shell command history | Medium |
Important: The creds.json and loot/ files may contain sensitive data. Protect ~/.rustsploit/ with appropriate file permissions (e.g., chmod 700).