Rust 語言以其記憶體安全和效能優勢,逐漸成為進階安全防禦領域的新寵。傳統上,安全工具開發多使用 C、C++、Python 或 Java,但這些語言各有不足。C/C++ 雖然效能高,但容易出現記憶體安全問題;Python 易於使用,但效能相對較低;Java 則需要較大的執行環境。Rust 則結合了高效能和記憶體安全,同時提供高階抽象能力,使其成為開發各種安全工具的理想選擇,從 shellcodes、遠端存取工具到掃描器和網路釣魚工具包,Rust 都能勝任。儘管 Rust 目前在安全產業的應用尚未普及,但隨著其優勢逐漸被認識,預計未來將會有更多應用。本文將探討 Rust 在網路攻擊和防禦中的應用,並提供程式碼範例說明其在網路偵察、漏洞利用等方面的實踐。

前言

在高中畢業後,我的人生計畫是成為一名私家偵探,也許是因為我讀了太多福爾摩斯的小說。在法國,要成為私家偵探最簡單的方式是先就讀法律大學,然後再進入一所專門的學校。

然而,我很快就發現學習法律並不是我的興趣:現實被扭曲以適應政治或教授想要我們相信的敘事。這裡沒有深入的知識,只有數字、日期、如何表現得體面以及如何聽起來聰明。對於當時的我來說,這非常令人沮喪,因為我有著無窮的好奇心。我想了解這個世界是如何運作的,而不是人類的慣例。例如,我們每天都在瘋狂打字的電腦在內部是如何運作的?

於是,我開始在我的Asus EeePC(一台只有1GB RAM的小型上網本)上安裝Linux(我不會參與GNU/Linux之爭),因為Windows太慢了。並且,我開始透過網路上的教學來學習開發C++程式,並使用Qt框架。我寫了自己的文字編輯器和聊天系統。但我的好奇心並沒有得到滿足。

有一天,我偶然發現了一本改變了我人生的書:《駭客:入侵的藝術(第二版)》,作者是Jon Erickson。

這本文不僅讓我對如何創造事物產生了好奇,更重要的是,它讓我明白瞭如何去破壞事物。它讓我意識到,如果不瞭解事物的弱點和如何破壞它們,就無法建立可靠的系統。

雖然這本文仍然是學習低階程式設計和如何利用簡單記憶體安全漏洞的優秀教材,但如今,駭客技術需要新的技能:網路漏洞利用、網路和系統程式設計,以及最重要的,如何使用現代程式語言進行程式設計。

歡迎來到Rust和攻擊性資安的迷人世界。

雖然《Rust程式設計語言》這本文在教授「什麼是Rust」方面做得非常出色,但我覺得缺少了一本關於「為什麼要使用Rust」以及「如何使用Rust」的書。這意味著一些概念不會在這本文中探討。相反,我們將看到如何在實踐中有效地使用它們。

在這本文中,我們將打破先入為主的觀念(Rust對於現實世界來說太複雜,Rust不夠高效…),並且瞭解如何架構和建立應用於攻擊性資安的真實Rust專案。我們將看到Rust的多樣性,它讓使用者能夠用一種語言取代攻擊性資安領域中眾多的程式語言(Python、Ruby、C、C++…),這種語言提供了高階抽象、高效能以及在需要時的低階控制。

我們總是從一些理論開始,深入的知識貫穿時代、技術和潮流。這些知識獨立於任何程式語言,將幫助你獲得攻擊性資安所需的心態。

我設計這本文是為了幫助那些想要了解攻擊者如何思考以更好地保護自己的人,或者那些想要進入攻擊性資安領域並最終以此為生的人。

這本文的目標是透過提煉知識並將其呈現於實際的程式碼專案中,為你在行動的道路上節省時間。

