黑客在攻擊過程中或者對目標網絡實施控制時經常使用域名。我們在做流量分析時不僅要通過流量的指紋特征識別威脅,也可以通過檢測是否解析了惡意域名來判斷網絡中是否存在肉雞。
不直接用威脅情報的原因
公司購買了一批威脅情報數據,其中一項重要的數據就是就是惡意域名列表。
因此可以在客戶流量里面導出DNS解析日志,去跟威脅情報的惡意域名情報直接匹配來找出惡意外鏈。但威脅情報往往有以下的缺陷:
誤報多。威脅情報誤報較多。從我手上的這份數據來看,不少正規中小網站、過期的域名、甚至Alexa排名一千以內的都被列為惡意域名。猜測有可能是網站被掛過黑頁,或者論壇上被傳過帶毒附件,導致整個域名被列入黑名單。而后期維護沒有跟上,導致沒有及時刪除誤報信息。
漏報更多。從威脅情報的性質上來看,越是大范圍/長時間的攻擊的行為、大面積傳播的病毒,越容易被威脅情報所捕獲。反之,針對性的APT特征攻擊則不容易被收錄,造成漏報。而我們公司的客戶主要是政企類,信息更為敏感,更容易被境外黑客盯上并發起針對性攻擊。
不可控。我允許安全系統的誤報和漏報,這畢竟是不可避免的,但我無法接受的是誤報和漏報的不可控。可控的安全系統你知道什么條件下會產生誤報,所以你可以用其它策略去降低誤報;你會知道什么樣的攻擊會被系統遺漏掉,所以可以用其它的安全方案來彌補這部分缺失。而威脅情報雖然內容可讀,但采集過程就是個“黑盒子”,你不知道里面的數據是怎么來的,告警出來的東西也不知道如何降低誤報,更不知道它會遺漏哪些信息。
因此我決定用這批威脅情報作為AI訓練集,學習威脅情報背后的數據特征,通過AI強大的泛化能力,可以減少漏報,并讓安全系統變得可控。
選擇AI維度
選擇維度是很重要一項工作,你做的是人工智能還是人工智障,是神經網絡還是神經病網絡,很大程度上取決于選擇的維度。
Alexa排名
一般情況下,惡意域名不會是流量非常高的網站域名。因此抓取Alexa排名作為維度。
通過接口抓取Alexa排名,如果域名沒有Alexa排名數據,則認為它的排名非常靠后,設置一個非常大的數值:99999999。
如果是多級域名的話,除了抓取多級域名的Alexa,還要抓取主域名的Alexa。這樣便共享了兩個維度。
搜狗rank
搜狗rank和Alexa排名一樣,反應的是網站的規模和流量的大小。不同的是Alexa是越小站越大,搜狗rank是值越大站越大。對于沒有搜狗rank的站點,認為rank為0。
搜狗與百度的收錄數量
一般情況下,惡意域名要多低調就有多低調,不會專門去做SEO的,也不會希望搜索引擎搜到到。
通過某個SEO接口爬取百度和搜狗收錄頁面的數量,對于搜不到的域名,認為收錄數量為0。
必應收錄數量
與“百度和搜狗收錄數量”形成互補,因為必應對于境外域名收錄的更全一些,而百度搜狗對國內收錄的更好一些。本來想用Google的,但是Google太頻繁的抓取會有驗證碼,沒必要把精力花費在破解驗證碼上面。
必應的收錄數量并沒有查詢接口,因此直接用site:domain.com的方式直接抓取收錄數量,相比SEO查詢接口,這樣直接抓取也更準確一些。
網站首頁完整度
很多域名會解析到web服務器,一般情況下,正規域名解析到正常的網站,而惡意域名的解析ip要么沒有web服務,要么是沒有內容的首頁。例如404頁面、Apache默認頁面、空白頁面等等。
那么怎么判斷首頁是不是正常的、完整的呢。最好的方法就是用head less瀏覽器(如phantomjs)去訪問,檢查首頁(包括多媒體資源)的大小,以及媒體資源是否成功加載。這是后續可以優化的一個點。
奈何懶癌晚期,我用了一個取巧的辦法,直接判斷首頁各個媒體標簽的數量。精心設計的首頁肯定有img link script等標簽,滿足的越多,完整度level遍越高。如下圖所示,取level值作為完整度。如果沒開放web服務,則level為0。
圖1 通過標簽給網頁完整度評分
是否是主流域名后綴
一般情況下,普通域名大多數常用.com、.net.、.cn等主流后綴,而惡意域名往往會喜歡注冊小國家域名,如.io、.ru等。
Cn域名一般都要求實名備案,惡意域名很少有cn域名。
地理位置
國內對主機審查比較嚴,除了被黑的肉雞,很少有專門申請國內主機用于黑客行為的。
另外公司客戶主要是政府國企類,向境外的反連本來就是可疑行為,因此把域名的地理位置作為一個維度。
A記錄與CNAME
正規公司如果有注冊企業郵箱的,企業域名一般都會注冊A記錄和CNAME。而惡意域名可能只會注冊A記錄。
如果一個域名既沒有A也沒有CNAME,維度值為0。
待加入維度
WHOIS匿名:惡意域名的whois信息一般都選擇匿名,因此whois信息是否匿名應當作為一個判斷維度。在實際測試過程中,發現很多匿名域名的注冊人、聯系方式等字段,不是為空,而是nameserver或者域名注冊商的聯系方式,因此要建立一個域名商匿名信息的黑名單來判斷是否匿名。維護成本有些高,在第一個版本中暫不加入該維度。
域名更新的頻繁程度:使用域名的ttl值或者歷史解析記錄。一般認為惡意域名的解析更換的比較頻繁。做壞事嘛,總是喜歡打一槍換一個地方。
是否使用CDN:檢測域名是否使用了CDN。CDN一般用于流量比較大的站點進行加速,木馬或shell的C&C域名一般是不會用的。
清洗數據
本文定義的惡意域名為黑客實施攻擊時用的域名,包括但不限于:木馬回連、病毒通訊、反彈shell、SSRF、XSS、DDoS、掛馬、礦機、DNS隧道木馬等等。總之都是黑客攻擊所用。
威脅情報里的域名中有不少我們不需要的,這些信息要人工剔除。例如博彩網站、色情站、垃圾郵件域名。用AI也可以識別:抓取網站內容后直接上樸素貝葉斯,或者走NLP情感分析的套路即可,這不在本文的討論范圍內。還有釣魚站也要剔除,識別釣魚站是另一個話題了。由于擔心誤報影響樣本質量,只取置信度較高的那部分域名。
惡意域名作為正樣本,正常域名作為負樣本,正負樣本比例大約為1:1。
取威脅情報中9月10日的全量惡意域名作為訓練集,另取9月11-17日的增量部分作為測試集。
圖2 整理后的惡意域名樣本
建立模型
筆者訓練了兩種模型,一個是機器學習的代表算法支持向量機SVM,另一個是卷積神經網絡CNN。最后通過多方面對比絕對線上業務使用哪個。
支持向量機
在神經網絡沒火之前,SVM是應用最廣泛的機器學習算法之一,對于線性可分的樣本集識別率很高,那也就注定了本次我們用SVM的效果不會差。
#載入訓練集數據
x_data, y_label = data.load_tran_data()
#數據歸一化。由于有些維度值特別高,如百度收錄量和Alexa值,有些維度則特別低,只有1或者0。因此等比縮小是不可取的,否則收斂特別慢。這里將數據縮小至方差為1均值0的數組
x_data = preprocessing.scale(x_data)
svc = svm.SVC()
parameters = [
{
'C': [0.5, 0.8, 0.9, 1, 1.1, 1.2,1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 3, 5, 7, 9, 11, 13, 15, 17, 19, 30, 50],
'gamma': [0.1, 0.5, 0.6, 0.7, 0.8,0.9, 1, 1.2, 1.3, 1.5, 2, 2.5, 3, 4, 5, 6, 7, 8, 9, 10, 11],
'kernel': ['rbf']
},
{
'C': [0.5, 0.8, 0.9, 1, 2, 3, 4,4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 5, 5.5, 6, 7, 9, 11, 13, 15, 17, 19, 30, 50],
'kernel': ['linear']
}
]
# 使用rbf和linear兩種核函數進行對比,同時使用“爆破”的方式尋找最優參數組合
clf = GridSearchCV(svc, parameters, cv=5,n_jobs=8)
clf.fit(x_data,y_label)
# 打印最優參數組合
print(clf.best_params_)
best_model= clf.best_estimator_
joblib.dump(best_model,"svm2.2.m")
最終確定的最優參數組合是核函數linear和C參數4.2,證明樣本是線性可分的。
在測試集上進行測試,結果準確率97.2% 、召回率97.3%,翻譯成我們熟悉的指標,誤報率為2.8%、漏報率都為2.7%。
卷積神經網絡
卷積神經網絡CNN可以說憑一己之力掀起了近幾年AI熱潮。CNN的特點是強調顯細節的特征,大家熟悉的是CNN在圖像方面的應用,其實一維的文本、二維的圖像、三維的空間、四維的宇宙、五維度的量子態……都可以卷,頗有“大餅卷一切”的趨勢,連三體人的智子都望塵莫及。
好吧,今天有點喝多了。吹牛x的習慣又犯了。
總之,理論上CNN可以擬合任意函數,自然包括線性函數了。
x_data, y_label = data.load_test_data() #載入訓練集并歸一化
x_data = preprocessing.scale(x_data)
x_data = np.expand_dims(x_data, axis=2)
dim = len(x_data[0])
y_len = len(y_label)
model = Sequential()
# 輸入層、卷基層
model.add(Conv1D(4, 3, input_shape=(dim, 1), padding='same', activation='relu', use_bias=True))
# BN算法,使輸出信號規范為“均值0,方差1”,目的是加速收斂
model.add(BatchNormalization())
# 最大池化層
model.add(MaxPooling1D(3))
# 放棄層,防止過擬合
model.add(Dropout(0.5))
# 再來一次卷積,經過BN算法規范化后,進行平均池化
model.add(Conv1D(8, 3, padding='same', activation='relu'))
model.add(BatchNormalization())
model.add(GlobalAveragePooling1D())
model.add(Dropout(0.5))
# 兩層全連接
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# 訓練模型
model.compile(loss='binary_crossentropy',
optimizer='sgd',
metrics=['accuracy'])
model.fit(x_data, y_label, batch_size=20, epochs=600)
model.save("cnn1.22.h5")
該模型在測試集上準確率為97.5%,召回率96.7%。
線上應用
SVM和CNN模型對比數據如下:
模型名稱
準確率
召回率
SVM
97.2% (誤報率2.8%)
97.3% (漏報率2.7%)
CNN
97.5% (誤報率2.5%)
96.7% (漏報率3.3%)
兩種模型相對而言,SVM誤報略高漏報低,而CNN誤報低漏報高。整體而言兩個模型指標相近,SVM略好于CNN,最終線上決定使用SVM作為檢測算法。
SVM的2.8%的誤報率反映在出口流量上有點多,需要配合其他策略如黑白名單降低誤報。后來在實際線上應用中,發現誤報的一部分來自于程序所用的API的域名,這類域名在訓練集中沒有包含。修正過程本文不再贅述。
|