用機器學習識別隨機生成的C&C域名

wyzsk發表於2020-08-19
作者: phunter · 2015/05/26 10:16

0x00 前言


本文用識別由域名生成演算法Domain Generation Algorithm: DGA生成的C&C域名作為例子,目的是給白帽安全專家們介紹一下機器學習在安全領域的應用,演示一下機器學習模型的一般流程。機器的力量可以用來輔助白帽專家們更有效率的工作。

本文用到的演示資料集和python演示程式碼請參見 https://github.com/phunterlau/dga_classifier 關於編碼和行文風格過於倉促的問題,請不要在意這些細節,如果有相關問題可以微博上@phunter_lau,大家互相交流進步。

0x01 為什麼要機器學習?


DGA生成C&C域名的辦法常見於一類botnet,比如conficker,zeus之類,他們的方法是用一個私有的隨機字串生成演算法,按照日期或者其他隨機種子(比如twitter頭條),每天生成一些隨機字串域名然後用其中的一些當作C&C域名。在他們的bot malware裡面也按照同樣的演算法嘗試生成這些隨機域名然後碰撞得到當天可用的C&C域名。

多數關於C&C域名的研究在於部分特性比如域名的快速轉換(fast flux)或者是分析malware的原始碼找出隨機演算法(或者像zeus這樣原始碼洩漏)。而對於一個白帽子專家來說,可能把域名給他看一下他也就能按照經驗猜出來大概,比如我們可以猜猜下面哪些域名可能是C&C域名:

fppgcheznrh.org
fryjntzfvti.biz
fsdztywx.info
yahoo.com.
baidu.com.
dmiszlet.cn
dmgbcqedaf.cn
google.com.
facebook.com.
frewrdbm.net

上面的例子混合了常見合法域名和confickr生成的一些C&C域名,白帽可以用多年人生的經驗輕鬆分辨,但大量隨機域名由機器生成,我們不能僱傭十萬個白帽專家挨個檢測,就好比觀眾朋友們可能看完上面10個域名就已經眼花了。

機器可以利用人類的經驗來完成這樣的重複性工作,比如分類(Classification)任務就是判別一個域名是不是C&C,判別一個狗咬不咬人,這些Yes or No的任務都是分類任務。分類任務歸於是機器學習裡面的監督學習(supervised learning),基本套路就是:

  1. 提供訓練資料集
  2. 把人類的經驗表示為特徵(feature)把資料集轉換成特徵向量(feature vector)
  3. 利用這些資料集和他們的特徵向量訓練合適的分類器(Classifier,不用擔心,這一步有無數開源工具)
  4. 評價分類效果,比如精度、召回率等等,並交叉檢驗分類效果 (Cross-validation)。

機器學習並沒有什麼神秘的技術,它本質上是用多個變數進行綜合決策,機器在這多個變數的約束下用數值計算方法找出近似最優解。比如在這個例子裡,白帽專家的經驗就是“這些域名看起來像是隨機的”。如果把“看起來”表示稱機器能計算的多個變數的特徵,機器就能幫助白帽專家判別哪些域名可能是C&C域名。

0x02 資料收集


分類的任務需要告訴機器他用來學習的正例(positive samples)和反例(negative samples),在這裡正例就是C&C域名,反例就是正常的合法域名。

正例和反例的涵蓋範圍和具體問題有關,具體到本文的例子,我選擇Conficker(ABC三種混合)當作正例,Alexa前10萬當作反例。Conficker的演算法早在多年前公開,這裡純粹是演示目的並沒有產品化的意義,對於實際的工作如果想讓模型有更廣泛的適用性,需要在訓練資料集里加入其他種類的C&C域名以及其他合法域名,然後用類似的辦法訓練得到一個更廣泛適用的模型。

整理好的資料集在:conficker_alexa_training.txt 格式是第一列為域名(字串),第二列為它的標記(0代表反例,1代表正例)

0x03 特徵工程


這幾乎是整個文章最有值得讀的部分。如果能把人類的經驗用數量化表達給機器,機器就能學習到人類的經驗,而特徵(feature)就是人類經驗的數量化。特徵工程是個反覆迴圈的過程,一開始我們找到基線特徵,用分類演算法計算並評價結果,如果結果不能達到預期,再回頭來加入新的特徵幫助更好的分類。