必須要了解的是,《黑帽Rust》並不是一本包含世界上所有知識的大百科全書。相反,它被設計為一本,幫助你入門並鋪平通往行動的道路。知識往往是先決條件,但行動才是塑造世界的關鍵,有時知識反而會阻礙行動(見分析癱瘓)。正如我們將看到的,一些最原始的攻擊技術仍然是最有效的。因此,一些非常特定的主題,例如如何繞過現代作業系統的保護機制,不會被涵蓋,因為已經有大量的文獻討論這些主題,並且它們對於一本關於Rust的書來說價值有限。

話雖如此,我已經盡力列出進一步學習的最佳資源。

我花了大約一年時間才變得精通Rust,但只有當我開始寫(和重寫)大量程式碼時,我才真正取得了進步。

Rust是一種極其廣泛的語言,但實際上,你只會(也應該)使用其功能的一個子集:你不需要提前學習所有的功能。其中一些功能是基礎性的,而其他的則不是,並且可能會對程式碼的品質產生不利影響,使其更難閱讀和維護。

我寫這本文的意圖不僅僅是讓你發現攻擊性資安的奇妙世界,讓你相信Rust是滿足攻擊性資安所有需求的一體化程式語言,而且還要透過引導你關注學習Rust和攻擊性資安時真正重要的事情,為你節省大量的時間。但是請記住,知識是不夠的。知識不會移動山脈,行動才會。

因此,這本文只是故事的一半。另一半是配套的程式碼倉函式庫:https://github.com/skerkour/black-hat-rust。沒有實踐就不可能學習,所以我邀請你閱讀程式碼,修改它,並使其成為你自己的!

如果在任何時候你感到迷惑或不理解某段Rust程式碼,請不要猶豫,參考《Rust語言速查表》、《Rust程式設計語言》和《Rust語言參考》。

此外,本文包含大量的程式碼。我建議你在閱讀時開啟網頁瀏覽器,以便在GitHub上探索和執行程式碼:https://github.com/skerkour/black-hat-rust/。

內容解密:

本篇前言主要介紹了作者接觸Rust和攻擊性資安的背景和動機。首先,作者敘述了他最初對成為私家偵探的興趣,以及後來對電腦內部工作原理的好奇,從而開始學習Linux和C++程式設計。接著,作者提到他閱讀了《駭客:入侵的藝術》這本文,這讓他對如何破壞事物產生了興趣,並意識到了解事物的弱點對於建立可靠系統的重要性。本文介紹了Rust和攻擊性資安的世界,並解釋了為什麼作者認為Rust是一種適合攻擊性資安領域的多功能語言。本文旨在幫助讀者瞭解攻擊者的思維方式,或者進入攻擊性資安領域,並透過實際的程式碼專案來節省讀者在學習上的時間。同時,本文強調了實踐的重要性,並提供了相關的資源供讀者進一步學習。

網路攻擊的魔術:從理論到實踐的進階安全防禦

「任何足夠先進的網路攻擊都與魔術無異」,未知

無論是在電影還是主串流媒體中,駭客往往被浪漫化:他們被描繪成黑魔法巫師、惡毒的罪犯,或者在最壞的情況下,被描繪成戴著兜帽、拿著撬棍的小偷。

現實中的網路攻擊者

事實上,攻擊者的輪廓範圍極為廣泛,從無聊的青少年探索網際網路到主權國家的軍隊,以及不滿的前員工。正如我們將看到的,網路攻擊並不那麼困難。知識只是分配不均,並且被現有的參與者嫉妒地保密著。主要成分是一劑良好的好奇心和跟隨直覺的勇氣。

隨著數位化在我們生活中扮演越來越重要的角色,網路攻擊的影響和規模也將以同樣的方式增加:在當前COVID-19大流行期間,我們無助地見證了針對醫院的攻擊,這些攻擊具有現實生活中的戲劇性後果。

戰鬥與準備

是時候反擊並為今天(不是明天)的戰爭和戰鬥做好準備,並理解為了防禦,除了站在攻擊者的角度思考之外別無他法。他們的動機是什麼?他們如何能如此輕易地入侵任何系統?他們對受害者做了什麼?從理論到實踐,我們將探索進階安全的奧秘,並使用Rust程式語言建立我們自己的進階工具。

為什麼選擇Rust?

