<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    3.7 Snort如何編寫良好的規則

    在開發Snort規則以最大化效率和速度時,需要記住一些通用概念。

    3.7.1 內容匹配

    Snort根據協議(ip、tcp、udp、icmp)對規則進行分組,然后根據端口(ip和icmp使用稍微不同的邏輯),然后根據具有content端口和不具有content端口對規則進行分組。對于帶有content的規則,使用多模式匹配器來選擇有機會基于單一內容進行匹配的規則。通過這個“快速”模式匹配器選擇用于評估的規則可以提高性能,尤其是應用于像HTTP這樣的大型規則組時。content越長越獨特,規則及其所有規則選項被不必要地評估的可能性就越小——可以肯定地說,通常好的流量比壞的流量多。沒有content的規則總是被評估(相對于它們所在的協議和端口組),這可能會拖累性能。雖然一些檢測選項,如pcrebyte_test,在數據包的有效負載部分執行檢測,但它們不被快速模式匹配引擎使用。如果可能的話,嘗試在規則中包含至少一個content(或uricontent)規則選項。

    3.7.2 抓住漏洞,而不是利用

    嘗試編寫針對漏洞的規則,而不是針對特定的漏洞。

    例如,尋找參數太大的易受攻擊命令,而不是綁定shell的shell代碼。

    通過為漏洞編寫規則,當攻擊者稍微改變漏洞時,規則更不易被規避。

    3.7.3 捕捉規則中協議的奇怪之處

    許多服務通常用大寫字母發送命令。FTP就是一個很好的例子。在FTP中,要發送用戶名,客戶端發送:

    user username_here

    尋找FTP root登錄嘗試的一個簡單規則可以是:

    alert tcp any any -> any any 21 (content:"user root";)

    雖然編寫一個查找根用戶名的規則看起來很簡單,但是一個好的規則將處理協議在接受用戶命令時可能處理的所有奇怪的事情。

    例如,以下每一個都被大多數FTP服務器接受:

        user root
        user root
        user root
        user root
        user<tab>root

    要處理FTP服務器可能處理的所有情況,規則需要比簡單的字符串匹配更聰明。

    在ftp上尋找root登錄的一個好規則是:

        alert tcp any any -> any 21 (flow:to_server,established;
            content:"root"; pcre:"/user\s+root/i";)

    在這個規則中有幾件重要的事情需要注意:

    • 該規則有一個flow選項,用于驗證該流量是在已建立的會話上發送到服務器的。
    • 該規則有一個content選項,用于查找root,這是攻擊中最長、最獨特的字符串。添加此選項是為了僅在有效負載中找到內容根時允許快速模式匹配器選擇此規則進行評估。
    • 該規則有一個pcre選項,查找user,后面至少跟著一個空格字符(包括tab),后面跟著根,忽略大小寫。

    3.7.4 優化規則

    檢測引擎的內容匹配部分有遞歸處理一些逃稅案例。沒有正確編寫的規則可能會導致Snort浪費時間重復檢查。

    遞歸現在的工作方式是,如果一個模式匹配,并且該模式之后的任何檢測選項失敗,那么就在上次找到模式的地方之后再次查找該模式。重復,直到再次找不到模式,或者opt函數全部成功。

    乍一看,這聽起來可能不像一個聰明的想法,但它是必要的。例如,以下規則:

    alert ip any any -> any any (content:"a"; content:"b"; within:1;)

    這條規則將查找緊接在“b”后面的“a”。如果沒有遞歸,payload“aab”將失敗,即使payload“aab”的“a”后面緊接著“b”很明顯,因為第一個“a”后面并沒有緊跟著“b”。

    雖然遞歸對于檢測很重要,但遞歸實現不是很智能。

    例如,以下規則選項沒有優化:

    content:"|13|"; dsize:1;

    通過查看這個規則片段,很明顯,該規則查找的是單個字節為0x13的數據包。但是,由于遞歸,具有1024字節的0x13的數據包可能會導致1023過多的模式匹配嘗試和1023過多的dsize檢查。為什么?0 * 13的內容將在第一個字節,然后dsize選項會失敗,因為遞歸,0 * 13的內容被發現后開始再次發現了前面的0 * 13,一旦發現,然后再次檢查dsize,重復,直到再沒有找到0 * 13的有效載荷。

    重新排序規則選項,以便將離散檢查(如dsize)移動到規則的開頭,從而加快Snort的速度。

    優化后的規則剪輯將是:

    dsize:1; content:"|13|";

    一個1024字節的0x13的數據包會立即失敗,因為dsize檢查是被檢查的第一個選項,而dsize是一個沒有遞歸的離散檢查。

    以下規則選項是離散的,通常應該放在任何規則的開始:

    • dsize
    • flags
    • flow
    • fragbits
    • icmp_id
    • icmp_seq
    • icode
    • id
    • ipopts
    • ip_proto
    • itype
    • seq
    • session
    • tos
    • ttl
    • ack
    • window
    • resp
    • sameip

    3.7.5 測試數值

    編寫規則選項byte_test和byte_jump是為了支持為具有長度編碼數據的協議編寫規則。RPC是產生這兩個規則選項需求的協議,因為RPC使用簡單的基于長度的編碼來傳遞數據。

    為了理解為什么byte_test和byte_jump是有用的,讓我們嘗試利用sadmind服務。

    這是利用的payload:

    89 09 9c e2 00 00 00 00 00 00 00 02 00 01 87 88  ................
    00 00 00 0a 00 00 00 01 00 00 00 01 00 00 00 20  ...............
    40 28 3a 10 00 00 00 0a 4d 45 54 41 53 50 4c 4f  @(:.....metasplo
    49 54 00 00 00 00 00 00 00 00 00 00 00 00 00 00  it..............
    00 00 00 00 00 00 00 00 40 28 3a 14 00 07 45 df  ........@(:...e.
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 04  ................
    7f 00 00 01 00 01 87 88 00 00 00 0a 00 00 00 04  ................
    7f 00 00 01 00 01 87 88 00 00 00 0a 00 00 00 11  ................
    00 00 00 1e 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 3b 4d 45 54 41 53 50 4c 4f  .......;metasplo
    49 54 00 00 00 00 00 00 00 00 00 00 00 00 00 00  it..............
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 06 73 79 73 74 65 6d 00 00  ........system..
    00 00 00 15 2e 2e 2f 2e 2e 2f 2e 2e 2f 2e 2e 2f  ....../../../../
    2e 2e 2f 62 69 6e 2f 73 68 00 00 00 00 00 04 1e  ../bin/sh.......
    <snip>

    讓我們將其分解,描述每個字段,并找出如何編寫規則來捕獲這種漏洞。

    有一些事情要注意RPC:

    • 數字被寫成uint32,占用4個字節。數字26將顯示為0x0000001a。
    • 字符串被寫入uint32,指定字符串的長度,然后是空字節,填充字符串的長度,以4字節的邊界結束。字符串“bob”將顯示為0x00000003626f6200。
    89 09 9c e2     - the request id, a random uint32, unique to each request
    00 00 00 00     - rpc type (call = 0, response = 1)
    00 00 00 02     - rpc version (2)
    00 01 87 88     - rpc program (0x00018788 = 100232 = sadmind)
    00 00 00 0a     - rpc program version (0x0000000a = 10)
    00 00 00 01     - rpc procedure (0x00000001 = 1)
    00 00 00 01     - credential flavor (1 = auth\_unix)
    00 00 00 20     - length of auth\_unix data (0x20 = 32

    接下來的32字節是auth_unix數據

    40 28 3a 10 - unix timestamp (0x40283a10 = 1076378128 = feb 10 01:55:28 2004 gmt)
    00 00 00 0a - length of the client machine name (0x0a = 10)
    4d 45 54 41 53 50 4c 4f 49 54 00 00  - metasploit
    
    00 00 00 00 - uid of requesting user (0)
    00 00 00 00 - gid of requesting user (0)
    00 00 00 00 - extra group ids (0)
    
    00 00 00 00     - verifier flavor (0 = auth\_null, aka none)
    00 00 00 00     - length of verifier (0, aka none)
    

    包的其余部分是傳遞到sadmind過程1的請求。

    然而,我們知道sadmind的漏洞在于它信任來自客戶端的uid。sadmind運行客戶機的uid為0作為根的任何請求。因此,我們已經對請求進行了足夠的解碼來編寫我們的規則。

    首先,我們需要確保我們的數據包是一個RPC調用。

    content:"|00 00 00 00|"; offset:4; depth:4;

    然后,我們需要確保我們的數據包是一個sadmind呼叫。

    content:"|00 01 87 88|"; offset:12; depth:4;

    然后,我們需要確保我們的數據包是對過程1(即漏洞過程)的調用。

    content:"|00 00 00 01|"; offset:20; depth:4;

    然后,我們需要確保包具有auth_unix憑據。

    content:"|00 00 00 01|"; offset:24; depth:4;

    我們不關心主機名,但我們希望跳過它,并檢查主機名后面的數字值。這就是byte_test有用的地方。從主機名的長度開始,我們得到的數據是:

    00 00 00 0a 4d 45 54 41 53 50 4c 4f 49 54 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00

    我們希望讀取4個字節,將其轉換為一個數字,并向前跳轉這么多字節,確保考慮到RPC對字符串所需的填充。如果我們這樣做,我們現在是:

    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00

    正好是uid的確切位置,也就是我們要檢查的值。

    在英語中,我們想要讀取4個字節,從數據包的開始讀取36個字節,并將這4個字節轉換為整數,并向前跳轉許多字節,在4字節邊界上對齊。在Snort規則中,我們使用:

    byte_jump:4,36,align;

    然后我們要尋找uid為0。

    content:"|00 00 00 00|"; within:4;

    現在我們已經擁有了規則的所有檢測功能,讓我們把它們放在一起。

        content:"|00 00 00 00|"; offset:4; depth:4;
        content:"|00 01 87 88|"; offset:12; depth:4;
        content:"|00 00 00 01|"; offset:20; depth:4;
        content:"|00 00 00 01|"; offset:24; depth:4;
        byte_jump:4,36,align;
        content:"|00 00 00 00|"; within:4;

    第三和第四串匹配是緊挨著的,所以我們應該結合這些模式。我們的結果是:

        content:"|00 00 00 00|"; offset:4; depth:4;
        content:"|00 01 87 88|"; offset:12; depth:4;
        content:"|00 00 00 01 00 00 00 01|"; offset:20; depth:8;
        byte_jump:4,36,align;
        content:"|00 00 00 00|"; within:4;

    如果在讀取客戶機的主機名時,sadmind服務容易發生緩沖區溢出,那么我們將檢查主機名的長度,以確保它不是太大,而不是讀取主機名的長度并向前跳躍那么多字節。

    要做到這一點,我們將讀取4個字節,從36個字節開始,把它變成一個數字,然后確保它不是太大(假設大于200個字節)。在Snort中,我們這樣做:

    byte_test:4,>,200,36;

    我們的完整規則是:

        content:"|00 00 00 00|"; offset:4; depth:4;
        content:"|00 01 87 88|"; offset:12; depth:4;
        content:"|00 00 00 01 00 00 00 01|"; offset:20; depth:8;
        byte_test:4,>,200,36;

    本文章首發在 網安wangan.com 網站上。

    上一篇 下一篇
    討論數量: 0
    只看當前版本


    暫無話題~
    亚洲 欧美 自拍 唯美 另类