基本特徵:隨機性和熵

我們可以想一下,具體為什麼C&C域名看起來和別的合法域名比如google.com不一樣呢?因為它看起來隨機,所以第一個特徵就是找一個數量來描述它的隨機性。我們用Shannon熵http://en.wikipedia.org/wiki/Entropy_(information_theory) 表達域名裡各個字元出現的隨機性,因為越是隨機熵值越高:

#!python
from collections import Counter
count = Counter(i for i in main_domain).most_common()
entropy = -sum(j/f_len*(math.log(j/f_len)) for i,j in count)#shannon entropy

Shannon熵可以很好的判別fryjntzfvti.bizgoogle.com/qq.com之間的區別,因為前者用了很多不重複字母而qq.com的重複字幕比較多。但是很多合法域名的熵值和C&C域名之間的並非是絕對差距,比如baidu.com也是五個不重複的字母,這單個特徵不足以最終決策,我們還一些其他高階的特徵。

高階特徵:還有什麼能表達隨機性呢

合法域名一般比較好念出來,C&C域名不好念

思考一下合法域名和C&C域名的目的,就可以想到:合法域名為了讓人類記住會選一些好念(pronounceable)的域名,比如 google yahoo baidu等等有母音字母之類好唸的,而C&C域名為了隨機性就不太好念,比如fryjntzfvti.biz。域名裡母音字母佔的比重可以是個很好的特徵。

“好念“這個概念也可以有另外一個高階一些的特徵,叫做gibberish detection,判斷一個字串是不是能用人類的語言念出來,比如google就不是一個英文單詞但是朗朗上口。這背後是一個基於馬爾可夫鏈的模型,具體細節可以參見 https://github.com/rrenaud/Gibberish-Detector

連續 vs 分散

透過進一步觀察我們可以發現,C&C域名的隨機性也表現在連續出現的字母和數字上。一般隨機生成的域名都不會出現大段連續的數字或者連續出現相同的字母。同時因為英文字母分佈裡子音字母遠多於母音字母,C&C更可能連續反覆出現子音字母,而合法域名為了好念多是母音子音交替。這些都是不容易想到但是容易計算的特徵,程式碼並不複雜。

還有什麼?n-gram 的平均排名!

這是我個人認為比較巧妙的想法。

對於字串文字的機器學習,n-gram (unigram(單字)bigram(相鄰雙字)trigram(相鄰三字))常常能提供重要的特徵。舉例來說,fryjntzfvti.biz的域名的bigram分解是以下12個:

^f,fr,ry,yj,jn,nt,tz,zf,fv,vt,ti,i$

這裡^$代表字串的開頭和結尾。觀眾朋友可以自行計算trigram當作練習。bigram/trigram本身出現的頻率也可以當作特徵,但是對這個問題來說,bigram本身可能有 (26+2)^2=784種組合,trigram就有21952種組合,特徵向量的長度太長而我們的資料約有25萬組,如果把他們本身當作特徵,模型訓練的速度很慢。(比較熟悉機器學習的觀眾朋友可能會提示用PCA等降維方法,我實際實驗表明降維到20維左右效果也不錯,在此不當作本文內容,請有興趣的觀眾朋友自己實驗一下。)

C&C域名的隨機演算法產生的bigram和trigram比較分散,而合法域名喜歡用比較好念好見的組合。如果把正例反例出現的bigram按照出現頻率進行排序會發現,合法域名的bigram在頻率排序裡的位置比較靠前,而隨機C&C域名產生的比較分散的bigram/trigram基本上頻率都很低,所以bigram/trigram的平均排名也可以很好的區分C&C和合法域名。

扯一些額外內容。n-gram的分析方法也常用於malware的程式碼和二進位制碼的自動分析,比如ASM裡面每個指令當作一個gram,指令的組合可能對應於一些可疑行為。靠人工找這些可能可疑行為對應的指令組合十分麻煩,但是機器就適合做這些繁瑣的事情啊,只要把所有n-gram扔給機器做分類,最後機器會給出特定組合的權重,就能找到這些對應的指令對了。二進位制程式碼的分析也有類似方法,參見最近Kaggle的malware分類比賽的獲勝報告(參考文獻3)。有白帽專家可能會問,有些可疑指令對可能距離比較遠怎麼辦?這種情況就是skip-gram分析,建議谷歌搜尋相關關鍵詞,這裡就不多說撐篇幅了。

