最佳化NFR之一--MSSQLHelloBufferOverflow_PHP
最後更新:2016-06-01
來源:互聯網
上載者:User
關鍵字
之一
最佳化
x00
長度
攻擊
這個
NFR
漏洞
說明
1. 前言 3
2. 警示資訊 3
3. NFR的檢測 4
4. 協議分析 8
5. 漏洞說明 15
6. 漏洞分析 18
7. 小結 20
1. 前言
NFR(Network Flight Recorder)是一個老牌的商業網路IDS產品,最初由Firewall的牛人Marcus J. Ranum建立,是作為一個通用的網路流量分析和記錄軟體來實現的,為了最大限度地發揮分析工具的靈活性,NFR提供了完善強大的N-Code指令碼語言,在很多的評測中表現出色。雖然L0pht為NFR提供過數百個簽名庫,但是缺乏一個可靠的簽名集一直是他的軟肋。
使用NFR有一段時間後,發現NFR存在著不少問題。且不說AI對中文的相容性差而經常退出,也不說NFR版本升級而攻擊事件說明卻一直用舊版本的說明,單是IDS中最關鍵的攻擊簽名庫,卻讓我大跌眼鏡。除了升級緩慢以外,甚至還存在不少錯誤的簽名。本系列文章針對其中我發現的部分問題進行分析和闡述,以便各位更好地利用NFR產品,同時也想和各位同仁討論IDS中攻擊簽名庫的編寫。由於知識和時間的限制,錯誤之處在所難免,還希望得到各位的指教,任何意見或建議請發至:benjurry@xfocus.org
SQL Server是微軟為對抗Oracle推出的資料庫, 佔領的市場份額已經僅次於Oracle,居世界第二,但是其安全性也一直受到使用者的置疑。從1996年,Microsoft公司推出的SQL Server 6.5版本到1998年推出的SQL Server 7.0,以及到2000年8月推出了SQL Server 2000,在版本和功能不斷升級的情況下,安全問題卻沒有得到很好地改善,不斷髮布針對SQL Server的資訊安全諮詢和補丁。在2003年1月24日,針對SQL Server的“Slammer”蠕蟲在Internet上肆虐,導致網路流量激增,嚴重影響了世界範圍內的電腦和網路系統。SQL Server的漏洞引起了各大安全公司和廠商的重視,因此NFR也立即發布了多個針對SQL Server的攻擊簽名,其中包括了2002年8月7日Immunity公司Dave Aitel發現的Hello Buffer Overflow漏洞。但是在使用過程中,我發現NFR針對該漏洞的警示非常多,於是我對此進行了分析,於是寫成了這個文章,以做記錄。
2. 警示資訊
下面是NFR針對MS SQL Hello Buffer Overflow的警示資訊:
Severity: Attack
Time: 13:54:21 15-Jul-2003
Source File: packages/mssql/sql2k.nfr
Line: 226
Host: benjurry-xfocus
Alert ID: mssql_sql2k:buffered_hello
Source ID: mssql_sql2k:source_me
Source: mssql_sql2k:source_me
Source Description: Sqlserver 2k overflow detector
Source PID: 36531
Alert Message: Saw 8 Mssql HELLO overflows from 192.168
0.110 in 900 seconds
: 3
Source IP: 192.168.0.110
Destination IP: --
其中包括了事件的嚴重等級、時間、NFR Sensor名字、攻擊源IP、目的IP和一些其他警示資訊。
在實際的使用中,一天產生了幾百條這種警示,看來這是個誤判,我們可以好好的分析一下了。
3. NFR的檢測
我們首先開啟NFR發布的簽名庫 MSSQL.fp(或者也可以在AI的package中查看) 看一下NFR的針對這個漏洞的攻擊簽名:
…..
變數定義等…
…
sqlserv_schema = library_schema:new(1, ["time","ip","int","ip","int", "str"],
scope());
sqlserv_rec = recorder("bin/list %c", "sqlserv_schema");
HELLO_SIG = "\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15";
MIN_LEN = strlen(HELLO_SIG);
…….
filter hello tcp (client, dport: 1433) {
declare $Blob inside tcp.connsym;
if ($Blob == NULL) {
$Blob = tcp.blob;
} else {
$Blob = cat($Blob, tcp.blob);
}
if (strlen($Blob) < MIN_LEN)
return;
if (prefix($Blob, HELLO_SIG)) {
if (COUNTHELLO[tcp.connsrc]) {
COUNTHELLO[tcp.connsrc] = COUNTHELLO[tcp.connsrc] + 1;
} else {
COUNTHELLO[tcp.connsrc] = 1;
}
if (do_alert(hello_overflow_alert, tcp.connsrc)) {
alert(source_me, hello_overflow_alert, tcp.connsrc,
tcp.connsport, tcp.conndst, tcp.conndport,
"--AlertDetails",
"ALERT_ID", "40-8",
"ALERT_CONFIDENCE", 60,
"ALERT_SEVERITY", "medium",
"ALERT_IMPACT", "unknown",
"ALERT_EVENT_TYPE", "attack",
"ALERT_ASSESSMENT", "unknown",
"IP_ADDR_SRC", tcp.connsrc,
"PORT_SRC", tcp.connsport,
"IP_ADDR_DST", tcp.conndst,
"PORT_DST", tcp.conndport,
"IP_PROTO_NUM", 6);
}
record packet.sec, tcp.conndst, tcp.conndport, tcp.connsrc,
tcp.connsport, $Blob to sqlserv_rec;
misc_attacks:rec(packet.sec, scope(),
"Mssql HELLO overflow!", tcp.connsrc, tcp.conndst);
}
}
從上面的N-CODE中我們可以看到,NFR在做這個檢測的時候步驟如下:
1、定義了一個攻擊特徵碼:
HELLO_SIG = "\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15";
和攻擊特徵碼的長度:
MIN_LEN = strlen(HELLO_SIG);
2、從TCP的載荷資料中取出資料,把這個資料的長度和特徵碼長度比較,如果這個資料長度小於攻擊特徵碼的長度,那麼就不再進行下一步的檢測;
3、否則,把這個資料和特徵碼進行字串匹配,如果一致則認為是攻擊行為,然後進行阻止或者警示。
接下來我們分析一下NFR IDS Record的資料,在AI中選擇package->Query->MSSQL->MSSQL Server 200,定好條件,按Table查到資料,隨便選取一條,copy出來得到如下內容:
Time: 15-Jul-2003 13:54:21
NFR: benjurry-xfocus
Destination Address:192.168.0.135
Destination Port: 1433
Source Address: 192.168.0.110
Source Port: 1391
Payload:
\x12\x01\x004\x00\x00\x00\x00\x00\x00\x15\x00\x06\x01\x00\x1b\x00
\x01\x02\x00\x1c\x00\x0c\x03\x00(\x00\x04\xff\x08\x00\x00\xc2\x00
\x00\x00MSSQLServer\x00x\x03\x00\x00
上面這條記錄包含了攻擊的時間,報告攻擊行為的NIDS sessor名字,目的IP、目的連接埠、源ip和源連接埠,而我們關心的是有效載荷Payload,因為它是NIDS用來和攻擊簽名庫比較的資料。但是NFR在這裡有幾個小問題:
1. 會把payload中的能轉換成ASCII字元的16進位數轉換成ASCII碼;
2. 不能處理Unicode的字元,這個將在以後的分析中可以看到。
在這個例子中為了分析方便,我們把上面的payload中的字元特徵碼部分轉換成16進位數:
\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15\x00\x06\x01\x00\x1b
\x00\x01\x02\x00\x1c\x00\x0c\x03\x00\x28\x00\x04\xff\x08\x00\x00
\xc2\x00\x00\x00MSSQLServer\x00x\x03\x00\x00
NFR抓到資料群組包後,發現是SQL Server包,便把裡面的內容和SQL Server的攻擊庫進行比較,很明顯,上面所列的資料和攻擊庫是相符合的,因此一個警示便產生了,但是這真是一個攻擊行為嗎?我們繼續看下面的分析。
4. 協議分析
根據Xfocus的協議分析項目(將會在近期公布項目成果),MS SQL 2000用的是TDS8.0,它的格式如下:
-------------------------------------------------
| TDS包頭(8位元組) | TDS負載資料 |
-------------------------------------------------
其中MS SQL SERVER 2000 TDS的包頭結構如下:
-------------------------------------------------------------------
| TOKEN | STATUS | LENGTH | SIGNED NUM | PACKET NUM | WINDOW SIZE |
-------------------------------------------------------------------
其中TOKEN欄位域1個位元組,用來表示TDS操作請求種類。在這個漏洞中是0x12,也就是NFR記錄中的承載中的第一個位元組0x12, 0x12是預登入驗證命令請求。其目的是獲得當前MS SQL SERVER2000的一些設定值,比如SQL SERVER版本,是否支援加密等資訊,作為用戶端在構造TDS包的一個依據。SQL Server在接受到該類型的包的時候,將會由SSlibnet.dll中的相應函數做處理,在我的系統SQL Server 2000(沒有SP)的情況下,相應函數如下:
.text:42CF6DDD ; Attributes: bp-based frame
.text:42CF6DDD
.text:42CF6DDD public ConnectionPreLogin
.text:42CF6DDD ConnectionPreLogin proc near
.text:42CF6DDD
.text:42CF6DDD var_4 = dword ptr -4
.text:42CF6DDD arg_0 = dword ptr 8
.text:42CF6DDD arg_4 = dword ptr 0Ch
.text:42CF6DDD arg_8 = dword ptr 10h
.text:42CF6DDD arg_C = dword ptr 14h
.text:42CF6DDD arg_10 = dword ptr 18h
.text:42CF6DDD
.text:42CF6DDD push ebp
.text:42CF6DDE mov ebp, esp
.text:42CF6DE0 push ecx
.text:42CF6DE1 mov eax, [ebp+arg_0]
.text:42CF6DE4 mov ecx, [eax+94h]
.text:42CF6DEA mov [ebp+var_4], ecx
.text:42CF6DED cmp [ebp+var_4], 1
.text:42CF6DF1 jz short loc_42CF6E01
.text:42CF6DF3 cmp [ebp+var_4], 1
.text:42CF6DF7 jle short loc_42CF6E3D
.text:42CF6DF9 cmp [ebp+var_4], 3
……
STATUS欄位域1個位元組,當它為0x01的時候表示此包為當前TDS會話中的最後一個TDS包。
LENGTH欄位域2個位元組,表示TDS包的總長度,包括TDS包頭的長度。
SIGNED NUM欄位域2個位元組,目前保留未用。
PACKET NUM欄位域1個位元組,表示此TDS包在當前TDS操作請求中的序號
WINDOW SIZE欄位域1個位元組,目前保留未用。
MS SQL SERVER 0X12 TDS的包主要包格式如下:
------------------------------------------------------
| TDS包頭(8位元組)| 欄位指示頭 | 資訊 |
------------------------------------------------------
其中欄位指示頭是一個可以變長的表,表的每一項代表了在一個欄位在資訊中的位移地址和長度資訊,在SQL2000中主要是4個欄位,其對應的欄位指示頭的結構如下:
{
BYTE CNETLIBVERNO;
WORD CNETLIBVEROFFSET;
WORD CNETLIBVERLEN;
BYTE CENYFLAGNO;
WORD CENYFLAGOFFSET;
WORD CENYFLAGLEN;
BYTE SINSTNAMENO;
WORD SINSTNAMEOFFSET;
WORD SINSTNAMELEN;
BYTE CTHREADIDNO;
WORD CTHREADIDOFFSET;
WORD CTHREADIDLEN;
BYTE FILEDEND;
}
資訊內容的結構如下:
{
BYTE CNETLIBVER[CNETLIBVERLEN]
BYTE CENYFLAG[CENYFLAGLEN];
BYTE SINSTNAME[SINSTNAMELEN]
DWORD CTHREADID[CTHREADIDLEN];
}
其中:
CNETLIBVERNO欄位域
位移:0
長度:1
含義:用戶端使用的網路連接庫(NETLIB)的版本號碼資訊的欄位編號。
說明:
備忘:該值固定為0
CNETLIBVEROFFSET欄位域
位移:1
長度:2
含義:用戶端使用的網路連接庫(NETLIB)的版本號碼資訊的欄位位移。
說明:欄位格式是網路位元組順序
備忘:
CNETLIBVERLEN欄位域
位移:3
長度:2
含義:用戶端使用的網路連接庫(NETLIB)的版本號碼資訊的欄位長度。
說明:欄位格式是網路位元組順序
備忘:該值固定為6
CENYFLAGNO欄位域
位移:5
長度:1
含義:用戶端使用強制加密標記欄位的欄位號。
說明:
備忘:該值固定為1
CENYFLAGOFFSET欄位域
位移:6
長度:2
含義:用戶端使用強制加密標記欄位的位移。
說明:欄位格式是網路位元組順序
備忘:
CENYFLAGLEN欄位域
位移:8
長度:2
含義:用戶端使用強制加密標記欄位的長度。
說明:欄位格式是網路位元組順序
備忘:該值固定為1
SINSTNAMENO欄位域
位移:0XA
長度:1
含義:用戶端要求使用伺服器的執行個體名欄位的欄位號。
說明:
備忘:該值固定為2
SINSTNAMEOFFSET欄位域
位移:0XB
長度:2
含義:用戶端要求使用伺服器的執行個體名欄位的位移。
說明:欄位格式是網路位元組順序
備忘:
SINSTNAMELEN欄位域
位移:0XD
長度:2
含義:用戶端要求使用伺服器的執行個體名欄位的長度。
說明:欄位格式是網路位元組順序
備忘:
CTHREADIDNO欄位域
位移:0XF
長度:1
含義:用戶端進程的線程ID欄位的欄位號。
說明:
備忘:該值固定為3
CTHREADIDOFFSET欄位域
位移:0X10
長度:2
含義:用戶端進程的線程ID欄位的的位移。
說明:欄位格式是網路位元組順序
備忘:
CTHREADIDLEN欄位域
位移:0X12
長度:2
含義:用戶端進程的線程ID欄位的長度。
說明:欄位格式是網路位元組順序
備忘:該值固定為4
FILEDEND欄位域
位移:0X14
長度:1
含義:此欄位標記欄位指示頭已經結實,下面的就是欄位的資訊。
說明:結束標記是0XFF
備忘:
CNETLIBVER欄位域
位移:0X15
長度:6
含義:用戶端使用的網路連接庫(NETLIB)的版本號碼。
說明:其版本號碼取的是DBNETLIB.DLL的版本
備忘:其格式是網路位元組格式,如版本號碼為80.528.00,則為
08 00 02 10 00 00
CENYFLAG欄位域
位移:0X1B
長度:1
含義:用戶端強制加密標誌。
說明:0代表用戶端不強制加密,1代表用戶端使用強制加密
備忘:
SINSTNAME欄位域
位移:0X1C
長度:SINSTNAMELEN
含義:用戶端要求使用的執行個體名。
說明:單位元組格式
備忘:預設執行個體使用MSSQLserver這個名字
CTHREADID欄位域
位移:0X1C+SINSTNAMELEN
長度:4
含義:用戶端進程的線程ID。
說明:欄位格式是主機位元組順序
備忘:
由上面的格式可以看出,一個用預設執行個體名MSSQLserver串連的SQL TDS包格式將是如下的格式:
\x12\x01\x00\x34\x00\x00\x00\x00
\x00\x00\x15\x00\x06\x01\x00\x1b
\x00\x01\x02\x00\x1c\x00\x0c\x03
\x00\x28\x00\x04\xff\x08\x00\x00
\xc2\x00\x00\x00MSSQ
LServer\x00
\x78\x03\x00\x00
而NFR的攻擊簽名庫卻是
HELLO_SIG = "\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15";
明顯是正常TDS 0x12預登陸包的一部分,這就難怪會有這麼多警示了,那NFR的這個攻擊簽名是如何得到的呢?
我們還是從它的漏洞說明入手吧!
5. 漏洞說明
下面是NFR N-CODE中對這個漏洞的說明:
FALSE POSITIVES
False positives are unlikely due to the nature of the attack
REFERENCES
Bugtraq Post
http://cert.uni-stuttgart.de/archive/bugtraq/2002/08/msg00125.html
Exploit
http://www.scan-associates.net/papers/sql2kx2.txt
我們可以看到這個漏洞來自於http://www.immunitysec.com/ 的Dave Aitel,而從http://cert.uni-stuttgart.de/archive/bugtraq/2002/08/msg00125.html所列的MS SQL Server Hello Overflow NASL script中我們可以看到這個漏洞的關鍵就在於以下幾個語句:
pkt_hdr = raw_string(
0x12 ,0x01 ,0x00 ,0x34 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x15 ,0x00 ,0x06 ,0x01 ,0x00 ,0x1b,
0x00 ,0x01 ,0x02 ,0x00 ,0x1c ,0x00 ,0x0c ,0x03 ,0x00 ,0x28 ,0x00 ,0x04 ,0xff ,0x08 ,0x00 ,0x02,
0x10 ,0x00 ,0x00 ,0x00
);
pkt_tail = raw_string (
0x00 ,0x24 ,0x01 ,0x00 ,0x00
);
…..
if(get_port_state(port))
{
soc = open_sock_tcp(port);
if(soc)
{
attack_string=crap(560);
sql_packet = pkt_hdr+attack_string+pkt_tail;
send(socket:soc, data:sql_packet);
r = recv(socket:soc, length:4096);
close(soc);
display ("Result:",r,"\n");
if(!r)
{
display("Security Hole in MSSQL\n");
security_hole(port:port, data:report);
}
}
其中pkt_hdr是根據TDS協議構造的包,中間加了560個字元X,pkt_tail是構造的TDS包尾。
從這裡我們可以看到,可憐的NFR居然不負責任地把MS SQL Server Hello Overflow NASL script中的pkt_hdr取出11個字元,毅然決然地把它們作為其簽名。更為可笑的是居然還寫著“False positives are unlikely due to the nature of the attack ”。
這就是在許多評測中遙遙領先的NFR?後來和stardust聊起這個事情的時候,認為這個現象和很多評測機構在評測IDS產品的時候,重視對產品漏報的檢測而忽視對誤判的評測有關。因為在評測產品時,基本上都是大部分都是黑箱評測,要檢測漏報只要收集幾個Exploits就可以進行,但要檢測漏報卻要產生正常的包,有些系統比如這裡的Sql Server,如果不瞭解它的協議包格式,是很難產生的。因此一些IDS產生便鑽了這樣的空子,只要收集一些Exploits,把其中的攻擊代碼作為攻擊簽名直接發布,而不去分析漏洞產生的真正原因。
下面我們來分析一下這個漏洞產生的原因,然後我們就可以根據這個分析,寫出比較完善的攻擊簽名。
6. 漏洞分析
用IDA對SSlibnet.dll反組譯碼,我們可以看到:
.text:42CF6F49 loc_42CF6F49: ; CODE XREF: sub_42CF6E4F+EA j
.text:42CF6F49 mov eax, [ebp+0xc]
.text:42CF6F4C add eax, [ebp-0x218]
.text:42CF6F52 push eax
.text:42CF6F53 lea ecx, [ebp-0x214]
.text:42CF6F59 push ecx
.text:42CF6F5A call strcpy
.text:42CF6F5F add esp, 8
.text:42CF6F62 push offset unk_42D01104
.text:42CF6F67 lea edx, [ebp-0x214]
.text:42CF6F6D push edx
.text:42CF6F6E call strcmp
.text:42CF6F73 add esp, 8
這個漏洞的原因就在這裡,當程式用strcpy拷貝的時候,如果源字串超出0x214(也就是532)後,目標地址後的環境變數就會被覆蓋導致溢出。由於這個漏洞很早,經曆了“Slammer”蠕蟲後,基本上所有的系統都補掉了這個漏洞,因此這裡就不在提供攻擊代碼了,有興趣的朋友可以自己分析和編寫一下。
另外需要在這裡說明的是,如果分析了TDS協議和這個漏洞的成因,完善的攻擊程式中的TDS包的長度是根據計算產生的,明顯不會是”\x00\x34”,以避過SQL Server中針對TDS包長度的校正(當然在這個版本的SQL Server中還不包含這個校正),或者攻擊程式把這攻擊代碼分成多個包,因此TDS格式中的status就不會是0x01,因此NFR是檢測不出完善的攻擊程式的的,也就是說針對這個漏洞,NFR 同時存在誤判和漏洞的情況。
在分析了這個漏洞的成因後,我們就可以針對這個問題寫出自己的NFR檢測代碼:
sqlserv_schema = library_schema:new(1, ["time","ip","int","ip","int", "str"],
scope());
sqlserv_rec = recorder("bin/list %c", "sqlserv_schema");
HELLO_SIG = "\x12 ";
#考慮了分包和長度不固定的因素,去除了後面不可靠的特徵串
MIN_LEN =29;
#包括TDS包頭和欄位指示頭的總長度
…….
filter hello tcp (client, dport: 1433) {
declare $Blob inside tcp.connsym;
if ($Blob == NULL) {
$Blob = tcp.blob;
} else {
$Blob = cat($Blob, tcp.blob);
}
if (strlen($Blob) < MIN_LEN)
return;
if (prefix($Blob, HELLO_SIG) && strlen($Blob) > 295) {
#考慮到執行個體名不可能超過255,因此這裡長度選擇了40(包頭)+255=295
#也可以增加一個Value,以便使用者自己根據事件情況進行調節
#警示
….
}
7. 小結
通過對NFR這個攻擊簽名的分析可以看出,發布完善的IDS攻擊簽名不是一個簡單的事情,它需要瞭解應用的協議格式和漏洞的成因。而不是簡單地收集網路上存在地exploits,然後截取其中的特徵碼。
目前很多人包括IDS開發人月都在討論IDS有沒有前途,需不需要。我想與其在那裡討論,不如靜下來好好分析漏洞,完善攻擊簽名,使IDS做的更準確。
與其坐而論不如起而行!!(出處:www.xfocus.net)