與逆向還原實(shí)戰(zhàn):從Base64到RSA混合加密)
搞惡意代碼分析這些年我最深的感受就是一個(gè)樣本能不能被真正看懂一半看行為另一半看通信。攻擊組織的技術(shù)迭代最明顯的往往不是漏洞利用那些花活而是通信加密這套基礎(chǔ)設(shè)施的升級(jí)。最近我在復(fù)盤一個(gè)業(yè)內(nèi)常被稱為蔓靈花的攻擊組織時(shí)明顯感覺到它的武器庫又一次更新了早年間那種拿Base64包一層就敢上線的時(shí)代早就過去取而代之的是AES加RSA混合加密、再包一層魔改TLS的組合。這篇文章把我梳理出來的加密通信演進(jìn)路徑、逆向還原方法和完整實(shí)操環(huán)境搭建過程都寫出來。無論你是剛進(jìn)入惡意代碼分析方向的新人還是已經(jīng)被加密C2流量折磨過一陣子的分析人員里面提到的定位算法、提取密鑰、解密驗(yàn)證的套路都可以直接拿來用。我會(huì)盡量把工具選型背后的理由講清楚也會(huì)把容易踩的坑單拎出來說省得你繞遠(yuǎn)路。1. 為什么攻擊武器都在升級(jí)加密通信1.1 C2通信在惡意軟件里到底扮演什么角色C2是Command and Control的縮寫說白了就是木馬回連的指揮鏈路。樣本被投放到目標(biāo)機(jī)器后會(huì)定期向攻擊者控制的服務(wù)器發(fā)送心跳同時(shí)接收指令、回傳文件整套遠(yuǎn)程控制都靠這條通道維持。你可以把它想象成工蜂回巢命令從這里下發(fā)數(shù)據(jù)從這里運(yùn)走。通道一斷即使樣本駐留得再深也只是個(gè)癱瘓的潛伏者。所以攻擊者對(duì)C2通信的加密投入從來都是不遺余力的。早期不少攻擊武器走的是明文HTTP上線后直接在日志里留下請(qǐng)求URL、上傳內(nèi)容安全審計(jì)人員掃一眼流量就能看出問題。如今的主流攻擊組織基本都完成了幾輪升級(jí)最常見的做法是把指令放在加密字段里用對(duì)稱加密保護(hù)數(shù)據(jù)內(nèi)容再用非對(duì)稱加密或者TLS保證密鑰傳輸環(huán)節(jié)不會(huì)被中間人直接拿走。從防御側(cè)來看加密通信反而是分析工作的重要突破口。攻擊者為了加密必須把算法、密鑰、初始化向量這些邏輯寫進(jìn)樣本里。這些東西只要有一處能被提取整條C2鏈路就能被還原。換句話說加密不是分析工作的終點(diǎn)而是一個(gè)關(guān)鍵切入點(diǎn)。1.2 武器庫升級(jí)是被防御側(cè)倒逼出來的攻擊者升級(jí)加密通信不是為了炫技更多是防御側(cè)壓力導(dǎo)致的必然選擇。近些年威脅情報(bào)共享機(jī)制越來越成熟一個(gè)樣本的特征被公開后全球的安全設(shè)備能在很短時(shí)間內(nèi)同步識(shí)別規(guī)則。再加上沙箱動(dòng)態(tài)分析、流量側(cè)機(jī)器學(xué)習(xí)建模、JA3指紋追蹤這些手段的普及早期那種“編碼代替加密”的偽裝方式幾乎全部失效。蔓靈花這類組織的應(yīng)對(duì)方式很有代表性。它們的加密通信經(jīng)歷了從簡(jiǎn)單編碼到對(duì)稱加密、再到非對(duì)稱加密加自定義TLS的完整演進(jìn)。每次升級(jí)都不是推倒重來而是在舊方案基礎(chǔ)上疊加新保護(hù)這也是為什么老樣本和新樣本經(jīng)常能看出明顯的同源特征。對(duì)于分析人員來說理解這條演進(jìn)路線等于拿到了一個(gè)從易到難的解密梯度從最弱的舊樣本入手逐步順藤摸瓜找到新樣本的分析思路。值得強(qiáng)調(diào)的是加密升級(jí)是有成本的。攻擊者要維護(hù)私鑰、要保證算法在各版本W(wǎng)indows上穩(wěn)定運(yùn)行、還要讓加密流量看起來不像惡意流量。這些成本會(huì)真實(shí)反映在樣本的代碼結(jié)構(gòu)和執(zhí)行邏輯里也是我們逆向時(shí)可以利用的線索。2. 武器庫里的加密通信演進(jìn)圖譜2.1 第一代明文HTTP與Base64偽加密最早一批樣本的通信方式簡(jiǎn)單得有些可愛。樣本上線后向C2域名發(fā)起HTTP請(qǐng)求參數(shù)經(jīng)過Base64編碼就當(dāng)作加密。有些甚至只是自定義了Base64字符表換了個(gè)排序就當(dāng)成安全方案。這種“偽加密”的識(shí)別成本極低。分析時(shí)可以先用熵值看數(shù)據(jù)特征Base64的字符集集中在大小寫字母、數(shù)字和“/”分布沒那么隨機(jī)再看請(qǐng)求參數(shù)的結(jié)構(gòu)Base64編碼后經(jīng)常以“”結(jié)尾做Padding這個(gè)特征在流量里相當(dāng)顯眼。如果樣本用了自定義字符表比對(duì)標(biāo)準(zhǔn)Base64字符集就能定位到替換表的位置。拆解這類樣本最大的價(jià)值在于建立信心。我建議新手從這類樣本練手先用Python寫個(gè)Base64解碼腳本觀察URL參數(shù)的還原結(jié)果再對(duì)照樣本代碼確認(rèn)編碼邏輯。這個(gè)過程能幫你熟悉從樣本到流量的完整分析鏈條后面遇到真正復(fù)雜的加密方案時(shí)就知道每一層該從哪里下手。2.2 第二代XOR與自定義混淆真正意義上的加密通信很多組織是從XOR開始入門的。XOR運(yùn)算本身很輕量既不需要額外依賴也不會(huì)帶來明顯的性能開銷非常適合集成到木馬模塊里。常見的做法是單字節(jié)XOR或者多字節(jié)XOR密鑰要么硬編碼在樣本中要么從計(jì)算機(jī)名、時(shí)間戳等運(yùn)行環(huán)境信息里臨時(shí)派生。XOR通信的一個(gè)顯著特征是加密前后數(shù)據(jù)長(zhǎng)度完全一致密文的熵相對(duì)較高但又不像AES輸出那樣均勻隨機(jī)。當(dāng)攻擊者使用多字節(jié)密鑰輪轉(zhuǎn)加密時(shí)可以通過已知明文攻擊來破解比如心跳包固定包含“hello”或者特定的會(huì)話標(biāo)識(shí)用明密文對(duì)就能反推出密鑰流。蔓靈花早期樣本里的XOR方案有一個(gè)值得注意的細(xì)節(jié)部分版本會(huì)在加密前先做Base64或自定義編碼再執(zhí)行XOR。這種多層混淆表面看增加了分析難度實(shí)際上每一層都有自己的特征反而更容易暴露算法的執(zhí)行順序。分析時(shí)只要順著樣本的字符串處理函數(shù)逐層剝開就能還原出完整的編碼鏈路。2.3 第三代AES這類對(duì)稱加密與動(dòng)態(tài)密鑰隨著檢測(cè)能力提升攻擊者很快意識(shí)到XOR太容易被統(tǒng)計(jì)特征識(shí)別開始轉(zhuǎn)向AES、RC4、ChaCha20這類標(biāo)準(zhǔn)對(duì)稱加密算法。最常見的是AES-CBC模式密鑰和IV的生成方式五花八門有的直接硬編碼在數(shù)據(jù)段里有的由文件屬性、卷序列號(hào)、系統(tǒng)版本組合后經(jīng)過MD5或SHA1派生還有的會(huì)在每次通信前重新生成密鑰再通過其他通道傳遞。分析這一代加密通信核心思路是先確定加密算法再找密鑰和IV。算法層相對(duì)容易識(shí)別AES的S盒、密鑰擴(kuò)展邏輯、輪常量在反匯編代碼里都有明顯特征困難在于密鑰的提取。硬編碼密鑰直接用二進(jìn)制搜索就能定位派生密鑰則需要?jiǎng)討B(tài)調(diào)試在哈希函數(shù)或派生邏輯的調(diào)用點(diǎn)上打斷點(diǎn)觀察寄存器里的參數(shù)值。這里要特別提醒一點(diǎn)AES-CBC解密時(shí)IV錯(cuò)誤只會(huì)影響第一個(gè)分組后續(xù)分組照樣能解出來。分析時(shí)如果發(fā)現(xiàn)解密結(jié)果前半部分亂碼、后半部分卻出現(xiàn)了正常ASCII字符基本可以斷定是IV找錯(cuò)了不用急著推翻整個(gè)算法判定。這個(gè)細(xì)節(jié)能幫你省下大量調(diào)試時(shí)間。2.4 第四代RSA混合加密與魔改TLS當(dāng)前威脅等級(jí)最高的一類方案是RSA與對(duì)稱加密的混合體系。樣本內(nèi)置RSA公鑰運(yùn)行時(shí)隨機(jī)生成AES會(huì)話密鑰再用RSA公鑰加密該會(huì)話密鑰后隨數(shù)據(jù)包發(fā)送。服務(wù)器側(cè)持私鑰解出AES密鑰后續(xù)大量數(shù)據(jù)走AES加解密。這種設(shè)計(jì)的巧妙之處在于即使分析人員完整逆向出樣本邏輯手頭沒有私鑰也無法直接解出RSA保護(hù)的會(huì)話密鑰傳統(tǒng)意義上的“解密C2流量”在這里就失效了。針對(duì)這種方案分析思路需要轉(zhuǎn)換不去硬解RSA而是走進(jìn)程內(nèi)提取路線。在樣本完成解密、即將使用AES密鑰的瞬間從內(nèi)存中把密鑰dump出來或者用Frida hook加密API直接攔截解密后的數(shù)據(jù)內(nèi)容。這就相當(dāng)于繞開了鎖直接看鎖后面已經(jīng)打開的文件柜。同期的TLS層也在快速演變。組織開始使用自定義證書字段、亂序的CipherSuite、固定時(shí)鐘偏移等方式生成獨(dú)特的TLS指紋試圖規(guī)避基于JA3的檢測(cè)。還有一些方案借助CDN中轉(zhuǎn)隱藏真實(shí)C2讓流量在攻擊者和服務(wù)器之間多跳幾層進(jìn)一步拉高溯源門檻。這部分內(nèi)容對(duì)分析環(huán)境的要求更高但思路依然是那句老話攻不破的協(xié)議就去盯實(shí)現(xiàn)協(xié)議的程序。3. 逆向還原加密通信的完整思路3.1 靜態(tài)定位先找算法再找密鑰拿到一個(gè)加密通信樣本我習(xí)慣先做靜態(tài)分析把算法和密鑰候選位置標(biāo)出來。第一步看導(dǎo)入表如果樣本導(dǎo)入了Advapi32.dll里的CryptEncrypt、CryptDecrypt系列函數(shù)大概率走的是Windows Crypto API如果導(dǎo)入的是WS2_32.dll里的send、recv同時(shí)又有大量?jī)?nèi)存操作可能是自研的私有加密實(shí)現(xiàn)如果樣本使用WinINet或WinHTTP庫通信層基本就是HTTP或HTTPS的封裝。第二步在反匯編代碼里搜索加密特征常量。AES的S盒初始化數(shù)據(jù)、TEA算法的0x9E3779B9魔數(shù)、MD5/SHA系列的初始哈希值都是一搜一個(gè)準(zhǔn)的信號(hào)。Ghidra和IDA Pro都有字節(jié)模式搜索功能直接把S盒的前幾字節(jié)輸入進(jìn)去能快速定位到算法所在的函數(shù)。字符串表同樣值得過一遍。樣本里經(jīng)常殘留錯(cuò)誤提示、調(diào)試日志或者固定的握手標(biāo)志比如“begin_session”“encrypt_key”這類字符串它們會(huì)把算法調(diào)用關(guān)系交代得一清二楚。靜態(tài)分析的目標(biāo)不是立刻寫出解密腳本而是把可疑函數(shù)、密鑰引用點(diǎn)、加密調(diào)用點(diǎn)這三類位置在代碼里標(biāo)出來給動(dòng)態(tài)調(diào)試畫一張地圖。3.2 動(dòng)態(tài)提取斷點(diǎn)、Hook與內(nèi)存搜索靜態(tài)分析經(jīng)常卡在密鑰生成環(huán)節(jié)密鑰可能被多次變換、白化處理光靠讀代碼根本繞不清楚。這時(shí)必須上動(dòng)態(tài)調(diào)試。我常用的方式是先在關(guān)鍵的API上下斷點(diǎn)比如send、recv、CryptEncrypt、CryptDecrypt、SSL_write、SSL_read。樣本調(diào)用這些函數(shù)時(shí)內(nèi)存里必然存在明密文轉(zhuǎn)換現(xiàn)場(chǎng)看函數(shù)參數(shù)就能拿到數(shù)據(jù)緩沖區(qū)的地址和長(zhǎng)度。Frida是個(gè)效率很高的選擇。用腳本attach到樣本進(jìn)程hook住發(fā)送函數(shù)在調(diào)用前后分別dump緩沖區(qū)調(diào)用前是明文調(diào)用后是密文兩組數(shù)據(jù)一對(duì)比加密邏輯基本就清楚了。下面這個(gè)例子是hook常見發(fā)送函數(shù)的關(guān)鍵思路var sendPtr Module.findExportByName(ws2_32.dll, send); Interceptor.attach(sendPtr, { onEnter: function(args) { this.buf args[1]; this.len args[2].toInt32(); }, onLeave: function(retval) { var data Memory.readByteArray(this.buf, this.len); console.log(hexdump(data, { offset: 0, length: this.len, header: true })); } });如果樣本有反調(diào)試機(jī)制動(dòng)態(tài)調(diào)試前需要先處理。常見的檢查點(diǎn)是PEB里的BeingDebugged標(biāo)記、NtQueryInformationProcess查詢、時(shí)間差檢測(cè)、線程名枚舉等。在調(diào)試器里把這些檢測(cè)點(diǎn)繞過之后再繼續(xù)hook否則樣本會(huì)直接退出給你留一個(gè)空蕩蕩的進(jìn)程列表。3.3 解密驗(yàn)證從樣本到PCAP的閉環(huán)拿到算法和密鑰后最后一步是拿真實(shí)的流量驗(yàn)證。先用Wireshark導(dǎo)出一段樣本通信的PCAP文件從里面取出加密載荷再根據(jù)逆向出的算法編寫解密腳本。這個(gè)環(huán)節(jié)我最常用Python配合pycryptodome庫可以覆蓋大部分對(duì)稱加密場(chǎng)景。以典型的AES-CBC為例解密腳本的骨架長(zhǎng)這樣from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import base64 key bytes.fromhex(SAMPLE_KEY_HEX) iv bytes.fromhex(SAMPLE_IV_HEX) enc_b64 SAMPLE_CIPHERTEXT_BASE64 ciphertext base64.b64decode(enc_b64) cipher AES.new(key, AES.MODE_CBC, iv) plaintext unpad(cipher.decrypt(ciphertext), 16) print(plaintext)注意AES-CBC的密鑰長(zhǎng)度決定算法分級(jí)128位對(duì)應(yīng)16字節(jié)、192位對(duì)應(yīng)24字節(jié)、256位對(duì)應(yīng)32字節(jié)。如果腳本解密出一屏亂碼先檢查密鑰長(zhǎng)度是不是搞錯(cuò)了再檢查是否跳過前8字節(jié)的會(huì)話標(biāo)識(shí)、是否漏掉Base64解碼步驟。驗(yàn)證成功后的流量日志非常寶貴它能直接告訴你攻擊者的指令格式、心跳間隔、回傳文件的分塊方式。把這些信息整理成檢測(cè)規(guī)則比單純提取IOC要實(shí)用得多因?yàn)槟憷斫獾氖峭ㄐ艆f(xié)議的語義而不是某個(gè)固化的哈希值。4. 從零搭建一套惡意流量解密分析環(huán)境4.1 需要準(zhǔn)備的虛擬機(jī)與工具清單建議用一臺(tái)Windows虛擬機(jī)專門運(yùn)行惡意樣本另一臺(tái)Linux虛擬機(jī)兼職DNS模擬和C2服務(wù)器模擬。物理機(jī)或宿主機(jī)承擔(dān)抓包工作。網(wǎng)絡(luò)結(jié)構(gòu)要完全隔離不能讓樣本真的連到互聯(lián)網(wǎng)。這種三機(jī)結(jié)構(gòu)能確保分析全程可控也能防止樣本通過局域網(wǎng)橫向擴(kuò)散。工具方面我整理了一份常用清單用途工具說明靜態(tài)分析IDA Pro / Ghidra函數(shù)定位、算法還原動(dòng)態(tài)調(diào)試x64dbg / Frida斷點(diǎn)、Hook、內(nèi)存dump流量捕獲WiresharkPCAP采集、特征分析域名模擬ApateDNS / INetSim偽造DNS應(yīng)答、模擬HTTP服務(wù)自動(dòng)化監(jiān)控Procmon注冊(cè)表、文件、進(jìn)程行為監(jiān)控腳本處理Python pycryptodome數(shù)據(jù)清洗、加解密驗(yàn)證調(diào)試建議先快照再運(yùn)行。每次分析前給虛擬機(jī)做一個(gè)干凈快照樣本一旦釋放惡意行為或者觸發(fā)自毀邏輯恢復(fù)快照就能快速重來。這個(gè)習(xí)慣看起來不起眼但在反復(fù)調(diào)試時(shí)能節(jié)省大量時(shí)間。4.2 把C2通信完整地騙到本機(jī)要讓樣本正常聯(lián)網(wǎng)最可控的辦法是偽造C2域名。在Windows虛擬機(jī)的hosts文件里把樣本內(nèi)置的C2域名指向Linux模擬機(jī)的IP然后在Linux上用INetSim監(jiān)聽對(duì)端口并把響應(yīng)內(nèi)容調(diào)整成樣本預(yù)期的格式。樣本解析到域名后會(huì)發(fā)現(xiàn)連上了“服務(wù)器”就會(huì)按照預(yù)設(shè)的心跳間隔持續(xù)回連。抓包時(shí)要重點(diǎn)記錄幾個(gè)位置TCP握手的第一個(gè)數(shù)據(jù)包、TLS ClientHello、樣本主動(dòng)發(fā)送的POST或GET請(qǐng)求。很多樣本在上線成功后會(huì)立刻收取一個(gè)密鑰或配置這個(gè)配置數(shù)據(jù)往往也是加密的解密后能看到攻擊者針對(duì)該主機(jī)下發(fā)任務(wù)的格式。Wireshark里可以用下面這組過濾條件快速篩選可疑流量tcp.port 80 || tcp.port 443 || tls.handshake.type 1跑一段時(shí)間后把流量導(dǎo)出為PCAP結(jié)合前面逆向出的密鑰做解密驗(yàn)證。我遇到最多的現(xiàn)象是樣本心跳包本身是固定結(jié)構(gòu)解密后能直接看到命令類型、會(huì)話編號(hào)和目標(biāo)ID。把這個(gè)結(jié)構(gòu)畫成字段偏移表后續(xù)遇到同源樣本時(shí)可以自動(dòng)匹配。4.3 解密并還原惡意指令內(nèi)容解密驗(yàn)證時(shí)建議把樣本運(yùn)行時(shí)間控制在三分鐘以內(nèi)這樣既能看到完整的心跳周期又不至于讓樣本持續(xù)上傳本地文件。如果解密后看到指令是下載并執(zhí)行某個(gè)文件或者調(diào)整自身持久化配置說明你的解密鏈路已經(jīng)打通了。還原指令內(nèi)容還有一個(gè)容易被忽視的作用為檢測(cè)規(guī)則提供語義依據(jù)。比如指令格式里固定包含一個(gè)魔數(shù)頭那么流量檢測(cè)設(shè)備就可以在不解密全部數(shù)據(jù)的情況下通過檢測(cè)魔數(shù)頭出現(xiàn)的位置和頻率來預(yù)判屬于特定家族。我自己做檢測(cè)規(guī)則時(shí)非常依賴這一步還原出來的結(jié)構(gòu)。5. 加密通信分析的常見坑與排查對(duì)照5.1 新手最容易踩的四個(gè)坑第一個(gè)坑是不關(guān)反調(diào)試就硬調(diào)。部分樣本使用雙進(jìn)程保護(hù)調(diào)試器附加后子進(jìn)程退出還有的在關(guān)鍵函數(shù)里插入大量垃圾指令干擾定位。建議動(dòng)態(tài)調(diào)試前先用x64dbg的ScyllaHide插件統(tǒng)一清理PEB標(biāo)記、斷點(diǎn)檢測(cè)等常見反調(diào)試項(xiàng)。省得花半天追一個(gè)永不觸發(fā)的中斷。第二個(gè)坑是把編碼當(dāng)成加密。Base64、Hex、URL編碼都只是編碼變換過程可逆且不需要密鑰。如果分析時(shí)急著搜索“密鑰”很容易被這些干擾項(xiàng)帶偏。判斷標(biāo)準(zhǔn)很簡(jiǎn)單變換過程是否需要密鑰參與沒有密鑰也能還原的就是編碼不是加密。第三個(gè)坑是高估了RSA私鑰獲取的可能。遇到RSA混合加密方案不要浪費(fèi)時(shí)間去爆破私鑰那是數(shù)學(xué)上的不可能任務(wù)。直接轉(zhuǎn)向進(jìn)程內(nèi)存在解密后、使用前的窗口期下鉤子通常幾分鐘就能拿到實(shí)際使用的會(huì)話密鑰。第四個(gè)坑是忽略Padding和分塊規(guī)則。CBC模式的數(shù)據(jù)在發(fā)送前通常要做Padding最后一塊可能不足16字節(jié)有些樣本還會(huì)自定義數(shù)據(jù)格式在密文前追加固定長(zhǎng)度的頭部信息。直接對(duì)整個(gè)載荷做解密前幾字節(jié)解出來是長(zhǎng)度字段或魔數(shù)容易被誤判為解密失敗。正確的做法是先把頭部字段解析清楚定位到有效密文起點(diǎn)再執(zhí)行解密。5.2 加密流量分析排查速查表現(xiàn)場(chǎng)現(xiàn)象可能原因排查方向特征字符串找到斷點(diǎn)卻不命中算法實(shí)現(xiàn)是純函數(shù)、無外部API調(diào)用在函數(shù)調(diào)用處打斷點(diǎn)回溯調(diào)用者按AES-CBC解密全是亂碼密鑰長(zhǎng)度錯(cuò)誤或IV提取位置錯(cuò)誤校驗(yàn)密鑰字節(jié)數(shù)核對(duì)Padding機(jī)制內(nèi)存里搜不到密鑰密鑰在函數(shù)執(zhí)行前臨時(shí)生成hook哈希函數(shù)dump派生輸入流量像HTTPS但證書異常魔改TLS或內(nèi)置固定證書對(duì)比證書鏈、檢測(cè)TLS指紋偏差域名解析成功但連不上模擬服務(wù)端不支持對(duì)應(yīng)協(xié)議用INetSim擴(kuò)展模塊按協(xié)議族監(jiān)聽解密后內(nèi)容前半部分正常后半部分亂碼IV錯(cuò)誤僅影響首分組保留后續(xù)明文反推有效IV這套速查表是我在實(shí)際分析中反復(fù)驗(yàn)證過的??吹絹y碼別慌張先確定問題在密鑰、IV還是數(shù)據(jù)切分再逐層排除。加密通信分析的本質(zhì)就是借助樣本自身留下的線索把攻擊者精心搭建的加密鏈路反向拆開。每次拆解成功你手里的檢測(cè)能力和威脅追蹤能力都會(huì)實(shí)實(shí)在在往前進(jìn)一步。最后分享一個(gè)我自己的經(jīng)驗(yàn)在分析加密通信時(shí)可以順手統(tǒng)計(jì)心跳包的固定長(zhǎng)度變化。即使在無法快速解密的場(chǎng)景下包的長(zhǎng)度分布、發(fā)送間隔也能暴露攻擊者的指令下發(fā)節(jié)奏和受控主機(jī)的活躍狀態(tài)。把這些元數(shù)據(jù)特征記下來后續(xù)不管是追蹤同源樣本還是做全流量回溯都經(jīng)常能派上大用場(chǎng)。