究竟還能再挖出來什麼特徵呢?

特徵工程就好像Taylor Swift的胸一樣,你只要需要,用力擠努力擠還是有的。如果按照反例Alexa前10萬名訓練隱含馬爾可夫鏈,計算一下從A_iA_i+1轉換的機率。這個轉換機率的分佈對於正例有一些區別,也可以用來幫助區分。具體關於馬爾可夫鏈相關知識請參見http://en.wikipedia.org/wiki/Markov_chain (解釋起來背後的原因篇幅比較大,就只貼一下這個特徵的分佈圖,但是不要害怕,看示例程式碼裡的實現其實很簡單,只是計算轉移矩陣而已)

markov example

領域特徵:安全專家的領域知識

對於C&C域名,不只是隨機性,其他白帽專家才知道的領域知識也會提供重要的特徵。

比如域名所在的ccTLD可以當作特徵。我們知道多數情況下.com的域名申請又貴又要稽核,所以現在很多C&C不會選擇.com,反而會選一些稽核不嚴的比如.biz .info .ru .ws以及最近爆發的.xyz之類的ccTLD都是C&C重災區。中國的白帽專家也可能知道.cn現在申請都得備案,所以C&C也不太可能用.cn的根域名當作C&C。值得提醒的是,這些情況並非100%確定,比如C&C可能找到一個cn域名的下級域名當C&C而主域名已備案,這些需要機器綜合考慮其他特徵來判斷。ccTLD這樣的類別特徵(categorial feature)在使用的時候需要編碼變成 is_biz=0/1, is_ws=0/1這樣展開的0/1向量,這個方法叫做OneHotEncoder。實際的模型結果也顯示出來.biz .info之類的ccTLD對C&C域名的判斷佔的重要性比重很大。

還有一些看似比較無聊但是很有價值的知識:比如C&C域名現在越來越長,因為短的域名都被搶光了,所以域名長度也可以是重要的特徵。更多這些特徵需要安全專家加入自己的領域知識來得到,專家的領域知識在機器學習裡的重要程度幾乎是第一位的。

Talk is cheap, show me the code!