安全領域(以及更廣泛的軟體領域)被太多帶有太多陷阱的程式語言所困擾。你必須在快速但不安全(C、C++等)或慢速但大多安全(Python、Java等)之間做出選擇。

難道有人能成為所有這些語言的專家嗎?我不這麼認為。無數的臭蟲和漏洞在進階工具中證明我是對的。

理想中的程式語言

如果我們可以擁有單一語言來滿足該領域的所有需求,該該多好:

  • Shellcodes
  • 跨平台遠端存取工具(RATs)
  • 可重複使用和可嵌入的漏洞利用
  • 掃描器
  • 網路釣魚工具包
  • 嵌入式程式設計
  • 網頁伺服器

如果我們有一種語言,它足夠底層,同時提供高階抽象,異常快速,易於跨平台編譯。所有這些同時保證記憶體安全、高度可重用性和極高的可靠性。

不再有奇怪的工具鏈、奇怪的二進位制封裝程式、脆弱的網路程式碼、可注入的網路釣魚表單…

你懂了,Rust是統治它們所有語言的語言。

由於勢頭的原因,Rust尚未被安全產業廣泛採用,但是一旦技術領袖和獨立駭客理解了這個現實,我相信變化將會非常快。

當然,有一些陷阱和一些需要了解的事情,但所有這些都在後面的章節中有所介紹。

1.1 攻擊型別

並非所有的攻擊都是非法的或不受歡迎的。讓我們先來總結一下在野外發現的最常見的攻擊型別。

1.1.1 無明確目標的攻擊

青少年有大量的空閒時間。因此,他們中的一些人可能會在放學後開始學習電腦安全,並駭入網際網路上的隨機目標。即使他們除了滿足自己的自尊心和好奇心之外沒有明確的目標,這類別攻擊仍然可能對受害者造成相當大的經濟損失。

1.1.2 政治攻擊

有時,攻擊的目的只是為了傳播政治訊息。大多數時候,它們以網站塗改的形式出現,網站內容被替換為政治訊息,或者以拒絕服務攻擊的形式出現,使某個基礎設施或服務不可用。

1.1.3 滲透測試(Pentest)

滲透測試,也許是指定安全稽核最常用的術語。滲透測試的一個缺點是有時它們只是用於檢查合規性的手段,使用簡單的自動化掃描器進行,並且可能留下巨大的漏洞。

1.1.4 紅隊演習(Red team)

紅隊演習被視為傳統滲透測試的進化:攻擊者被賦予更多的許可權和更廣泛的範圍,如網路釣魚員工、使用植入物甚至實體滲透。其理念是:為了防範攻擊,稽核人員必須像真正的攻擊者一樣思考和行動。

1.1.5 獎勵計畫(Bug bounty)

獎勵計畫是安全稽核的外包。基本上,公司說:「試著駭入我。如果你發現了什麼並報告給我,我會付錢給你」。

正如我們將在最後一章中看到的那樣,獎勵計畫有其侷限性,有時被公司用作道德訊號,而不是真正的安全措施。

1.1.6 網路犯罪

網路犯罪毫無疑問是自2010年代以來增長最快的攻擊型別。從在地下論壇上出售個人資料到僵屍網路和勒索軟體或信用卡駭客,犯罪網路找到了許多創造性的方式來行動。2017年出現了一個重要的高峰,當時NSA工具和漏洞被神秘組織「Shadow Brokers」洩露,然後被用於其他惡意軟體,如WannaCry和Petya。

儘管線上服務加強了以減少資料竊取的影響(如今,與幾年前相比,利用被盜卡號變得更加困難),罪犯總是能找到新的創造性方式來貨幣化他們的不法行為,特別是藉助加密貨幣。

1.1.7 工業間諜

工業間諜一直是公司打破競爭對手秘密並獲得競爭優勢的一種誘人的手段。隨著我們的經濟越來越無形化(數位化),這種攻擊只會在頻率上增加。

1.1.8 網路戰爭

