文件監(jiān)控程序V2.0改進(jìn)版(網(wǎng)站防掛馬好手)
目前WAF用戶體驗(yàn)性差
某狗、某網(wǎng)站防護(hù)、某主機(jī)衛(wèi)士,只針對(duì)文件上傳進(jìn)行攔截和防護(hù),先不說(shuō)特征庫(kù)全不全的問(wèn)題,但是,如果攻擊者利用遠(yuǎn)程下載的方式,直接下載到服務(wù)器,或者通過(guò)命令執(zhí)行寫(xiě)進(jìn)去,亦或者通過(guò)其他方式進(jìn)行創(chuàng)建寫(xiě)入的,這些防護(hù)軟件就沒(méi)有辦法管了。而殺軟對(duì)webshell后門(mén)貌似也不是很敏感,所以,這就尷尬了。。擺在管理員面前的,只有手工查殺或者工具主動(dòng)掃描兩條路了。但是,你真的確定你天天會(huì)上去點(diǎn)查殺么?
好吧,我也遇到了這樣的問(wèn)題。一天登錄服務(wù)器千百遍啊,就是為了點(diǎn)下查殺按鈕。
前段時(shí)間給一個(gè)客戶維護(hù)服務(wù)器,服務(wù)器總是被掛馬,裝了個(gè)某狗,然后并沒(méi)有解決問(wèn)題,到不是說(shuō)安全狗不行,是現(xiàn)在很多防護(hù)軟件并不貼心啊。。
解決思路
起初為了解決這個(gè)問(wèn)題,用C#寫(xiě)了個(gè)命令行程序,對(duì)網(wǎng)站目錄進(jìn)行循環(huán)檢測(cè)