特徵工程部分的程式碼流程如下

  1. tld_appender.py (解析每個域名的ccTLD)
  2. gram_freq_rank.py (生成bigram/trigram的基準排名)
  3. feat_n_gram_rank_extractor.py (得到bigram/trigram排名)
  4. feat_extractor.py (各個特徵計算的函式,需要包含https://github.com/rrenaud/Gibberish-Detector
  5. feat_normalizer.pyfeat_vectorizer.py (特徵歸一化向量化)

最後會輸出 vectorized_feature_w_ranks_norm.txt的歸一化向量化的結果檔案。對於25萬組資料,這個檔案比較大,就沒有包含在github程式碼庫裡了,請自行生成。

這些是上面談到的各個特徵在在程式碼裡的入口,完整的程式碼請參見github:

#!python
#in feat_extractor.py
f_len = float(len(main_domain))
count = Counter(i for i in main_domain).most_common()#unigram frequency
entropy = -sum(j/f_len*(math.log(j/f_len)) for i,j in count)#shannon entropy
unigram_rank = np.array([gram_rank_dict[i] if i in gram_rank_dict else 0 for i in main_domain[1:-1]])
bigram_rank = np.array([gram_rank_dict[''.join(i)] if ''.join(i) in gram_rank_dict else 0 for i in bigrams(main_domain)])#extract the bigram
trigram_rank = np.array([gram_rank_dict[''.join(i)] if ''.join(i) in gram_rank_dict else 0 for i in trigrams(main_domain)])#extract the bigram

#linguistic feature: % of vowels, % of digits, % of repeated letter, % consecutive digits and % non-'aeiou'
vowel_ratio = count_vowels(main_domain)/f_len
digit_ratio = count_digits(main_domain)/f_len
repeat_letter = count_repeat_letter(main_domain)/f_len
consec_digit = consecutive_digits(main_domain)/f_len
consec_consonant = consecutive_consonant(main_domain)/f_len

#probability of staying in the markov transition matrix (trained by Alexa)
hmm_prob_ = hmm_prob(hmm_main_domain)

一個技巧是,因為有ccTLD這個離散特徵,我們把所有的特徵用字典dict儲存,然後用scikit-learn的DictVectorizer將特徵向量化:

#!python
#in feat_vectorizer.py
from sklearn.feature_extraction import DictVectorizer
vec = DictVectorizer()
measurements = [feat_dict for domain,cla,feat_dict in feat_table]
feature_list = vec.fit_transform(measurements).toarray()

值得再次提醒注意的是,特徵工程是個反覆的過程,不用在一開始就找到足夠多的好的特徵。在我的實際實驗裡,後面的高階特徵也花了好幾天的時間反覆思考得到。

0x04 模型選擇和訓練


判斷一個域名是不是C&C域名這樣Yes or No任務是分類任務。對於分類任務,常見的模型一般是Logistic Regression啊決策樹啊之類的。這裡我選擇SVM支援向量機作為分類演算法,關於SVM的理論知識可以前往 http://en.wikipedia.org/wiki/Support_vector_machine 閱讀了解,而我們用起來它只需要:

    #!python
from sklearn.svm import SVC
classifier = SVC(kernel='linear')

就可以建立一個SVM的線性分類演算法了,這個演算法讀入之前特徵工程產生的特徵並作出預測,就是這麼簡單:

#!python
probas_ = classifier.fit(X_train, y_train).predict(X_test)

機器學習的這些分類演算法模型做的工作可以認為是利用每個例子的N個特徵向量的權衡考慮得到預測結果。不同的分類演算法的權衡考慮方法不一樣,但是從總體來看是個多變數的平衡。因為本文重點在於特徵工程,而關於模型的訓練原理以及背後的數學可以說很多,在此就僅把模型訓練當作黑盒處理,有興趣的觀眾朋友們可以多多研究scikit-learn關於分類演算法的教程 http://scikit-learn.org/stable/tutorial/statistical_inference/supervised_learning.html。對於模型引數的選擇可以透過交叉驗證(Cross validation)來優選最適合的引數,這一點請當作進階自行閱讀。

一些個人的意見就是,各種分類演算法的效果其實差不太多,區別主要在於適用情況上,如果發現一種分類演算法的結果明顯不滿意,可能是因為它不適合這個問題(比如樸素貝葉斯就不適合特徵相關度高的),也有可能這個演算法需要的特徵還沒被挖掘出來,需要回到特徵工程上面再深入挖一些有利於區分正例反例的特徵。總的來說,特徵工程弄好了就定好了分類效果的上限,模型只是盡力接近這個上限,多花時間在搞特徵上最能提高。除非是深度學習這樣帶特徵學習的猛獸,這就是題外話了。

0x05 評價函式和交叉驗證


為了評價機器的預測效果,我們需要量化的評價函式。對於“判斷域名是否為C&C”的問題可以考慮:

  1. 對於真正的C&C域名能抓住多少,多少漏網?(true positive vs false negative用召回率recall衡量)
  2. 如果一個域名是合法域名,會不會當作C&C誤殺?(true positive vs false positive用精度precision衡量)

關於precision(精度)和recall(召回率)的相關介紹,請參見wikipedia http://en.wikipedia.org/wiki/Precision_and_recall 計算precision和recall的程式碼很簡單:

#!python
from sklearn.metrics import precision_recall_curve
precision, recall, thresholds = precision_recall_curve(y_truth, probas_)

在訓練模型的時候,測試資料對我們並不可見(否則就不叫預測了,就是作弊啊),那麼問題就是,只有訓練資料我們怎麼評價我們的模型的預測效果呢?一個好用的技術叫做交叉驗證(Cross-validation),基本方法就是假裝看不見一小部分訓練資料(一般是1/5),用剩下的4/5資料訓練模型,看看這4/5資料訓練的模型對那1/5的資料的預測能力,因為那1/5的資料我們知道它裡面那些域名是C&C哪些不是,這樣就可以計算precision和recall。為了公平起見,一般我們會把資料隨機洗牌,然後做多次交叉檢驗,這叫做K-fold cross validation。

對於每次的交叉檢驗,我們可以畫出precision vs recall的曲線圖,從中可以看到precision和recall的相對平衡,比如下圖:

AUC example

我們可以看到,如果要保持0.8左右的recall召回率,precision可以達到90%以上,但是如果要召回率達到100%,那precision只能有15%左右了。

域名的分類效果和域名的長度也有關係,我們可以畫出來平均的Accuracy和域名長度的關係圖:

accuracy example

正如大家想到的一樣,對短的域名分類效果一般,因為短域字串本身的資訊不如長域名豐富。不過現在C&C的域名越來越長,如果只看長於12個字元的C&C域名,預測效果還是很不錯的。

值得提醒的是,測試資料的預測誤差比交叉檢驗得到誤差不同(一般測試的誤差要大得多)。在測試資料上的誤差需要深入的瞭解和除錯,這些是進階內容,請今後自己在實戰裡摸索,很有挑戰性喲。

0x06 總結


這篇文章用域名字串特徵判別C&C的任務,簡單介紹了一下機器學習在安全領域的一個小小應用,主要為了演示一下一個機器學習任務的基本流程。在機器學習裡,特徵工程幾乎是最重要的部分,在這篇文章裡面我們深入挖掘了“看起來像”這個分類特徵的若干種可以量化的表達方式,有些特徵需要反覆思考得到,有些特徵需要領域知識。對於輸入的一組很好的特徵,基本上各種分類器都能有不錯的表現,我們用精度和召回率評價模型的分類效果,看看有哪些C&C我們放過了,哪些合法域名我們誤殺了,用交叉驗證的方式判斷模型效果,並根據這些評價來調整模型。更簡單一點就是這個套路:

  1. 準備資料集
  2. 抽取特徵(幾乎是最重要的工作)
  3. 選取合適的模型(絕大多數情況都有開源的程式碼)
  4. 設計評價函式並交叉驗證(設計一個適合自己問題的評價)
  5. 對測試資料預測

在實際工作裡,2-5這幾步可能需要反覆完善:用基準特徵訓練模型,用交叉檢驗搜尋選擇最優模型引數並評價,如果評價不滿意,繼續新增新的更好的特徵,如果新增特徵還不滿意,就再繼續調整模型引數新增新的特徵,擠一擠總是有的嘛。

在安全領域很多方面都可以用機器學習來輔助,比如從日誌裡挑出可疑行為,在exe檔案裡找出malware的hook插入點之類的,都遵循上面類似的套路。這篇文章想起到拋磚引玉的作用,給各位白帽專家做參考,看看機器學習在你們的特定專業領域裡應用。在我的實際工作裡,機器學習的多方面技術都有應用,比如可以用clustering聚類的辦法把可能的botnet聚集在一起,用遺傳演算法反解出雜湊攻擊的隨機數生成演算法,用深度學習做(此處被公司要求馬賽克掉)的一些研究。

提醒一點的是,機器學習的主要目的是簡化和輔助而不是取代專家的工作,它可以減輕白帽專家批次處理一些繁複複雜問題的負擔,讓專家集中精力到更重要的工作上,它的預測判斷基於一定的前提條件,預測的結果是0-1之間的機率。對於一個可能C&C域名是殺是放,還是取決於執行最後決策的人類白帽專家。你問我支援不支援機器的預測,我是支援的,但是一切還都要按基本法來,對吧。

安全的工作好比大海撈針,機器學習可能就是幫我們撈針的磁鐵,歡迎大家加入機器學習的行列。

0x07 深入閱讀和參考文獻


從域名的“看起來像”這個特徵來判斷C&C域名的想法受到這片文章的啟發 http://www.sersc.org/journals/IJSIA/vol7_no1_2013/5.pdf

如果想繼續學習一些關於機器學習的知識,建議在Coursera上學習史丹佛的機器學習入門課 https://www.coursera.org/course/ml

機器學習在安全領域有很多應用,比如Kaggle的Malware分類任務,機器可以分析反編譯的ASM和原始binary自動判別malware的種類並取得很好的效果,幾位獲勝者的報告很值得研究一下https://www.kaggle.com/c/malware-classification

在Kaggle比賽和本文中用到的機器學習軟體包 scikit-learn 的主頁在http://scikit-learn.org/stable/ (已被牆)

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章