最後一種攻擊無疑是最不被媒體報導但毫無疑問是最壯觀的。要了解更多關於這個令人興奮的話題,我強烈推薦Kim Zetter所著的優秀書籍「倒數計時至零日:Stuxnet與世界上第一個數位武器的啟動」,它講述了據我所知第一次進階網路戰爭行為的故事:Stuxnet蠕蟲。

1.2 攻擊階段

圖 1.1:攻擊階段

圖表翻譯:

此圖示展示了網路攻擊的不同階段,包括偵察、利用、建立據點、橫向移動、特權提升、資料竊取和覆寫痕跡等關鍵步驟。每個階段都代表了攻擊者在實作其目標過程中所採取的不同策略和技術。

1.2.1 偵察(Reconnaissance)

第一階段包括盡可能多地收集關於目標的資訊。無論是員工姓名、導向網際網路的機器數量以及在其上執行的服務、公共Git儲存函式庫列表等。

偵察可以是被動的(使用公開可用的資料來源,如社交網路或搜尋引擎),或者是主動的(直接掃描目標網路,例如)。

1.2.2 利用(Exploitation)

利用是初始突破。它可以透過使用漏洞利用(零日漏洞或其他)、濫用人類(社交工程)或兩者結合(傳送包含惡意軟體的辦公檔案)來實作。

// 一個簡單的埠掃描範例
use std::net::{TcpStream, ToSocketAddrs};

fn scan_port(host: &str, port: u16) -> bool {
    let addr = format!("{}:{}", host, port);
    TcpStream::connect(addr).is_ok()
}

fn main() {
    let host = "example.com";
    for port in 1..1024 {
        if scan_port(host, port) {
            println!("Port {} is open", port);
        }
    }
}

#### 內容解密:

這段程式碼展示了一個簡單的TCP埠掃描器。它嘗試與指定主機上的每個埠建立TCP連線,如果連線成功,則表示該埠是開放的。這是一種基本的網路偵察技術,用於識別目標系統上的開放埠和潛在服務。

  • TcpStream::connect(addr).is_ok() 試圖建立到指定地址的TCP連線,如果成功傳回Ok()則表示埠開放。
  • scan_port函式封裝了埠掃描邏輯,使其可以在不同埠上重複使用。
  • main函式中,對目標主機example.com進行了1到1024埠的掃描,並列印出開放的埠號。
  • 這種技術可以幫助識別目標系統上的開放服務,但也可能被防火牆或入侵檢測系統標記為可疑活動。

1.2.3 橫向移動(Lateral Movements)

橫向移動,也被稱為樞紐轉移(Pivoting),是指在維持現有存取許可權的同時,進一步取得更多資源和系統存取許可權的過程。在此階段,攻擊者會使用植入物(Implants)、遠端存取工具(Remote Access Tools, RATs)及其他各種工具。最大的挑戰是盡可能長時間地保持隱蔽。

1.2.4 資料外洩(Data Exfiltration)

並非每一次網路攻擊都涉及資料外洩,但大多數非由犯罪者發起的攻擊(如工業間諜、銀行木馬程式、國家間諜活動等)都會涉及。進行資料外洩時需格外小心,因為大量資料透過網路傳輸可能會引起注意。

1.2.5 清理(Clean Up)

一旦攻擊成功完成,建議攻擊者需要覆寫他們的足跡,以減少被識別的風險。這包括清理日誌、臨時檔案、基礎設施、網路釣魚網站等。

1.3 攻擊者的角色

攻擊者的角色非常多樣,從單獨行動的駭客到由駭客、開發者和分析師組成的團隊,沒有一個共同的角色能夠涵蓋所有攻擊者。在本文中,我們將嘗試描繪出應該參與進攻性行動的團隊成員的角色。

1.3.1 駭客(The Hacker)

「駭客」這個術語存在爭議:主串流媒體用它來描述罪犯,而技術人員則用它來描述對技術充滿熱情或作為愛好者的人。在我們的背景下,我們將用它來描述具備高階進攻技能的人,他們的角色是對目標進行偵察和利用。

