目標讀者:系統安全愛好者
閱讀時長:約 7分鐘
本文概要:一種 Linux 中進程隱藏的思路、操作與步驟截圖
前置知識:最好對 Linux 下編譯程序略知一二(文末有知識注解與擴展閱讀,最后奉上網易小風景)
感謝 NSRC 老司機團長的分享~
前言
之前團隊內技術交流時小伙伴分享的自建蜜罐深深的引起了我的興趣,于是本人決定山寨一個類似的蜜罐把玩一番~但是擺在眼前的一個問題就是:
一些監控進程需要運行在蜜罐中,一但“請君入罐”后被黑客察覺到這些奇奇怪怪的進程,就十分尷尬了
所以當務之急是需要將這些監控進程隱藏起來,對Linux略知一二的樓主自然也就接受挑戰啦。
準備工作
眾所周知,Linux 操作系統天生自帶一個虛擬分區 /proc,該分區下保存硬件信息、內核運行參數、系統狀態信息等等,進程運行時的一些信息自然也就存在這個分區下。
如上圖所示,系統里運行的每一個進程都會在 /proc 分區下新建一個以自己 pid 命名的目錄,并將本進程的參數存到該目錄下。在該目錄下我們可以通過修改 cgroup 文件暴力的將進程綁定在某個 CPU 上,也可以通過修改 cpuset 文件來優化進程在 NUMA 架構系統上的運行效率,還可以通過修改 oom_adj 文件讓系統 OOM 機制永遠高抬貴手。而 ps、top 這類查看進程的命令恰恰也就是在 /proc 分區下收集信息。換句話說,如果讓 ps、top 命令選擇性失明,也就能達到我們隱藏進程的目的了。
初次嘗試
網上關于 Linux 隱藏進程的方式有很多,比如:
1)強行將進程 pid 變為 0,這種方法存在破綻因此不予考慮。
2)還有一種簡單的方法:系統啟動時會依據 /etc/fstab 文件內容來掛載分區,在 proc 分區掛載參數中加入 hidepid=2 參數后,登陸系統的用戶只能查看到當前用戶啟動的進程的信息。也就是說, tomcat 用戶只能看到屬于 tomcat 用戶進程的信息。
這種方法也存在弊端,罐中的黑客只能看到有限的進程信息,可能就會產生會懷疑。而且如果再存在個最近流行的 tomcat 提權漏洞,那罐子的身份就會瞬間露餡。所以這種方式也不合適,究竟如何才能將我們的問題解決在系統最底層呢?
“肝”起來
對于 Linux 系統來說有著得天獨厚的優勢,我們可以從內核解決一切問題。可是樓主的 C語言 實在是捉急,不由得又開始在互聯網上尋找巨人的肩膀。一篇相關的干貨貼引起了我的注意 http://blog.csdn.net/billpig/article/details/6038330(文末有引用說明),這位作者的思路很明確,在內核中新增兩個信號量,當進程向內核發出 hide 信號時,內核將不會為該進程在 /proc 目錄下生成對應的目錄,從而也就從底層鏟除了進程的信息,即使黑客獲得了 root 權限也無法通過常規手段察覺到蛛絲馬跡。除此之外,新增的unhide信號作用恰好與 hide 信號相反。
通過查閱 crux 官方文檔發現,Demo 中使用的內核版本為 2.6.15。在include/asm-i386/unistd.h 文件中定義新信號量 294 和 295。
系統在接收到我們新定義的 294 和 295 兩個信號量之后需要調用對應的函數來做出相應的動作。在 kernel/sys.c 中我們實現 294 和 295 信號調用的函數。如下圖所示,sys_hide 和 sys_unhide 兩個函數主要功能是修改進程 hide 變量的值。
proc 相關內核代碼位于 fs/proc/base.c 中,在進程相關結構體中新聲明變量hide,通過發送信號量來修改 hide 的值,最后在 base.c 文件 proc_pid_readdir 函數中將 hide 變量的值作為進程是否在 proc 文件系統中體現的依據。
除此之外還有些零碎的步驟,查看上文中的原帖地址即可,不再贅述。
踩坑
新內核編譯完成后本以為可以愉快的開始玩耍了,結果新內核根本無法啟動。通過各種修改啟動參數發現,內核報錯 “kernel too old” ,這就很尷尬了。再次站在巨人的肩膀上發現,確實是因為 kernel too old 。之前缺乏編譯老舊內核的經驗,現在才知道原來 gcc 調用的 libc 不是完全向下兼容的,我們可以通過
file /lib/libc-*.*.so
來查看當前 libc 能夠編譯的最低版本的內核,如下圖所示 libc-2.5.so 可以編譯最低到 2.6.9 版本的內核,如果編譯內核版本低于 2.6.9 的話就會產生 kernel too old 的報錯導致內核無法啟動。
綜上,我們的 libc 版本不可太高,又因為 Demo 中修改的是 i386 架構的內核源碼,所以我選擇的實驗環境為 CentOS 5.11 (32bit) 。
后續
至此,我們就可以成功安裝并啟動 2.6.15 版本內核了,然而事情并沒有結束。系統供外部進程使用的信號量僅有如下 64 個。
所以一般情況下我們的進程只能發出上圖這 64 種信號,我們想要發出 294 和 295 號信號量就必須借助于與系統關系更密切的 C語言 程序完成。
然后通過執行命令
gcc hide.c -fPIC -shared -o hide.so
將 hide.c 編譯為 hide.so 的動態鏈接庫。
以后我們如果有進程需要隱藏的話,只需要在代碼里調用 hide.so 中的 hide 函數即可,同樣 unhide 函數是他的逆過程。如下圖所示,在 Python 中 import ctypes ,使用它調用 hide.so 庫即可實現當前 Python2.7 進程的隱藏與恢復。
至此,已經實現了內核級別的進程隱藏,可以愉快地做一些想做而又怕被別人發現的事情了…
Linux下編譯內核
就跟Windows編譯程序是一樣的思路,Windows 下程序編譯之后是一個可執行的文件,Linux下呢,編譯內核的時候,一般也是從Git上把代碼拖下來,是一個代碼工程,編譯后,生成一個對應格式的文件,這個時候該文件在Linux下 就跟其他文件一樣,就是一個普通的文件。如果你想要把編譯的這個內核在當前的系統中啟動起來的話,由于當前系統中已經有一個正在運行的內核了,一個思路是你可以設置Linux一個變量(類似于Windows的環境變量),將該變量指向這個新編譯好的內核文件,然后這個時候再重啟當前Linux系統,系統啟動時會讀取那個變量的值,然后啟動新的內核。(實驗的時候建議在虛擬機里操作哦)