但是240G的源代碼,跑完一次需要1個(gè)多小時(shí),也就是說(shuō),在這個(gè)時(shí)間范圍內(nèi),網(wǎng)馬可以存活一定時(shí)間,對(duì)于一個(gè)黑客來(lái)說(shuō),一個(gè)多小時(shí),能做很多事情,所以,這不是我想要的。
之前一直在琢磨,有沒(méi)有一個(gè)好的方式,能夠代替人工去監(jiān)控網(wǎng)站目錄文件,一旦出現(xiàn)webshell,就自動(dòng)進(jìn)行隔離呢。
之前寫(xiě)了個(gè)小工具,但是界面有點(diǎn)小氣。。
從烏云群里要了個(gè)shack2寫(xiě)的,一個(gè)“超級(jí)文件監(jiān)控”工具,貌似是前兩年的,我就順手改了改,實(shí)現(xiàn)了下這個(gè)功能,下面詳細(xì)說(shuō)下正題。
要想實(shí)現(xiàn)這個(gè)功能,首先得解決兩個(gè)問(wèn)題。
1、針對(duì)文件的創(chuàng)建、更改、重命名進(jìn)行監(jiān)控;
2、對(duì)觸發(fā)創(chuàng)建、更改、重命名的文件內(nèi)容進(jìn)行判斷;
先說(shuō)說(shuō)第一個(gè)
我是用C#來(lái)寫(xiě)的
程序開(kāi)始,肯定得先指定文件監(jiān)控路徑
privatevoidbtn_selectDir_Click(object sender, EventArgs e)
????????{
??????????? FolderBrowserDialog dialog =?new FolderBrowserDialog();
??????????? dialog.Description =?"請(qǐng)選擇監(jiān)控文件路徑";
????????????if?(dialog.ShowDialog()?== DialogResult.OK)
????????????{
??????????????? String foldPath = dialog.SelectedPath;
????????????????this.txt_dir.Text = foldPath;
????????????}
C#對(duì)文件進(jìn)行監(jiān)控,得用到FileSystemWatcher類
FileSystemWatcher類提供了Created, Deleted,Rename等事件的監(jiān)控
例如shack2寫(xiě)的文件監(jiān)控源碼,我以創(chuàng)建文件為例:
?privatevoidfileCreate_EventHandle(Object sender, FileSystemEventArgs e)??//文件增刪改時(shí)被調(diào)用的處理方法???????????{ ??????????? add++; ????????????this.lvw_log.Invoke(new updateResult(addNode),?new Object[]?{ e,?"創(chuàng)建"?}) ????????}
當(dāng)觸發(fā)了創(chuàng)建文件操作時(shí),將內(nèi)容輸出。
當(dāng)然了,除了創(chuàng)建以外,還可以用WatcherChangeTypes來(lái)注冊(cè)其他事件。
因?yàn)閟hack2已經(jīng)共享了源碼,這里就不再累述。
文件監(jiān)控這里,百度有很多現(xiàn)成的方法,這個(gè)完全可以復(fù)制粘貼的,重點(diǎn)是第二個(gè)問(wèn)題,如何對(duì)觸發(fā)行為的內(nèi)容進(jìn)行處理。
大致處理思路是:
觸發(fā)文件狀態(tài)(如:創(chuàng)建)---->對(duì)觸發(fā)該狀態(tài)的文件進(jìn)行檢查------->確定文件內(nèi)容是否存在可疑------->不處理or隔離
知道了思路,寫(xiě)起來(lái)就相對(duì)簡(jiǎn)單些了。
先創(chuàng)建個(gè)ScanHelper類
這個(gè)類,主要是寫(xiě)掃描方法和所關(guān)聯(lián)的特征碼,也就是表達(dá)式。以及文件的處理措施。
這里的表達(dá)式,可以根據(jù)需求來(lái)自己設(shè)定,也可以將特定腳本類型的特征碼進(jìn)行歸類。
c
onst string sPattern =?"webshell特征表達(dá)式";
????????conststring sString =?"HTML、JS代碼特征表達(dá)式2";
????????conststring sjm =?"加密一句話或加密webshell特征表達(dá)式3";
????????static Regex RegPattern =?new Regex(sPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
????????static Regex RegString =?new Regex(sString, RegexOptions.Compiled | RegexOptions.IgnoreCase);
????????static Regex Regsjm =?new Regex(sjm, RegexOptions.Compiled | RegexOptions.IgnoreCase);
????????///<summary>
????????///?日志文件路徑
????????///</summary>
??????? public static string LogPath { get; set;?}
????????///<summary>
????????///?臨時(shí)文件目錄
????????///</summary>
??????? public static string TempPath { get; set;?}
????????///<summary>
????????///?掃描文件
????????///</summary>
????????///<param name="filePath"></param>
表達(dá)式寫(xiě)清楚了,然后就是寫(xiě)文件打開(kāi)的方法和文件內(nèi)容的判斷
這里使用 FileInfo NextFile = new FileInfo(filePath);
判斷文件是否存在 if (NextFile.Exists)
string FileText = File.ReadAllText(Path.GetFullPath(filePath));//讀取文件內(nèi)容
if (RegString.IsMatch(FileText)?|| RegPattern.IsMatch(FileText)?|| Regsjm.IsMatch(FileText))?//如果存在危險(xiǎn)字符
如果存在設(shè)定好的字符,則處理文件
這里呢,當(dāng)然不能直接刪除,通過(guò)moveTo的方式移動(dòng)到別的地方來(lái)處理
NextFile.MoveTo(Path.Combine(TempPath,?NextFile.Name));
只是判斷文件還不行,如果文件出現(xiàn)重復(fù),依然會(huì)出現(xiàn)問(wèn)題,這里,需要對(duì)文件進(jìn)行一個(gè)處理
比如說(shuō),當(dāng)發(fā)現(xiàn)1.asp文件存在問(wèn)題,我們處理以后,下次再出現(xiàn)1.asp文件時(shí),則會(huì)報(bào)錯(cuò)。。。
這里,我們需要用到下面的方法來(lái)處理下文件重復(fù)的問(wèn)題。
//如果自身文件在運(yùn)行,不能直接覆蓋,需要重命名之后再移動(dòng)?
if?(File.Exists(Path.Combine(TempPath, NextFile.Name)))
????????????????????????{
????????????????????????????if?(File.Exists(Path.Combine(TempPath, NextFile.Name +?".bak")))
????????????????????????????{
??????????????????????????????? File.Delete(Path.Combine(TempPath, NextFile.Name +?".bak"));
????????????????????????????}
??????????????????????????? File.Move(Path.Combine(TempPath, NextFile.Name), Path.Combine(TempPath, NextFile.Name +?".bak"));
????????????????????????}
??????????????????????? NextFile.MoveTo(Path.Combine(TempPath, NextFile.Name));
上面代碼的意思是,當(dāng)發(fā)現(xiàn)文件重復(fù)了,在新處理的文件名加上.bak來(lái)做區(qū)分,當(dāng)再次發(fā)現(xiàn).bak也重復(fù)的時(shí)候,直接進(jìn)行刪除操作。
在處理完這些問(wèn)題后,有一個(gè)核心的問(wèn)題,線程占用的情況。。
也就是說(shuō),在windows文件系統(tǒng)中,你創(chuàng)建一個(gè)文件時(shí),會(huì)觸發(fā)created和changed兩種操作,你修改文件內(nèi)容時(shí),也同樣會(huì)觸發(fā)這兩個(gè)操作,這樣就會(huì)沖突了。這不是關(guān)鍵,關(guān)鍵問(wèn)題時(shí),在執(zhí)行監(jiān)控時(shí),你還要對(duì)文件內(nèi)容進(jìn)行判斷和處理,這樣就很容易發(fā)生錯(cuò)誤。
我問(wèn)過(guò)好多C#大牛,都說(shuō)不好解決,最好的辦法,是用C++來(lái)寫(xiě),通過(guò)底層的方式來(lái)處理,但是坑的是,我還沒(méi)學(xué)C++,C#也是才學(xué)了2個(gè)月吧,如果這樣就放棄了,那整個(gè)程序就白寫(xiě)了。。
后來(lái)琢磨了好幾天,想到了一個(gè)不是解決辦法的解決辦法。
用try catch和Thread.Sleep來(lái)解決這個(gè)問(wèn)題。
示例代碼:
private void fileCreate_EventHandle(Object sender, FileSystemEventArgs e)??//文件增刪改時(shí)被調(diào)用的處理方法??
????????{
??????????? add++;
????????????this.lvw_log.Invoke(new updateResult(addNode),?new Object[]?{ e,?"創(chuàng)建"?});
??????????? Thread.Sleep(20000);
????????????try
????????????{
????????????????if?(File.Exists(e.FullPath))?//是文件
????????????????{
??????????????????? string type = e.ChangeType == WatcherChangeTypes.Created ??"重命名"?:?"改名";
??????????????????? lock (this)
????????????????????{
??????????????????????? ScanHelper.ScanFile(e.FullPath);
????????????????????}
????????????????}
????????????}
????????????catch
????????????{
??????????????? Thread.Sleep(20000);
????????????????if?(File.Exists(e.FullPath))?//是文件
????????????????{
????????????????????//string type = e.ChangeType == WatcherChangeTypes.Created ??"重命名"?:?"改名";
??????????????????? lock (this)
????????????????????{
??????????????????????? ScanHelper.ScanFile(e.FullPath);
????????????????????}
????????????????}
????????????}
????????}
try執(zhí)行,當(dāng)文件觸發(fā)了創(chuàng)建操作,就執(zhí)行文件內(nèi)容判斷和處理的方法
如果線程出錯(cuò),則通過(guò)catch的方式來(lái)重復(fù)執(zhí)行,但是catch不再判斷文件狀態(tài),只是判斷是文件,然后對(duì)內(nèi)容進(jìn)行檢查,發(fā)現(xiàn)問(wèn)題,則進(jìn)行下一步處理操作。
這里用到了?? Thread.Sleep(20000);睡眠時(shí)間,是為了讓程序在判斷的時(shí)候,有足夠的時(shí)間釋放線程,免得出現(xiàn)沖突,而導(dǎo)致程序崩潰。

當(dāng)然了,界面我沒(méi)改動(dòng),還是shack2得界面,我只是在界面上加入了一個(gè)隔離區(qū)操作。
我把軟件貼出來(lái),大家可以玩玩。
使用方法:監(jiān)控目錄輸入你要監(jiān)控的網(wǎng)站目錄,點(diǎn)擊“開(kāi)始監(jiān)控”。
隔離區(qū)默認(rèn)設(shè)置在C:TEMP目錄下,凡是被隔離的文件,均在該目錄下。
當(dāng)創(chuàng)建文件、修改文件或者重命名文件時(shí),內(nèi)容中如果含有webshell的關(guān)鍵詞或函數(shù),則會(huì)進(jìn)行處理。
補(bǔ)充說(shuō)明:
監(jiān)控后綴和跳過(guò)后綴完全可以忽略,因?yàn)檫@個(gè)監(jiān)控,是針對(duì)所有文件內(nèi)容的,所以無(wú)所謂監(jiān)控哪個(gè)或不監(jiān)控哪個(gè),管它是畸形文件名還是war,都會(huì)去進(jìn)行檢測(cè)。
第一次執(zhí)行時(shí),執(zhí)行可能會(huì)有些緩慢,當(dāng)執(zhí)行完一次后,會(huì)建立索引,后面速度則會(huì)有提升。。
軟件中已寫(xiě)好了近百種特征,共170個(gè)webshell樣本,雖然不一定全,但是至少還是有些查殺能力的。
一般同文件名,首次上傳到被處理,存活周期在10-20秒之間。
當(dāng)文件監(jiān)控到“修改”操作時(shí),其實(shí)就已經(jīng)執(zhí)行了處理操作,只是我把顯示的時(shí)間延遲了20秒。

不管什么軟件,誤殺的情況是不可避免的,假如在使用該軟件的時(shí)候,軟件匹配到特征,就會(huì)將文件隔離到c:temp目錄下,并不會(huì)直接刪除
當(dāng)你發(fā)現(xiàn)文件是誤刪除的,可以點(diǎn)擊“停止監(jiān)控”,將文件根據(jù)軟件下方顯示的位置,還原到原來(lái)的目錄,再點(diǎn)擊監(jiān)控就可以了,這樣,這個(gè)文件就不會(huì)查殺了,除非這個(gè)文件的名字、內(nèi)容有了變動(dòng),則會(huì)繼續(xù)觸發(fā)規(guī)則。
因?yàn)檫@段時(shí)間要做移動(dòng)APP安全審計(jì)的工作,C#暫時(shí)先放放,準(zhǔn)備開(kāi)始學(xué)移動(dòng)APP這塊,功能暫時(shí)不做改動(dòng)了。
后面找時(shí)間修改,計(jì)劃是把功能修改的更為簡(jiǎn)潔,把監(jiān)控的記錄完善下,增加白名單區(qū)域等小功能。
有什么問(wèn)題,歡迎郵件或站內(nèi)信反饋。
歡迎測(cè)試。