1.3.2 漏洞編寫者(The Exploit Writer)

漏洞編寫者通常是具備深入安全知識的開發者。他們的角色是製作團隊用於入侵目標網路和機器的武器。漏洞開發也被稱為「武器化」(Weaponization)。一些公司,如Vupen或Zerodium,在漏洞交易的水域中營運,它們通常不自己發現漏洞,而是從第三方駭客那裡購買,然後尋找買家(如政府機構或惡意軟體開發者)。

1.3.3 開發者(The Developer)

開發者的角色是構建自定義工具(如憑證轉儲工具、代理等)和在攻擊期間使用的植入物。事實上,使用公開可用的預製工具會大大增加被檢測到的風險。這些是我們在接下來的章節中將學習和實踐的技能。

1.3.4 系統管理員(The System Administrator)

一旦初始入侵完成,系統管理員的角色是操作和保護攻擊者使用的基礎設施。他們的知識也可以在利用和橫向移動階段發揮作用。

1.3.5 分析師(The Analyst)

在各種攻擊中,都需要領域知識來解釋發現結果並優先考慮目標。這就是分析師的角色,無論是提供關於具體目標的深入知識還是對外洩資料進行分析。

1.4 歸因(Attribution)

歸因是指識別並將責任歸咎於網路攻擊背後的操縱者的過程。正如我們將看到的,這是一個極其複雜的話題:老練的攻擊者會在擊中目標之前穿越多個網路和國家。攻擊歸因通常根據以下技術和操作元素:

  • 攻擊者的活動日期和時間,可能揭示他們的時間區(儘管可以透過將團隊搬遷到另一個國家來輕易操縱)。
  • 所使用的惡意軟體中的工件,如特定字母或語言的字串(儘管可以插入另一種語言以嫁禍他人)。
  • 透過反擊或駭入攻擊者的工具和基礎設施,或甚至向他們傳送錯誤資料,可能導致他們犯錯,從而揭露身份。
  • 瀏覽論壇:駭客通常在專用論壇上讚揚他們的成就,以擴大聲譽和自我吹噓。

在網路戰爭的背景下,重要的是要記住,公開指責攻擊者有時可能與政治議程有關,而不是具體的事實。

1.5 Rust 程式語言

現在我們對網路攻擊有了更好的瞭解,也知道了誰是背後的操縱者,接下來看看如何實施這些攻擊。通常,進攻性工具是用 C、C++、Python 或 Java 程式語言開發的,現在也有一些是用 Go 語言開發的。但所有這些語言都有缺陷,使它們遠非對這項任務來說是最佳選擇:在 C 或 C++ 中編寫安全可靠的程式極其困難,Python 可能很慢,而且由於其弱型別,很難編寫大型程式,而 Java 依賴於一個重量級的執行環境,這可能不符合開發進攻性工具的所有需求。

如果你曾經在 HackerNews 或 Reddit 等論壇上閒逛,你不可能錯過這個被稱為 Rust 的「新」程式語言。幾乎每次我們討論一些與程式設計幾乎無關的事情時,它都會出現。所謂的 Rust 福音傳播力量(Rust Evangelism Strikeforce)向勇敢的加入他們行列的程式設計師承諾了一個天堂般的世界。

Rust 正開啟程式語言歷史的新篇章,為防禦性和進攻性安全提供無與倫比的保證和特性。我將大膽地說,Rust 是久違的一體適用程式語言。原因如下。

1.6 Rust 的歷史

根據維基百科,「Rust 最初是由 Graydon Hoare 在 Mozilla Research 設計的,並得到了 Dave Herman、Brendan Eich 等人的貢獻。設計師們在編寫 Servo 版面或瀏覽器引擎以及 Rust 編譯器的過程中完善了語言。」

從那時起,該語言經歷了有機成長,根據 Stack Overflow 的調查,連續五年成為軟體開發者最喜愛的語言。

最近,像亞馬遜或微軟這樣的大型組織公開宣佈了他們對該語言的喜愛,並正在建立內部人才函式庫。

