Переполнение буфера работает в gdb, но не без него
Я на CentOS 6.4 32 бит и пытаюсь вызвать переполнение буфера в программе. В GDB он работает. Вот результат:
[root@localhost bufferoverflow]# gdb stack
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.el6_4.1)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/bufferoverflow/stack...done.
(gdb) r
Starting program: /root/bufferoverflow/stack
process 6003 is executing new program: /bin/bash
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.107.el6_4.2.i686
sh-4.1#
однако, когда я запускаю стек программы только сам по себе, это ошибки seg. С чего бы это?
7 ответов
развитие эксплойта может привести к серьезным головным болям, если вы не должным образом учитываете факторы, которые вводят недетерминированности в процессе отладки. В частности, адреса стека в отладчике могут не совпадать с адресами во время обычного выполнения. Этот артефакт возникает из-за того, что загрузчик операционной системы помещает переменные среды и аргументы программы до начало стек:
поскольку ваша уязвимая программа не принимает никаких аргументов, переменные среды, вероятно, являются виновником. Mare уверен, что они одинаковы в обоих вызовах, в оболочке и в отладчике. С этой целью вы можете обернуть свой вызов в env
:
env - /path/to/stack
и с отладчиком:
env - gdb /path/to/stack
($) show env
LINES=24
COLUMNS=80
в приведенном выше примере есть две переменные среды, установленные gdb, которые вы можете далее отключить:
unset env LINES
unset env COLUMNS
теперь show env
должен возвращать пустой список. На этом этапе вы можете начать процесс отладки, чтобы найти абсолютный адрес стека, на который вы предполагаете перейти (например,0xbffffa8b
), и hardcode его в свой эксплойт.
еще одна тонкая, но важная деталь: есть разница между вызовом ./stack
и /path/to/stack
: С argv[0]
содержит программу именно так, как вы ее вызвали, вам нужно обеспечить равные строки вызова. Вот почему я использовал /path/to/stack
в приведенных выше примерах и не только ./stack
и gdb stack
.
при обучении использованию уязвимостей безопасности памяти я рекомендую использовать программу-оболочку ниже, которая выполняет тяжелую работу и обеспечивает равные смещения стека:
$ invoke stack # just call the executable
$ invoke -d stack # run the executable in GDB
вот этот скрипт:
#!/bin/sh
while getopts "dte:h?" opt ; do
case "$opt" in
h|\?)
printf "usage: %s -e KEY=VALUE prog [args...]\n" $(basename )
exit 0
;;
t)
tty=1
gdb=1
;;
d)
gdb=1
;;
e)
env=$OPTARG
;;
esac
done
shift $(expr $OPTIND - 1)
prog=$(readlink -f )
shift
if [ -n "$gdb" ] ; then
if [ -n "$tty" ]; then
touch /tmp/gdb-debug-pty
exec env - $env TERM=screen PWD=$PWD gdb -tty /tmp/gdb-debug-pty --args $prog "$@"
else
exec env - $env TERM=screen PWD=$PWD gdb --args $prog "$@"
fi
else
exec env - $env TERM=screen PWD=$PWD $prog "$@"
fi
адрес указателя кадра стека при запуске кода в gdb отличается от его обычного запуска. Таким образом, вы можете повредить обратный адрес прямо в режиме gdb, но он может не работать в обычном режиме. Основная причина этого заключается в том, что переменные среды различаются между двумя ситуациями.
поскольку это всего лишь демонстрация, вы можете изменить код жертвы и распечатать адрес буфера. Затем измените свой обратный адрес на offset+address буфера.
In реальность, однако, вам нужно угадать обратный адрес add НОП санки перед вашим вредоносным кодом. И вы можете угадать несколько раз, чтобы получить правильный адрес, так как ваша догадка может быть неправильной.
надеюсь, это поможет вам.
причина переполнения буфера работает под gdb и segfaults в противном случае, что gdb отключает рандомизацию макета адресного пространства. Я считаю, что это было включено по умолчанию в gdb версии 7.
вы можете проверить это с помощью следующей команды:
show disable-randomization
и установите его с
set disable-randomization on
или
set disable-randomization off
вот простой способ запуска программы с одинаковыми стеками в терминале и в gdb
:
во-первых, убедитесь, что ваша программа компилируется без защиты стека,
gcc -m32 -fno-stack-protector -z execstack -o shelltest shelltest.c -g
и ASLR отключен:
echo 0 > /proc/sys/kernel/randomize_va_space
Примечание: значение по умолчанию на моей машине было 2, Обратите внимание на ваше перед изменением этого.
затем запустите программу так (терминал и gdb соответственно):
env -i PWD="/root/Documents/MSec" SHELL="/bin/bash" SHLVL=0 /root/Documents/MSec/shelltest
env -i PWD="/root/Documents/MSec" SHELL="/bin/bash" SHLVL=0 gdb /root/Documents/MSec/shelltest
внутри gdb
обязательно unset
LINES
и COLUMNS
.
Примечание: я получил эти переменные среды играя с программы Test.
эти два запуска дадут вам идентичные указатели на верхнюю часть стека, поэтому нет необходимости в удаленных сценариях, если вы пытаетесь использовать двоичный файл, размещенный удаленно.
Я попробовал решение, принятое здесь, и оно не работает (для меня). Я знал, что gdb добавил переменные среды, и по этой причине адрес стека не совпадает, но даже удаляя эти переменные, я не могу работать с моим эксплойтом без gdb (я также попробовал скрипт, размещенный в принятом решении).
но поиск в Интернете я нашел другой скрипт, который работает для меня: https://github.com/hellman/fixenv/blob/master/r.sh
использование в основном то же самое, что скрипт в принятом решении:
- r.sh gdb ./ program [args] для запуска программы в gdb
- r.Ш./ program [args] для запуска программы без gdb
и этот скрипт работает для меня.
одна из главных вещей, которые делает gdb, что не происходит вне gdb, - это нулевая память. Скорее всего, где-то в коде вы не инициализируете свою память, и она получает значения мусора. Gdb автоматически очищает всю выделенную память, скрывая эти типы ошибок.
например: следующее должно работать в gdb, но не вне его:
int main(){
int **temp = (int**)malloc(2*sizeof(int*)); //temp[0] and temp[1] are NULL in gdb, but not outside
if (temp[0] != NULL){
*temp[0] = 1; //segfault outside of gdb
}
return 0;
}
попробуйте запустить программу под valgrind, чтобы узнать, может ли она обнаружить эту проблему.
Я на CentOS 6.4 32 бит и пытаюсь вызвать переполнение буфера в программе... Однако, когда я запускаю стек программы только сам по себе, это ошибки seg.
вы также должны убедиться, что FORTIFY_SOURCE не влияет на ваши результаты. Ошибка seg звучит как FORTIFY_SOURCE может быть проблемой, потому что FORTIFY_SOURCE вставит" более безопасные " вызовы функций для защиты от некоторых типов переполнений буфера. Если компилятор может вывести размеры буфера назначения, то размер проверяется и abort()
вызывается при нарушении (т. е. по вашей вине seg).
чтобы отключить FORTIFY_SOURCE для тестирования, вы должны скомпилировать с помощью -U_FORTIFY_SOURCE
или -D_FORTIFY_SOURCE=0
.