前言 3
报警信息 3
NFR的检测 4
协议分析 8
漏洞说明 15
漏洞分析 18
小结 20
前言
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针对该漏洞的报警非常多,于是我对此进行了分析,于是写成了这个文章,以做记录。
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和一些其他报警信息。
在实际的使用中,一天产生了几百条这种报警,看来这是个误报,我们可以好好的分析一下了。
变量定义等…
…
HELLO_SIG = "\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15";
MIN_LEN = strlen(HELLO_SIG);
…….
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在这里有几个小问题:
会把payload中的能转换成ASCII字符的16进制数转换成ASCII码;
不能处理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的攻击库进行比较,很明显,上面所列的数据和攻击库是相符合的,因此一个报警便产生了,但是这真是一个攻击行为吗?我们继续看下面的分析。
根据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的这个攻击签名是如何得到的呢?
我们还是从它的漏洞说明入手吧!
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_tail = raw_string (
0x00 ,0x24 ,0x01 ,0x00 ,0x00
);
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,把其中的攻击代码作为攻击签名直接发布,而不去分析漏洞产生的真正原因。
下面我们来分析一下这个漏洞产生的原因,然后我们就可以根据这个分析,写出比较完善的攻击签名。
.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;
…….
if (strlen($Blob) < MIN_LEN)
return;
if (prefix($Blob, HELLO_SIG) && strlen($Blob) > 295) {
….
}