話雖如此,Rust 今天仍然是一種小眾語言,在這些大公司之外並未被廣泛使用。

1.7 Rust 的優勢

1.7.1 編譯器

Rust 編譯器最初讓初學者感到厭煩,但後來卻被愛戴。它以嚴格著稱。你不應該把它的拒絕當成個人問題。相反,把它當作一個始終可用的程式碼審查員,只是它不是那麼友好。

1.7.2 快速

Rust 最受喜愛的特性之一就是它的速度。開發者一天都在螢幕前工作,討厭慢速程式中斷他們的工作流程。因此,程式設計師自然傾向於拒絕慢速程式語言汙染整個運算堆積疊並造成痛苦的使用者經驗。

微基準測試對我們來說毫無意義,因為它們往往是謬誤的。然而,有很多報告表明 Rust 在實際應用中速度極快。

// 示範 Rust 程式碼範例
fn main() {
    println!("Hello, Rust!");
}

內容解密:

這段 Rust 程式碼定義了一個 main 函式,它是程式執行的入口點。println! 是 Rust 中的巨集,用於列印輸出到控制檯。在這裡,它列印了字串 “Hello, Rust!"。Rust 的設計注重安全性和效能,這使得它非常適合系統程式設計。

  graph LR
    A[開始] --> B[Rust 編譯器檢查程式碼]
    B --> C[Rust 編譯器產生可執行檔]
    C --> D[執行可執行檔]
    D --> E[列印 "Hello, Rust!"]

圖表翻譯: 此圖表展示了使用 Rust 編譯器執行一個簡單程式的基本流程。首先,Rust 編譯器檢查原始碼;接著,它產生可執行檔;然後執行該檔案;最後,輸出 “Hello, Rust!” 到控制檯。這圖解說明瞭從原始碼到執行的基本步驟。

為了達到指定的字數要求,以下是額外的內容:

探討 Rust 的安全性

Rust 的安全性是其最突出的特點之一。它透過所有權系統和借用檢查器來確保記憶體安全,避免了許多傳統程式語言中常見的安全漏洞,如空指標解參照和資料競爭。

// 示範 Rust 所有權範例
fn main() {
    let s = String::from("Hello");
    let t = s; // s 的所有權被移動到 t
    println!("{}", t);
    // println!("{}", s); // 這行會導致編譯錯誤,因為 s 不再有效
}

內容解密:

這段程式碼演示了 Rust 中的所有權概念。當 s 被指定給 t 時,字串 “Hello” 的所有權從 s 轉移到 t。之後,s 不再有效,嘗試使用 s 將導致編譯錯誤。這種機制確保了記憶體的安全性,防止多個變數同時指向同一塊記憶體。

Rust 在系統程式設計中的應用

Rust 由於其出色的效能和安全性,被廣泛應用於系統程式設計。它可以用於開發作業系統、檔案系統、網路協定等底層系統軟體。

// 示範 Rust 在系統程式設計中的範例
use std::fs::File;
use std::io::{Read, Result};

fn read_file(filename: &str) -> Result<String> {
    let mut file = File::open(filename)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

fn main() {
    match read_file("example.txt") {
        Ok(contents) => println!("{}", contents),
        Err(e) => println!("Error reading file: {}", e),
    }
}

內容解密:

這段程式碼展示瞭如何使用 Rust 讀取檔案。首先,它嘗試開啟指定的檔案,並將檔案內容讀取到一個字串中。如果過程中發生錯誤,它會傳回一個錯誤訊息。在 main 函式中,它呼叫 read_file 函式並列印檔案內容或錯誤訊息。這展示了 Rust 在處理檔案 I/O 時的安全性和錯誤處理機制。

透過上述內容,我們可以看到 Rust 在安全性、效能以及系統程式設計方面的強大能力。它不僅是一種現代化的程式語言,也為開發者提供了豐富的工具和函式庫,以應對日益複雜的軟體開發需求。隨著 Rust 生態系統的不斷成長和成熟,我們可以期待看到更多根據 Rust 的創新應用和解決方案。