книги хакеры / Защита_от_взлома_сокеты,_эксплойты,_shell_код_Фостер_Дж_
.pdf
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
||||
|
|
|
C |
|
|
E |
|
|
|
|
|
|
|
|
C |
|
|
E |
|
|
|
||||||||
|
|
X |
|
|
|
|
|
|
|
|
|
|
|
|
X |
|
|
|
|
|
|
|
|
||||||
|
- |
|
|
|
|
|
|
|
d |
|
|
|
|
- |
|
|
|
|
|
|
|
d |
|
||||||
|
F |
|
|
|
|
|
|
|
|
|
t |
|
|
|
|
F |
|
|
|
|
|
|
|
|
|
t |
|
||
|
D |
|
|
|
|
|
|
|
|
|
|
i |
|
|
|
|
D |
|
|
|
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
|
r |
|
|
|
|
|
|
|
|
|
|
|
|
|
r |
||||
P |
|
|
|
|
|
|
|
NOW! |
o |
|
|
P |
|
|
|
|
|
|
|
NOW! |
o |
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
BUY |
|
|
|
|
|
|
|
|
|
|
|
BUY |
|
|
||||||||
|
|
|
|
|
to |
|
|
|
|
|
|
|
Пример: уязвимость, связанная с переполнением буфера 561 |
|
|
to |
|
|
|
|
|
|
|||||||
w Click |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
m |
|
|
w Click |
|
|
|
|
|
|
|
|
m |
|||||||||
w |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
|
|
|
|
|
||
|
w |
|
|
|
|
|
|
|
|
|
|
o |
|
|
|
|
w |
|
|
|
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
|
|
|
.c |
|
|
|
|
. |
|
|
|
|
|
|
|
|
.c |
|
||||
|
|
p |
|
|
|
|
|
|
|
e |
|
|
|
|
|
p |
|
|
|
x cha |
|
e |
|
||||||
|
|
|
d |
|
|
xchtypedef struct _t_ { |
|
|
|
d |
|
|
g |
|
|
|
|||||||||||||
|
|
|
|
f- |
|
an |
WORD |
t_s; |
/* размер этого элемента */ |
|
|
|
|
f- |
|
|
n |
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
WORD |
t_p; |
/* родительский узел */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
WORD |
t_l; |
/* левый потомок */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
WORD |
t_r; |
/* правый потомок */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
WORD |
t_n; |
/* следующий элемент в списке */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
WORD |
t_d; |
/* не используется, зарезервировано */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* для указателя на себя */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} TREE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Сама структура дерева стандартна. В поле t_s хранится размер выделенного блока. Эта величина округляется до границы слова, так что в двух младших битах можно хранить флаги. Самый младший бит t_s равен 1, если блок используется, и 0 – если он свободен. Следующий бит проверяется только, если младший бит равен 1, и в этом случае он равен 1, если предыдущий блок свободен, и 0, если занят.
Âдействительности используются только поля t_s, t_p è t_l. Пользовательские данные хранятся по адресу, который находятся в поле t_l.
Логика алгоритма управления проста. Когда блок освобождается функцией free(), младший бит в поле t_s сбрасывается в 0, помечая тем самым, что блок свободен. Если число свободных узлов достигло некоторого порога, обычно 32, и освобождается еще один блок, то дерево передается функции realfree, которая реально освобождает память. Смысл такого решения в том, чтобы уменьшить число дорогостоящих операций освобождения и повысить за счет этого производительность. Функция realfree заново балансирует дерево, чтобы оптимизировать последующие вызовы malloc è free в будущем. Во время этой операции проверяется бит занятости в соседних блоках. Если оба блока свободны, они объединяются, и новый блок перемещается на нужное место в дереве в соответствии со своим размером. Как и в случае dlmalloc во время объединения производятся манипуляции над указателями.
Âпримере 11.5 приведена реализация функции realfree, эквивалентной chunk_free в dlmalloc. Именно здесь открывается возможность для эксплойта, так что имеет смысл хорошо разобраться в этом коде.
Пример 11.5. Функция realfree
1 static void
2 realfree(void *old)
3 {
4 |
TREE |
*tp, *sp, *np; |
5 |
size_t ts, size; |
|
6 |
|
|
7 |
COUNT(nfree); |
|
8 |
|
|
9 |
/* указатель на блок */ |
10tp = BLOCK(old);
11ts = size(tp);
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
||
|
|
|
C |
|
E |
|
|
|
|
|
|
|
C |
|
E |
|
|
|
||||||
|
|
X |
|
|
|
|
|
|
|
|
|
X |
|
|
|
|
|
|
||||||
|
- |
|
|
|
|
|
d |
|
|
|
- |
|
|
|
|
|
d |
|
||||||
|
F |
|
|
|
|
|
|
|
i |
|
|
|
F |
|
|
|
|
|
|
|
i |
|
||
|
|
|
|
|
|
|
|
t |
|
|
|
|
|
|
|
|
|
|
t |
|
||||
P |
D |
|
|
|
|
|
|
|
|
o |
|
P |
D |
|
|
|
|
|
|
|
|
o |
||
|
|
|
|
NOW! |
r |
|
|
|
|
|
NOW! |
r |
||||||||||||
|
|
|
|
|
BUY |
|
|
|
|
|
|
|
|
BUY |
|
|
||||||||
|
|
|
|
to |
|
|
564 Глава 11. Написание эксплойтов II |
|
|
|
|
to |
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
m |
|
w |
|
|
|
|
|
|
|
|
|
m |
||
w Click |
|
|
|
|
|
|
o |
|
w Click |
|
|
|
|
|
|
o |
||||||||
|
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
|
|
||
|
. |
|
|
|
|
|
|
.c |
|
|
|
. |
|
|
|
|
|
|
.c |
|
||||
|
|
p |
df |
|
|
|
|
e |
|
|
|
|
p |
df |
|
|
|
|
e |
|
||||
|
|
|
|
|
g |
|
|
|
|
|
|
|
|
|
g |
|
|
|
||||||
|
|
|
|
|
n |
|
|
|
|
|
|
|
|
|
|
n |
|
|
|
|
||||
|
|
|
|
-xcha |
|
|
|
|
|
Если «подложный блок» правильно заполнен и содержит правильные ад--x cha |
|
|
|
|
|
реса возврата и того места в памяти, где этот адрес находится, то в результате манипулирования указателями в функции t_delete можно будет выполнить произвольный код. Уязвимой эту реализации делает хранение управляющей информации в том же блоке, что и данные. В некоторых операционных системах malloc реализована таким образом, что эта информация хранится отдельно. Тогда невозможно так создать подложный блок, чтобы вызвать желаемые манипуляции с указателями.
Эксплойты для ошибок при работе с целыми числами
Ошибки при работе с целыми числами представляют собой опасный источ- ник уязвимостей в программах с открытым исходным текстом. Такие ошибки были найдены в OpenSSH, Snort, Apache и библиотеке Sun RPC XDR, а также во множестве в ядре. Их сложнее обнаружить, чем ошибки переполнения стека, и разработчики хуже понимают их последствия.
К тому же ни один из современных анализаторов исходных текстов не пытается обнаружить ошибки при вычислениях с целыми числами. По большей части анализаторы лишь выполняют поиск по регулярному выражению функций из библиотеки LIBC, относительно которых известно, что они потенциально могут быть причиной уязвимости. Хотя обычно поиск ошибок при работе с целыми числами имеет смысл начинать с функций выделения памяти, но, вообще говоря, они не связаны ни с какой конкретной стандартной функцией.
Переполнение целого числа
Переполнение целого числа (integer wrapping) возникает, когда в результате увеличения целое число достигает максимально допустимого значения и при переходе через него становится равным нулю, а затем принимает небольшие значения. Точно также, переход через нуль происходит в результате уменьшения небольшого целого числа, в результате оно начинает принимать большие значения. В следующих примерах фигурирует исключительно функция malloc, но проблема не связана именно с ней или вообще какой-то отдельной функцией из библиотеки LIBC. Мы будем рассматривать только переход через максимальное значение, а, стало быть, лишь операции сложения и умножения. Но не забывайте и о переходе через ноль в результате вычитания и деления. В примере 11.7 показано, как может возникать переполнение целого при сложении.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
||
|
|
|
C |
|
E |
|
|
|
|
|
|
|
C |
|
E |
|
|
|
||||||
|
|
X |
|
|
|
|
|
|
|
|
|
X |
|
|
|
|
|
|
||||||
|
- |
|
|
|
|
|
d |
|
|
|
- |
|
|
|
|
|
d |
|
||||||
|
F |
|
|
|
|
|
|
|
i |
|
|
|
F |
|
|
|
|
|
|
|
i |
|
||
|
|
|
|
|
|
|
|
t |
|
|
|
|
|
|
|
|
|
|
t |
|
||||
P |
D |
|
|
|
|
|
|
|
|
o |
|
P |
D |
|
|
|
|
|
|
|
|
o |
||
|
|
|
|
NOW! |
r |
|
|
|
|
|
NOW! |
r |
||||||||||||
|
|
|
|
|
BUY |
|
|
|
|
|
|
|
|
BUY |
|
|
||||||||
|
|
|
|
to |
|
|
566 Глава 11. Написание эксплойтов II |
|
|
|
|
to |
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
m |
|
w |
|
|
|
|
|
|
|
|
|
m |
||
w Click |
|
|
|
|
|
|
o |
|
w Click |
|
|
|
|
|
|
o |
||||||||
|
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
|
|
||
|
. |
|
|
|
|
|
|
.c |
|
|
|
. |
|
|
|
|
|
|
.c |
|
||||
|
|
p |
df |
|
|
|
|
e |
|
|
|
|
p |
df |
|
|
|
|
e |
|
||||
|
|
|
|
|
g |
|
|
|
|
|
|
|
|
|
g |
|
|
|
||||||
|
|
|
|
|
n |
|
|
|
|
|
|
|
|
|
|
n |
|
|
|
|
||||
|
|
|
|
-xcha |
|
|
|
|
|
Если эту программу откомпилировать и выполнить, то произойдет ава--x cha |
|
|
|
|
|
рийный выход. Причина в том, что мы пытаемся записать 4294967295 букв ‘A’
âбуфер нулевой длины. Если переменной length1 присвоить значение 0xfffffffe, а length2 – значение 2, поведение будет аналогичным. Если же length1 = 0x5, à length2 = 0x1, то мы получим «нормальное поведение».
Пример 11.7 может показаться надуманным и непрактичным, так как он не предполагает никакого взаимодействия с пользователем, и программа немедленно «грохается» в «уязвимом» случае. Но в нем продемонстрированы проблемы, которые могут возникнуть и в реальных программах из-за переполнения целых чисел. Например, обращение к malloc в строке 1 на практике чаще выглядит так: buf = (char *) malloc(length1 + 1). Единица в этом случае нужна для резервирования места под нулевой байт, поскольку все строки должны завершаться нулем, иначе возможно переполнение стека или затирание кучи. Конечно же, в реальном приложении переменной length1 не будет присвоено литеральное значение 0xffffffff. Обычно оно вычисляется на основе «данных, полученных от пользователя». Логическая ошибка возникла потому, что программист предположил, что пользователь введет «нормальное» значение, а не такое большое, как 4294967295. Но не забывайте, что внешние данные могут поступать из самых разных источников: переменные окружения, аргументы
âкомандной строке, конфигурационный параметр, число отправленных приложению пакетов, поле в заголовке сетевого протокола и ттому подобных. Поэтому если значение length должно задаваться пользователем и никак иначе, то в программе следует проверять, что оно укладывается в определенный программистом разумный диапазон. Ошибка переполнения при умножении, показанная в примере 11.8, очень похожа на рассмотренную выше.
Пример 11.8. Переполнение целого числа при умножении
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 int main(void)
5 {
6unsigned int i, length1, length2;
7 char *buf;
8
9// 0xffffffff/5 â 16-ричном или 1073741824 в десятичном виде
10length1 = 0x33333333;
11length2 = 0x5;
12
13// выделить память для хранения length1*length2 + 1 байтов
14buf = (char *) malloc(length1*length2 + 1);
15 16 // напечатать длину и содержимое буфера в 16-ричном виде