Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги хакеры / журнал хакер / специальные выпуски / Специальный выпуск 45_Optimized

.pdf
Скачиваний:
15
Добавлен:
20.04.2024
Размер:
11.27 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

журнал «DVD ЭКСПЕРТ» просто и доступно о домашнем кинотеатре!

С сентября ищите в продаже

первый номер

ежемесячного журнала

100 страниц полезной информации

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

O V E R F L O W

-xcha

 

 

 

 

 

 

 

 

 

 

 

 

 

B U F F E R

 

 

 

 

 

 

 

30 ОСНОВЫ МАССИВНОЕ ПЕРЕПОЛНЕНИЕ

Мысла Владислав aka DigitalScream (digitalscream@real.xakep.ru)

МАССИВНОЕ

ПЕРЕПОЛНЕНИЕ

ÀТЫ ЗНАЕШЬ, ЧТО ТАКОЕ ARRAY OVERFLOW?

Îставались считанные секунды… "Не может быть, что нельзя угадать 10-значный код для входа, если системой пользуется миллион клиентов", - думал он… Код за кодом - закрыто! Но вдруг блок ввода заморгал…

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Массив - это последовательность элементов одинаковой структуры. Например, массив чи- сел - это последовательность чисел и ни- чего другого.

По сути, переполнение массивов - это обычное переполнение буфера, происходящее при обработке массивов.

Óпользуются переполнением буфера в борь-

бе за нужную информацию. Постоянно на-æå много лет хакеры

ходятся новые ошибки, их связывают одну с другой, классифицируют. В наше время найти ошибку переполнения в распространенном продукте довольно сложно. Порой кажется, что ошибки нет или ею нельзя воспользоваться. А чаще всего именно безобидные, на первый взгляд, ошибки приводят к возможности несанкционированного управления работой приложения. Чтобы воспользоваться такими уязвимостями, надо проверить, при обработке каких данных происходит ошибка. В этой статье речь пойдет об ошибках работы с массивами.

В примере 1 описан псевдокласс Door, который занимается управлением входной дверью, причем, перед тем как закончить свою работу, он сохраняет все неправильно введенные пароли в виде логов.

Какую ошибку хранит в себе этот код?

ЧТЕНИЕ ДАННЫХ ИЗ ПАМЯТИ

При работе с массивами данных программист использует массивы. Если необходимо хранить в массиве объекты

ПРИМЕР 1. ПРОТОКОЛИРОВАНИЕ ОШИБОК

...

Door::Door() { lIndex = 0;

TypedUID = new UserID[0xFF]; }

...

void Door::LogBadInput( UserID uidInput ) {

TypedUID[ lIndex ] = uidInput; lIndex ++; }

...

Door::~Door() {

for( long lOffset = 0;lOffset < lIndex; ++lOffset ) WriteToLog(FormatLogMessage( "UserID", TypedUID[lOffset], sizeof(UserID) );

delete [] TypedUID; }

...

Фрагмент памяти для примера 2

или структуры, то он может с легкостью создавать массивы, в качестве элементов которых будут нужные ему данные. Но, если нужно создать массив строк, то

âкачестве элементов необходимо брать указатели на строки, а не их самих. Это определено тем, что элементом массива может быть любой тип данных, с предопределенным объемом. А строка может состоять из 1, 2, 6, 165 байтов, поэтому в массив записывается именно указатель на строку (он занимает 4 байта), который никак не зависит от ее длины. Давай посмотрим на пример 2. Что здесь неверно? На первый взгляд, все в порядке: функции GetUserIDByIndex передают индекс пользователя, а она возвращает его идентификатор. Для хранения идентификаторов используется массив UserIDList из 0x07 элементов (нумерация начинается с нуля). Но что будет, если мы передадим GetUserIDByIndex число, большее 0x07, то есть что произойдет при попытке доступа к элементу с индексом больше размерности массива?

Взгляни на рисунок. Это участок памяти, в которой хранится список идентификаторов пользователей, и объявленная сразу же за ним переменная,

âкоторой хранится пароль админа. Если ты попытаешься передать функции GetUserIDByIndex в качестве аргу-

ПРИМЕР 2. ПЕРЕПОЛНЕНИЕ МАССИВОВ

...

UserIDList = new byte[0x07]; Password = new char[0x07];

...

byte Door::GetUserIDByIndex(long lIndex)

{

return UserIDList[lIndex - 1 ]; }

...

ПРИМЕР 3. ИСПОЛЬЗОВАНИЕ ПЕРЕПОЛНЕНИЯ МАССИВОВ #1

...

void Door::LoginIDByIndex(long lIndex, char[0x07] szPassword ) {

if ( !CheckPassword( GetUserIDByIndex(lIndex), szPassword ) )

ShowMessage("Bad password for user %i", GetUserIDByIndex(lIndex) );}

...

мента единицу, то она вернет идентификатор первого пользователя, двойку - второго и т.д. Но, если передать ей число 8, функция все равно вернет результат. Откуда взялся 8-й элемент, если размер списка - 7? Все очень просто: индекс элемента - это неявный указатель на местоположение элемента в памяти. Неявный потому, что адрес вычисляется как адрес_массива+индекс_элемента*размер_элемента. В нашем примере размер элементов - 1 байт, значит индекс и будет смещением относительно на- чала массива. Но! В памяти сразу за массивом находится пароль админа, а так как идентификаторов всего 7, то 8-й индекс - это первый символ пароля. Таким образом, если существует функция, предназначенная для аутентификации пользователя, которой передают индекс пользователя и пароль, причем она имеет вид, показанный в примере 3 (в случае неправильного пароля выводит сообщение об ошибке, содержащее идентификатор пользователя), то, передав ей индекс 8, ты получишь сообщение вида "Bad password for user 97".

Если интерпретировать число 97 как код символа, то результатом будет "а" - первый символ пароля. И если теперь

ХАКЕРСПЕЦ 08(45) 2004

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

последовательно передавать функции

 

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

-xcha

 

 

LoginIDByIndex числа 9, 10 и т.д., ты

 

 

 

 

 

 

 

 

 

сможешь узнать пароль админа.

ПЕРЕЗАПИСЬ ДАННЫХ В ПАМЯТИ

Массивы могут хранить в себе опасность более высокую, чем чтение памяти. Куда интереснее случаи, когда есть возможность записи в массивы (пример 4).

Давай посмотрим, что происходит. SetData - это функция, которая копирует данные юзера в свою область памяти. Причем она не проверяет размер копируемых данных, а приемник lpTable ограничен размером 0xFF(255). И, как ты уже догадался, если передать функции данные, размер которых больше 255, то элементы из lpData с индексом, большим 254 (нумерация с нуля), будут скопированы не в lpTable, а в szPassword, который находится в куче прямо после lpData. Поэтому все числа, что не поместились в lpData, перезапишут пароль. Осталось только передать свой пароль :-). Для этого можно передать функции буфер, состоящий из:

-255*4 байт данных (перезаписать lpTable),

-числа 0x61616161 (перезаписать пароль),

-числа 0x00000000 (закончить строку пароля нулевым байтом).

ПРИМЕР 4. ИСПОЛЬЗОВАНИЕ ПЕРЕПОЛНЕНИЯ МАССИВОВ #2

long lpTable [0xFF]; char szPassword [0xFF];

...

void SetData (long* lpData, long lSize ) {

for (long lOffset = 0; lOffset<lSize; ++lOffset ) lpTable[lOffset] = lpData[lOffset];

}

...

В результате пароль изменится на "аааа" и может быть использован для авторизации в системе.

ПЕРЕЗАПИСЬ ДАННЫХ В СТЕКЕ ИЛИ КУЧЕ

Как видишь, можно манипулировать ходом исполнения программы, используя ошибки в работе с массивами. Взгляни на пример 5. У нас имеется функция, которая создает массив и записывает в указанный элемент какое-либо число, и функция, которая показывает пароль админа.

Массив lpNewData является временным, и поэтому память для него выделяется в стеке. Учитывая архитектуру стека и местоположение буфера, можно говорить о том, что, если в качестве индекса указать число, превосходящее размеры массива, оно перезапишет собой данные, находящиеся в верхних адресах. А среди них есть и адрес возврата для выхода из функции, поэтому если передать функции параметры 0х101 и любое другое число "A", то при выходе из функции управление передастся на адрес, указанный в переменной "A". Так, например, если знать адрес функции ShowPassword и указать его в качестве второго параметра, то программа покажет пароль

ПРИМЕР 5. ИСПОЛЬЗОВАНИЕ ПЕРЕПОЛНЕНИЯ МАССИВОВ #3

char szPassword [0xFF];

...

void CreateData( long lDefaultIndex, long lValue ) {

long lpNewData[0xFF]; ZeroMemory( lpNewData, 0xFF );

lpNewData[lDefaultIndex] = lValue;

...................................

}

void ShowPassword() { ShowMessage( "Password: %s",

szPassword ); }

...

31

админа. Но это уже - переполнение стека, рассмотрение которого в рамки данной статьи не входит. Если память под массив выделяется в куче, то получить управление можно, перезаписав глобальную таблицу смещений функций. Способов много и все определяется архитектурой уязвимого приложения.

ОБХОД ОГРАНИЧЕНИЙ НА РАЗМЕРНОСТЬ МАССИВА

Однако не все так просто. Во избежание переполнений в обработке массивов программеры используют проверки на указанный индекс элемента. Если он больше размера массива, то операция отменяется (пример 6).

Но и здесь есть выход! Переменная intDefaultIndex является числом, но числом со знаком. Поэтому, хотя проверка на размер производится, значе- ние не проверяется на отрицательность. Это значит, что, если в прошлом примере ты указал номер элемента 0x101, то теперь необходимо просто вычислить его: индекс = старый_индекс + 0x80000000. Таким образом, появляется возможность обхода проверки на указанный индекс элемента.

Теперь ты знаком с уязвимостями класса Array Overflow. Нужно иметь в виду возможность такого взлома и думать о своей защите. E

ПРИМЕР 6. ИСПОЛЬЗОВАНИЕ ПЕРЕПОЛНЕНИЯ МАССИВОВ С ПРОВЕРКОЙ ИНДЕКСА

char szPassword [0xFF];

...

void CreateData(int intDefaultIndex, int intValue ) {

long lpNewData[0xFF]; if( intDefaultIndex > 0xFF ) return;

ZeroMemory( lpNewData, 0xFF ); lpNewData[intDefaultIndex] = intValue;

...................................

}

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

O V E R F L O W

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

B U F F E R

 

Более детальную информацию о цело- численных переполнениях ты можешь найти в статье "Integer Overflow".

Атака на переполнение массива может быть использована как способ получе- ния контроля над исполнением программы.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

 

 

 

 

 

 

 

 

 

 

 

F

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

 

 

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

32

ОСНОВЫ

ДЕРНИ PRINTF ЗА ХВОСТ

w Click

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

Крис Касперски aka мыщъх

 

w

 

 

 

 

 

 

 

o

w

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

F L O W

-xcha

 

 

 

 

ДЕРНИ PRINTF

 

 

 

 

 

 

 

 

 

O V E R

 

 

 

 

 

 

 

ЗА ХВОСТ

 

F E R

 

 

 

 

 

 

 

ФОРМАТИРОВАННЫЙ ВЫВОД ПОД ПРИЦЕЛОМ

 

U F

 

 

 

 

 

 

 

зык Си выгодно отличается от Паскаля поддержкой спецификаторов, представляющих собой мощный

 

 

 

 

 

 

 

 

 

 

 

 

B

 

 

 

 

 

 

 

ßинструмент форматного ввода/вывода. Настолько мощный, что фактически образует язык внутри языка,

 

 

 

 

 

 

 

 

 

 

 

полигон для взломщика.

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

До сих пор не зафиксировано ни одной атаки с помощью форматного ввода на приложения Windows NT.

Для аварийного завершения программы достаточно вызвать нарушение доступа, обратившись к невыделенной, несуществующей или заблокированной ячейке памяти.

Îвода не многочисленны и встречаются,

главным образом, в UNIX-приложениях, гдешибки форматного вы-

традиции терминального режима все еще сильны. По некоторым оценкам, в 2002 году было обнаружено порядка 100 уязвимых приложений, а в 2003 - свыше 150! Атаке подверглись сервера баз данных, вращающихся под Oracle, и сервисы UNIX, такие, как syslog или ftp. Ни одной атаки на приложения Windows NT до сих пор не зафиксировано. Это не значит, что Windows NT лучше, просто графический интерфейс не располагает к интенсивному использованию форматного вывода, да и количество консольных утилит под NT очень невелико; в общем, нельзя считать, что он находится в безопасности. И сейчас ты в этом убедишься!

ИСТОЧНИКИ УГРОЗЫ

Основных источников угрозы три: а) навязывание бажной программе собственных спецификаторов; б) врожденный дисбаланс спецификаторов; в) естественное переполнение буфера-приемника при отсутствии проверки на предельно допустимую длину строки.

Состояние стека на момент вызова функции printf

НАВЯЗЫВАНИЕ СОБСТВЕННЫХ СПЕЦИФИКАТОРОВ

Если пользовательский ввод попадет в строку форматного вывода (что происходит довольно часто) и находящиеся в нем спецификаторы не будут отфильтрованы, злоумышленник сможет манипулировать интерпретатором форматного вывода по своему усмотрению, вызывая ошибки доступа, читая и перезаписывая ячейки памяти и при благоприятных условиях захватывая управление удаленной системой.

Рассмотрим следующий пример, к которому мы не раз будет обращаться в дальнейшем.

f(){

char buf_in[32], buf_out[32]; printf("введи имя:"); gets(buf_in); sprintf(buf_out, "hello, %s!\n", buf_in); printf(buf_out);

}

РЕАЛИЗАЦИЯ DOS

Для аварийного завершения программы достаточно вызвать нарушение доступа, обратившись к невыделенной, несуществующей или заблокированной ячейке памяти. Это легко. Встретив спецификатор "%s", интерпретатор форматного вывода извлекает из стека парный ему аргумент, трактуя его как указатель на строку. Если же тот отсутствует, интерпретатор хватает первый попавшийся указатель и начинает читать содержимое памяти по этому адресу до тех пор, пока не встретит нуль или не нарвется на запрещенную ячейку. Политика запретов варьируется от одной операционной системы к другой, в частности, при обращении по адресам 00000000h - 0000FFFFh и 7FFF000h - FFFFFFFFh Windows NT всегда возбуждает исключение. Остальные же адреса в зависимости от состояния кучи, стека и статической памяти могут быть как доступными, так и нет.

Откомпилируем пример, приведенный выше, и запустим его на выполнение. Вместо своего имени введем

строку "%s". Программа выдаст следующее:

введи имя: %s hello, hello, %s!\n!"

Чтобы понять, что такое "hello, %s!" и откуда оно здесь взялось, необходимо проанализировать состояние стека на момент вызова printf(buf_out), в чем нам поможет отладчик, например тот, который интегрирован в Microsoft Visual Studio (см. скрин).

Первым идет двойное слово 0012FF5Ch (на микропроцессорах архитектуры Intel младший байт располагается по меньшему адресу, то есть все числа записываются в памяти в обратном порядке). Это указатель, соответствующий аргументу функции printf, которому, в свою очередь, соответствует буфер buf_out, содержащий непарный спецификатор "%s" и заставляющий функцию printf извлекать следующее двойное слово из стека, которое представляет собой обыкновенный мусор, оставленный предыдущей функцией. По стечению обстоятельств он (мусор и указатель одновременно) указывает на тот же самый buf_out, и потому нарушения доступа не происходит, зато слово "hello" выводится дважды.

Будем рыть дальше, снимая со стека следующую последовательность адресов: 00408000h (указатель на строку "hello, %s!\n"), 0012FF3Ch (указатель на buf_out), 0012FF3Ch (снова он), 0040800Ch (указатель на строку "введи имя:"), 73257325h (содержимое буфера buf_in, трактуемое как указатель, между прочим указывающий на невыделенную ячейку памяти).

Таким образом, первые пять спецификаторов "%s" проходят сквозь интерпретатор форматного вывода вполне безболезненно, а вот шестой посылает его в космос. Процессор выбрасывает исключение, и выполнение программы аварийно прекращается. Разумеется, спецификаторов не обязательно должно быть ровно шесть - до остальных все равно не дойдет управление.

ХАКЕРСПЕЦ 08(45) 2004

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

Реакция программы на шесть спецификаторов ”%s”

Обрати внимание: Windows NT приводит именно тот адрес, который мы и ожидали.

РЕАЛИЗАЦИЯ PEEK

Для просмотра содержимого памяти уязвимой программы можно воспользоваться спецификаторами "%X", "%d" и "%c". Спецификаторы "%X" и "%d" извлекают парное им двойное слово из стека и выводят его в шестнадцатеричном или десятичном виде соответственно. Спецификатор "%c" извлекает парное двойное слово из стека, преобразует его до однобайтового типа char и выводит в символьном виде, отсекая три старших байта. Таким образом, наиболее зна- чимыми из всех являются спецификаторы "%X" и "%c".

Каждый спецификатор "%X" отображает всего лишь одно двойное слово, лежащее в непосредственной близости от вершины стека (точное расположение зависит от прототипа вызываемой функции). Соответственно, N спецификаторов отображают 4*N байт, а максимальная глубина просмотра равна 2*C, где C - предельно

допустимый размер пользовательского ввода в байтах. Увы! Читать всю память уязвимого приложения нам никто не даст, отдавая на растерзание лишь крошечный кусочек, в котором, если повезет, могут встретиться секретные данные (например, пароли) или указатели на них. Впрочем, узнать текущее положение указателя тоже неплохо. Но обо всем по порядку.

Запустим нашу демонстрационную программу и введем спецификатор "%X". Она ответит:

введи имя: %X hello, 12FF5C!

Откуда взялось 12FF5C? Обращаясь к дампу памяти, мы видим, что это - двойное слово, следующее за аргументом buf_out и представляющее собой результат жизнедеятельности предыдущей функции, или, попросту говоря, мусор. Ну и какая нам радость от этого? Буфер содержит наш собственный ввод, в котором заведомо нет ничего интересного. Но это лишь часть айсберга. Как уже говорилось в статье, посвященной перепол-

ФУНКЦИИ, ПОДДЕРЖИВАЮЩИЕ ФОРМАТИРОВАННЫЙ ВЫВОД

Услугами интерпретатора форматного ввода/вывода пользуется множество функций, не только printf и не только в консольных программах. Графические

приложения и серверное программное обеспечение, исполняющееся под Windows NT, активно используют функцию sprintf, выводящую отформатированную строку в оперативный буфер.

Перечисленные в таблице функции сами по себе не опасны. Опасными их делает нали- чие пользовательского ввода в форматном аргументе. Именно такие участки кода и нужно искать при исследовании программы на уязвимость.

функция

 

назначение

 

 

 

fprintf

ASCII

форматированный вывод в

fwprintf

UNICODE

ôàéë

 

 

 

fscanf

ASCII

форматированный ввод с

 

 

fwscanf

UNICODE

потока

 

 

 

 

printf

ASCII

форматированный вывод в

wprintf

UNICODE

stdout

 

 

 

 

scanf

ASCII

форматированный ввод с

wscanf

UNICODE

stdin

 

 

 

_snprintf

ASCII

форматированный вывод в

 

 

_snwprintf

UNICODE

буфер с ограничителем длины

 

 

 

sprintf

ASCII

форматированный вывод в

swprintf

UNICODE

буфер

 

 

 

sscanf

ASCII

форматированный ввод из

 

 

swscanf

UNICODE

буфера

 

 

 

vfprintf

ASCII

форматированный вывод в

vfwprintf

UNICODE

поток (stream)

 

 

 

vprintf

ASCII

форматированный вывод в

vwprintf

UNICODE

stdout

 

 

 

_vsnprintf

ASCII

форматированный вывод в

_vsnwprintf

UNICODE

буфер с ограничителем длины

 

 

 

vsprintf

ASCII

форматированный вывод в

буфер

 

 

33

няющимся буферам, для передачи управления на shell-код необходимо знать его абсолютный адрес, который в большинстве случаев неизвестен, и спецификатор "%X" как раз и выводит его на экран!

Теперь введем несколько спецификаторов "%X", для удобства разделив их пробелами:

введи имя: %X %X %X %X %X %X %X hello, 12FF5C 408000 12FF3C 12FF3C 40800C 25205825 58252058!

Обрати внимание на два последних двойных слова. Да это же содержимое буфера пользовательского ввода! Ведь ACSII-строка "%X " в шестнадцатеричном представлении выглядит как "25 58 20".

Идея - сформировать указатель на интересующую нас ячейку памяти, положить его в буфер, а затем натравить на него спецификатор "%s", чи- тающий память вплоть до встречи с нулевым байтом или запрещенной ячейкой. Нулевой байт не помеха, достаточно сформировать новый указатель, расположенный за его хвостом. Запрещенные ячейки намного коварнее - всякая попытка доступа к ним вызывает аварийное завершение программы, и, до тех пор пока администратор не поднимет упавший сервер, атакующему придется скучать и пить пиво. А после перезапуска расположение уязвимых буфером данных может оказаться совсем иным, что обесценит все ранее полученные результаты. Конечно, волков бояться - в лес не ходить, но и соваться в воду, не зная броду, тоже не стоит. В общем, со спецификатором "%s" следует быть предельно осторожным, а то недолго и DoS схлопотать.

Допустим, мы хотим прочитать содержимое памяти по адресу 77F86669h (по ней можно определить версию операционной системы, так как у всех она разная). Расположение буфера пользовательского ввода нам уже известно - актуальные данные на- чинаются с шестого двойного слова (см. листинг). Остается подготовить боевую начинку. Вводим целевой адрес, записывая его в обратном порядке и набирая непечатные символы с помощью <ALT> и цифровой клавиатуры. Добавляем к ним шесть спецификаторов "%X", "%d" или "%c" (поскольку содержимое этих ячеек нас никак не волнует, подойдут любые). Добавляем опознавательный знак, например звездочку или двоеточие, за которым будет идти спецификатор вывода строки "%s". И, наконец, скармливаем полученный результат программе (опознавательный знак необходим для того, чтобы быстро определить, где кончается мусор, а где начинаются актуальные данные)

Если перевести символы в строке после двоеточия в шестнадцатерич- ную форму, получится 8B 46 B3 40 3E »

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

O V E R F L O W

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

B U F F E R

 

Если пользовательский ввод попадет в строку форматного вывода, злоумышленник сможет манипулировать интерпретатором форматного вывода по своему усмотрению.

Unicodeфункции используют для завершения строки двойной символ нуля и к одиноч- ным нулям относятся довольно лояльно.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

F

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

P

D

 

 

 

 

 

 

 

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NOW!

 

r

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

 

34

ОСНОВЫ

ДЕРНИ PRINTF ЗА ХВОСТ

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

p

df

 

 

 

 

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

L O W

-xcha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Просмотр дампа памяти по вручную сформированному указателю

 

V E R F

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Каждый спецификатор занимает два

 

O

 

 

 

 

 

 

 

 

 

 

 

 

 

 

байта и снимает со стека четыре.

 

E R

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

B3 00. Откуда взялся нуль? Это ASIIZ-

 

 

 

 

 

 

Получилось! Мы сделали это!

 

F

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

строка, и нуль служит ее завершите-

 

 

 

 

Действуя и дальше таким макаром,

 

F

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

лем. Если бы его здесь не оказалось,

 

 

 

 

мы сможем просмотреть практически

 

U

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

спецификатор "%s" вывел бы на эк-

 

 

 

 

всю доступную память программы.

 

B

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ран намного больше информации.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Фактически мы реализовали аналог

 

 

 

 

 

РЕАЛИЗАЦИЯ POKE

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

бейсик-функции peek, судьбоносность

 

 

 

 

 

 

 

Спецификатор "%n" записывает в

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

которой уже обсуждалась в другой

 

 

 

 

парный ему указатель количество вы-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

статье, однако не спеши открывать на

веденных на данный момент байт, тем

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

радостях пиво. Данная реализация

 

 

 

 

самым позволяя нам модифициро-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

peek'а очень ограничена в возможнос-

 

 

 

 

вать содержимое указателей по свое-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

тях. Указатель, сформированный в на-

 

 

 

 

му усмотрению. Обрати внимание: мо-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

чале буфера, не может содержать в

 

 

 

 

дифицируется не сам указатель, а то,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

себе символ нуля, а потому первые

на что он указывает!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

snprintf

17 Мбайт адресного пространства не-

 

 

Перед демонстрацией нам необходи-

 

 

 

 

 

 

доступны для просмотра. Указатель,

 

 

 

 

мо найти в стековом хламе подходя-

 

 

 

 

 

 

значитель-

 

 

 

 

 

 

 

 

 

 

но безопас-

сформированный в конце буфера, мо-

 

 

 

 

щий указатель, предварительно про-

 

 

 

 

 

 

íåå sprintf,

жет указывать практически на любой

читав его содержимое строкой типа

 

 

 

 

 

 

òàê êàê

 

 

 

 

 

 

 

 

 

контролиру-

адрес, поскольку старший байт адреса

 

 

 

 

"%X %X %X", как мы уже делали. Вы-

 

 

 

 

 

 

ет размер

удачно совпадает с символом завер-

 

 

 

 

 

 

берем 12FF3Ch, указатель на буфер

 

 

 

 

 

 

буфера-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

шающего нуля, однако, чтобы дотя-

 

 

 

 

пользовательского ввода buf_in. Что-

 

 

 

 

 

 

приемника и

 

 

 

 

 

 

 

 

 

 

никогда не

нуться до такого указателя, потребует-

 

 

 

 

бы его достать, нужно снять со стека

 

 

 

 

 

 

устроит пе-

ся пересечь весь буфер целиком, а

 

 

 

 

два двойных слова; этим у нас займут-

 

 

 

 

 

 

реполнение.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

это не всегда возможно.

 

 

 

 

ся спецификаторы "%c%c".

.rdata:004053B4 aMicrosoftVisua db 'Microsoft Visual C++ Runtime Library',0

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

Поскольку модификация буфера .

 

 

 

 

 

e

 

 

 

p

df

 

 

 

g

.c

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-x cha

 

 

 

 

 

осуществляется после его вывода на экран, доказательства перезаписи памяти приходится добывать в отладчи- ке. Загрузив подопытную программу в Microsoft Visual Studio (или любой другой отладчик на твой вкус), установи точку останова по адресу 401000 (адрес функции main) или, подогнав к ней курсор (Ctrl+G, Address, "401000", <Enter>), нажми Ctrl+F10 для пропуска инструкций стартового кода, совершенно не интересующего нас в настоящий момент.

Пошагово трассируя программу по F10 (Step Over - трассировка без захода внутрь функций), введи заданную строку, когда тебя об этом попросят (экран консоли начнет призывно мигать) и продолжай трассировку вплоть до достижения строки 0040103Сh, вызывающей функцию printf. Теперь перейди в окно дампа памяти и введи в адресной строке "ESP", сообщая отладчику, что нам угодно просмотреть содержимое стека, а затем вернись к дизассемблерному коду и нажми F10 еще раз.

Содержимое буфера пользовательского ввода немедленно изменится, подсвечивая ядовито-красным цветом число "0F 00 00 00", записанное в его начале. Перезапись выбранной ячейки памяти успешно состоялась!

Напоминаю, если спецификаторы перекрывают буфер пользовательского ввода, мы можем самостоятельно сформировать указатель, переза-

 

 

 

 

 

 

 

 

 

 

Дизассемблер утверждает, что по

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

адресу 004053B4h в нашей демон-

 

Функция sprintf относится к числу

 

 

 

 

 

 

 

 

 

 

страционной программе расположен

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

копирайт фирмы Microsoft.

самых опасных.

Модифици-

Давай выведем его на экран. Как мы

руемая с

помним, начало буфера соответству-

 

 

 

 

 

 

 

 

 

 

 

помощью

ет шестому спецификатору. Каждый

 

 

 

 

 

 

 

 

 

 

 

хитрости со

 

 

 

 

 

 

 

 

 

 

 

спецификатор занимает два байта и

 

Теперь определимся с числом, кото-

 

 

 

 

писывая выбранные ячейки памяти

специфика-

 

 

 

 

 

тором

 

 

 

 

снимает со стека четыре. Еще два

рое мы хотим записать. Записывать

 

 

 

 

произвольным образом. То есть поч-

"%n",

байта уходят на спецификатор "%s",

можно только маленькие числа - на

 

 

 

 

ти произвольным. К ограничениям

ячейка

 

 

 

 

должна при-

выводящий строку. Так сколько всего

большие просто не хватит размера

 

 

 

 

выбора целевых адресов теперь еще

надлежать

надо передать спецификаторов прог-

буфера. Сойдемся на числе 0Fh (это

 

 

 

 

присоединяются и ограничения выбо-

странице с

 

 

 

 

атрибутом

рамме? Составляем простенькое ли-

нечетное число, четные приносят

 

 

 

 

ра перезаписываемого значения, ко-

PAGE_READ

нейное уравнение и сходу решаем

несчастье :). Считаем: два символа

 

 

 

 

торые, между прочим, очень жестки.

WRITE, â

 

 

 

 

его, получая в ответе двенадцать.

выводят спецификаторы, снимающие

 

 

 

 

 

 

 

 

противном

 

 

 

 

 

 

 

 

случае про-

Одиннадцать из них выгребают со

лишние двойные слова с верхушки

 

ДИСБАЛАНС

цесс сгене-

 

стека все лишнее, а двенадцатый вы-

стека, семь приходится на строку

СПЕЦИФИКАТОРОВ

рирует иск-

лючение.

водит содержимое расположенного

"hello, " (äà-äà, îíà òîæå â äîëå), òîã-

 

 

 

 

 

 

 

Каждому спецификатору должен

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

за ним указателя.

да у нас остается: 0Fh - 02h - 07h =

 

 

 

 

соответствовать парный аргумент. Но

 

 

 

 

 

 

 

 

 

 

Указатель формируется тривиаль-

= 06h. Шесть символов, которые мы

 

 

 

 

должен еще не значит обязан. Ведь

 

 

 

 

 

 

 

 

 

 

но: открываем ASCII-таблицу симво-

должны ввести самостоятельно. Они

спецификаторы и аргументы програм-

 

 

 

 

 

 

 

 

 

 

лов (как вариант - запускаем HIEW) и

могут быть любыми, например, "qwer-

 

 

 

 

мисту приходится набивать вручную,

 

 

 

 

 

 

 

 

 

 

переводим 4053B4h в символьное

ty" или что-то в этом роде. Остается

 

 

 

 

и ему ничего не стоит ошибиться!

 

 

 

 

 

 

 

 

 

 

представление. Выворачиваем его на-

добавить спецификатор "%n", и

 

 

 

 

Транслятор откомпилирует такую

 

 

 

 

 

 

 

 

 

 

изнанку и вводим в программу, при

сформированную строку можно пере-

 

 

 

 

программу вполне нормально, воз-

 

 

 

 

 

 

 

 

 

 

необходимости используя цифровую

дать программе:

 

 

 

 

можно, негромко выругавшись при

 

 

 

 

 

 

 

 

 

 

клавиатуру и клавишу <ALT> (см.

 

 

 

 

 

 

 

этом и выдав на экран предупреждаю-

 

 

 

 

 

 

 

 

 

 

скрин).

введи имя: qwerty%c%c%n

 

 

 

 

щий warning. Но что произойдет по-

 

 

 

 

 

 

 

 

 

 

 

hello, qwerty\ !

òîì?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Если аргументов окажется больше,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

чем спецификаторов, "лишние" будут

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

проигнорированы. В противном слу-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

чае функция форматированного вы-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

вода, не зная сколько ей аргументов

Формирование указателя в конце буфера и вывод его на экран

 

 

 

 

 

 

 

реально передали, снимет со стека

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ХАКЕРСПЕЦ 08(45) 2004

 

 

 

df-xchan

 

 

 

 

 

 

ÈÃÐÛdf-x chan

 

 

 

 

 

 

 

 

 

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

to

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

w Click

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

.c

 

 

 

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

ПО КАТАЛОГАМ

 

g

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

e

 

 

 

 

 

 

 

 

 

 

 

 

 

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

С ДОСТАВКОЙ НА ДОМ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

www.gamepost.ru

 

 

 

www.e-shop.ru

 

 

 

 

 

 

 

 

 

 

 

 

 

 

РЕАЛЬНЕЕ,

 

PC Accessories

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ЧЕМ В МАГАЗИНЕ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

БЫСТРЕЕ,ЧЕМ ТЫ ДУМАЕШЬ

 

 

 

 

 

 

 

 

 

 

 

 

$865,99

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Демонстрация перезаписи ячейки памяти

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

первый встретившийся ей мусор. События будут разви-

Øëåì i-O Display Systems

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

i-glasses HRV

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ваться по сценарию, описанному выше ("Навязывание

$89,99

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

собственных спецификаторов"), с той лишь разницей, что

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

навязывать спецификаторы злоумышленник сможет толь-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ко косвенно или не сможет вовсе.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ПЕРЕПОЛНЕНИЕ БУФЕРА-ПРИЕМНИКА

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Функция sprintf относится к числу самых опасных, и

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

все руководства по безопасности в один голос твердят,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

что лучше пользоваться ее безопасным аналогом -

Master Pilot w

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

snprintf. Почему? Природа форматированного вывода та-

/Programmer

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

кова, что предельно достижимую длину результирующей

$849,99

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

строки очень трудно рассчитать заранее. Рассмотрим сле-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

дующий код:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

f()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

char buf[???];

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

sprintf(buf,"имя:%s возраст:%02d вес:%03d рост:%03d\n",

Øëåì/ i-O Display

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

name, age, m, h);

Systems i-glasses

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

...

 

SVGA

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

}

 

$79,99

 

$259,99

 

$149,99

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Как вы думаете, каких размеров буфер нам потребуется?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Из неизвестных факторов здесь присутствуют: длина стро-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ки name и "длина" целочисленных переменных age, m, h,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

преобразуемых функцией sprintf в символьное представ-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ление. Кажется логичным, если мы отводим два столбца на

 

Клавиатура/ Microsoft

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

возраст и по три на рост и вес, то за вычетом имени и дли-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Wireless Optical Desktop

Джойстик

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ны форматной строки нам потребуется всего 8 байт. Пра-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Джойстик/

for Bluetooth

 

CH FlightStick Pro USB

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

вильно? А вот и нет! Если строковое представление пере-

Freestyler Bike

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

менных не умещается в отведенных ему позициях, оно ав-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

томатически расширяется, дабы избежать усечения ре-

$149,99

 

$219.99

 

$219.99

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

зультата. В действительности же, десятичное представле-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ние 32-разрядных переменных типа int требует резервиро-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

вания 11 байт памяти, в противном случае возникает угроза

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

переполнения буфера.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ЗАКЛЮЧЕНИЕ

Клавиатура/ Auravision

 

Педали/CH Pro

 

Джойстик/ CH Flight

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Ошибки обработки спецификаторов - частный случай

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

EluminX Illuminated

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Pedals USB

 

Sim Yoke USB

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

проблемы интерполяции строк. Некоторые языки, напри-

Keyboard

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

мер Perl, позволяют не только форматировать вывод, но

Заказы по интернету – круглосуточно!

 

e-mail: sales@e-shop.ru

 

 

 

 

 

 

 

 

 

 

 

и внедрять переменные и даже функции (!) непосред-

Заказы по телефону можно сделать

ñ 09.00 äî 21.00 ïí - ïò

 

 

 

 

 

 

 

 

 

 

 

ственно в саму выводимую строку, что существенно уп-

 

 

 

ñ 10.00 äî 19.00 ñá - âñ

 

 

 

 

 

 

 

 

 

 

 

рощает и ускоряет программирование. К сожалению, хо-

WWW . E - SHOP . RU

WWW . GAMEPOST . RU

 

 

 

 

 

 

 

 

 

 

 

рошие идеи становятся фундаментом воинствующего

(095) 928-6089 (095) 928-0360 (095) 928-3574

 

 

 

 

 

 

 

 

 

 

 

вандализма. Удобство не сочетается с безопасностью.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Что удобно программировать - удобно и ломать, хотя об-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ратное утверждение неверно.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

В общем, не воспринимай языковые возможности как

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

догму. Подходи к ним творчески, отбирая только лучшие

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

функции и операторы. E

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

O V E R F L O W

-xcha

 

 

 

 

 

 

 

 

 

 

 

 

 

B U F F E R

 

 

 

 

 

 

 

36 ОСНОВЫ ЛОМАЕМ СТРУКТУРЫ

Мысла Владислав aka DigitalScream (digitalscream@real.xakep.ru)

ЛОМАЕМ

СТРУКТУРЫ

СТРУКТУРА НЕ ВСЕГДА КРИТЕРИЙ ЦЕЛОСТНОСТИ

ßвно происходило переполнение, но что перезаписывалось и как это влияло на дальнейшее исполнение программы, было загадкой. Что-то смутно напоминало ситуацию с выделением памяти в куче через функцию malloc()…

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

Î

шибки переполнения

 

 

 

 

ПРИМЕР 1. ПРОГРАММА ПРОВЕРКИ ИМЕНИ И ПАРОЛЯ

 

буфера часто требуют

 

 

 

 

от хакера проявить

 

 

 

 

 

 

 

смекалку. Особенно это

struct credentials { char name[0x10]; char pass[0x10];

 

 

 

 

касается тех моментов,

bool admin; };

 

когда появляется необходимость в

 

void CheckUser(credentials* cUser) {

подделке каких-либо данных. Если ты

 

 

if(!strcmp(cUser->name,"adm") && !strcmp(cUser->pass,"adm") )

 

сталкивался с переполнениями в ку-

 

че, то понимаешь, о чем идет речь. В

cUser->admin = true; }

 

данной статье я приведу несколько

int _tmain(int argc, _TCHAR* argv[]) {

 

примеров того, как эксплуатируются

credentials* cUser = new credentials;

 

уязвимости, требующие фальсифика-

 

cUser->admin = false; char* name = argv[1];

ции структурированных участков па-

 

 

char* pass = argv[2];

 

мяти, и как этим может воспользо-

 

ваться злоумышленник для исполне-

memcpy( cUser->name, name, strlen(name) );

 

ния своего кода. Поехали!

memcpy( cUser->pass, pass, strlen(pass) );

 

Все данные в памяти представлены

cUser->name[strlen(name)] = '\0';

 

одинаково. Не имеет значения, с чем

 

cUser->pass[strlen(pass)] = '\0';

работает программа: со строками, чис-

 

 

CheckUser(cUser);

лами, структурами и т.д. Так или иначе,

 

 

все данные - это набор байт опреде-

......

ленной длины, и ничего более. Все ог-

 

return 0; }

 

раничения в обработке созданы прог-

 

раммистом, и они существуют только

 

 

 

 

 

на уровне понимания кода. Если ты

ПЕРЕПОЛНЕНИЕ СТРУКТУР

 

является частью структуры. Давай

создаешь строку, то она в памяти ни-

 

 

При переполнении буфера ха-

 

взглянем на пример 1.

 

 

чем не отличается от массива чисел

кер имеет дело, в основном, со

 

Это программа, которая проверяет

или координат какого-либо многоу-

строками, и единственное ограни-

 

имя пользователя и пароль. Если они

гольника. Другое дело - интерпрета-

чение, которое накладывается на

 

совпадают и равны "adm" - пользова-

ция данных. Если это строка, то ты зна-

shell-код, - это отсутствие в нем ну-

 

тель считается администратором, ес-

ешь, что она должна заканчиваться

левых байтов. Но бывают ситуа-

 

ли не совпадают - гостем. Сразу мож-

нулем, если это координаты, то их ко-

ции, когда переполнение происхо-

 

но заметить, что для заполнения

личество должно быть парным и т.д.

дит в обработке строки, которая

 

структуры credentials используется

 

 

 

 

 

команда memcpy без проверки на

 

 

 

 

 

длину передаваемых данных. Этим

 

 

 

 

 

 

 

 

 

 

можно воспользоваться, чтобы пере-

 

 

 

 

 

записать память приложения и, воз-

 

 

 

 

 

можно, получить управление над ним.

 

 

 

 

 

Чтобы подтвердить теорию, нужно по-

 

 

 

 

 

играть с передаваемыми приложению

 

 

 

 

 

данными. Если запустить программу с

 

 

 

 

 

параметрами "demo demo", она ска-

 

 

 

 

 

жет, что ты являешься гостем, "adm

 

 

 

 

 

adm" - вернет, что ты админ. Кажется,

Так должна работать прога...

 

 

 

 

все в порядке, но не стоит забывать о

 

 

 

 

 

 

 

 

 

 

переполнении! Передай приложению

 

 

 

 

 

строку: hacker aaaaaaaaaaaaaaaa\x01, и

 

 

 

 

 

она опознает тебя как администрато-

 

 

 

 

 

ра. Причина такого поведения ясна -

 

 

 

 

 

передавая длинный пароль, ты пере-

 

 

 

 

 

записываешь значение переменной

 

 

 

 

 

admin, и именно она хранит в себе

 

 

 

 

твой статус в системе. Если перепол-

А так - не должна :-)

 

 

 

 

нения не происходит, переменная

 

 

 

 

 

 

ХАКЕРСПЕЦ 08(45) 2004

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

×ïîê! :-)

По сути, переполнение структур ничем не отличается от переполнений строк.

admin равна нулю, но поскольку ты передал пароль из 16 символов "а" и одного байта "0x01", а размер переменной pass - всего 16, то "0x01" перезапишет значение admin и система сочтет тебя администратором.

ПЕРЕПОЛНЕНИЕ КЛАССОВ

Все сказанное касается также и классов. Возьмем предыдущий пример, но на этот раз все данные будут храниться не в структуре, а в классе. Более того, пусть теперь можно будет указать данные не для одного, а для двух пользователей.

Давай опять проверим работу полу- чившегося приложения, которое также чувствительно к длине передаваемых ему строк. Приложение работает корректно, если длина строки не превышает 16 символов; если нарушить это правило, то перед вами откроются другие возможности, совсем не предусмотренные программистом :-). Так, например, при передаче в качестве пароля для первого пользователя строку из 30 байт приложение крити- чески завершит свою работу.

Причина происшедшего в том, что для хранения класса используется динамическая память. Переполняя буфер, ты можешь изменить значе- ния статических переменных, это верно, но ошибка не из-за этого. Посмотри на отладочную информацию. Видно, что после того как первый экземпляр класса получил логин и пароль, у второго изменилось зна- чение VTable. А VTable - это адрес таблицы методов класса. В ней находятся все методы, помеченные как виртуальные, а все остальные находятся в сегменте кода. Адрес этой

таблицы хранится по указателю объекта, а поскольку они создаются в стеке, то адрес таблицы лежит там же. Но на самом деле все просто: функции хранятся в области кода, переменные фиксированной длины лежат в той же области памяти, где расположен сам объект. Чтобы перезаписать область кода, необходимо копировать буфер очень большей длины, и поэтому, даже если есть возможность записи в сегмент кода, ей не всегда можно воспользоваться из-за расстояния от него до стека или кучи. Однако для большей гибкости и реализации наследования программисты используют виртуальные функции. Они ничем не отлича- ются от обычных за исключением метода доступа к ним. Если точнее, адрес функции не жестко прописан в коде, а находится в таблице, которая может меняться в ходе выполнения программы. Адрес таблицы хранится в первых 4 байтах объекта, а поэтому, если есть переполнение, то его можно перезаписать. Именно это и произошло в рассмотренном примере. Когда ты передал строку "name AAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAA name pass", пароль, хранящийся в первом экземпляре, перезаписал виртуальную таблицу методов второго. Чтобы в этой ситуации получить контроль над приложением, необходимо найти такой адрес в памяти, который не содержит в себе нулей и указывает на адрес, по которому находится твой shell-код. Надеюсь, теперь тебе понятно, в чем дело. Существует еще множество методов пере-

полнения объектов в памяти, но эта »

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

t

 

P

D

 

 

 

 

 

 

 

 

o

 

 

 

 

NOW!

r

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

o

 

w

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

df

 

 

 

 

e

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

O V E R F L O W

-xcha

 

 

 

 

 

 

 

 

 

 

 

 

 

B U F F E R

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

F

 

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

P

 

 

 

 

 

 

NOW!

o

38

ОСНОВЫ

ЛОМАЕМ СТРУКТУРЫ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w Click

to

 

 

 

 

 

m

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

тема заслуживает отдельного рас-

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ПРИМЕР 2. ПСЕВДОФУНКЦИЯ ДОЗАПИСИ ФАЙЛА

 

 

p

df

 

 

 

g

.c

 

смотрения, а пока что мы остано-

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-x cha

 

 

 

 

 

вимся на переполнении структури-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

рованных данных.

 

 

......

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ПЕРЕПОЛНЕНИЕ

 

 

 

 

void CreateLog(char* szUser)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

{ FILE *hFile;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ФАЙЛОВОГО ПОТОКА

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

char szHeader[] = "File created successful\n";

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Взглянем на пример 2.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

char szUserName[0xFF];

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Если ты внимательно изучил преды-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

дущий пример, то, наверно, заметил,

 

 

 

hFile = fopen( "default.log", "a" );

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

что в нем используется небезопас-

 

 

 

strcpy( szUserName, szUser );

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ная функция strcpy(), которая, как из-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

fprintf( hFile, "%s", szHeader );

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

вестно, может привести к перезаписи

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

...... }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

памяти, что, собственно, и произош-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ло. Осталось только разобраться, как

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ведет себя приложение после пере-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

дать все это достаточно сложно из-за

рированных данных, хотя говорить о та-

 

 

 

 

 

 

 

 

 

 

полнения. Если передать функции

 

 

 

 

 

 

 

 

 

 

строки длиной меньше 255, она кор-

реализации системы работы с файла-

 

 

 

 

 

ких уязвимостях можно очень долго. Ос-

 

 

 

 

 

 

 

 

 

ректно выполнит и без проблем за-

ми. Куда более верным решением яв-

новное, что ты должен вынести из этой

 

 

 

 

 

 

 

 

 

 

 

вершит свою работу. Но, если ей пе-

ляется перезапись массива _stdbuf .

статьи, - это то, что, по сути, переполне-

 

 

 

 

 

 

 

 

 

 

 

редать в качестве параметра строку

Перезаписывая файловые потоки, не-

ние структур ничем не отличается от пе-

 

 

 

 

 

 

 

 

 

из букв "А" длиной 255+8, она, вмес-

обязательно пытаться пользоваться

реполнений строк. Они просто требуют

 

 

 

 

 

 

 

 

 

 

 

то того чтобы записать в файл строку

ими для передачи управления на

от атакующего более точных знаний о

 

 

 

 

 

 

 

 

 

 

 

 

"File created successful", запишет

shell-код. Если длина буфера позво-

работе программы, но зато взамен за-

 

 

 

 

 

 

 

 

 

 

 

 

"ААААА". Более того, если передать

ляет перезаписать просто адрес возв-

частую дают более простые способы

 

 

 

 

 

 

 

 

 

 

 

 

строку длиной хотя бы 255+255, то

рата, то нужно использовать предос-

для передачи управления на shell-код.

 

 

 

 

 

 

 

 

 

 

 

 

функция просто аварийно завершит

тавленную возможность. Но при этом

 

 

Напоследок хотелось бы напомнить,

 

 

 

 

 

 

 

 

 

 

свою работу. Давай посмотрим вни-

корректно перезаписать указатель на

что статья рассчитана на программис-

 

 

 

 

 

 

 

 

 

 

мательнее, что происходит. Касатель-

файл, чтобы дать функции успешно

тов, которые решили повысить уро-

 

 

 

 

 

 

 

 

 

 

 

 

но записи в файл строки "ААААА", то

завершить свою работу.

вень безопасности в своих приложе-

 

 

 

 

 

 

 

 

 

 

 

причина в том, что, переполняя пере-

 

 

 

 

 

 

 

 

 

 

ниях. Все, о чем ты прочитал, носит

 

 

 

 

 

 

 

 

 

 

 

 

менную szUserName, ты перезаписы-

ARE YOU MIND OVERFLOWS?

только информативный характер и не

 

 

 

 

 

 

 

 

 

 

ваешь значение переменной

 

 

 

На этом завершим небольшой экс-

должно быть использовано в неза-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

szHeader. А если увеличить размер

курс в технологии перезаписи структу-

 

 

 

 

 

 

конных целях. E

 

 

 

 

 

 

 

 

 

 

 

 

передаваемой строки, то в результа-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

те будет перезаписан указатель на

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

файл. Что это нам дает? Все очень

 

РЕАЛИЗАЦИЯ АУТЕНТИФИКАЦИИ С ПОМОЩЬЮ

 

 

 

 

 

 

 

 

 

 

 

 

просто. Если раньше программа вве-

 

КЛАССОВ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ла логин в защищенном файле, то те-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

перь ты можешь перенаправить ее

 

 

 

class credentials { private: bool admin;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

вывод в любой другой файл или на

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

public: char name[0x10]; char pass[0x10];

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

стандартные потоки stdout или srderr!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Для этого необходимо просто переза-

 

 

 

public: SetCredentials(char* _name, char* _pass );

 

 

 

 

 

 

 

 

 

 

 

 

писать указатель на файл на какой-

 

 

 

virtual void Administrate();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

либо другой указатель, который ссы-

 

 

 

private: virtual void Admin(); };

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

лается на поддельную структуру

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

credentials::SetCredentials(char* _name, char* _pass ) {

 

 

 

 

 

 

 

 

 

 

 

 

файла. Но и это еще не все! Раз есть

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

memcpy( name, _name, strlen(_name) );

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

возможность подделки потока, то

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

можно его заставить перезаписать

 

 

 

memcpy( pass, _pass, strlen(_pass) );

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

любой участок памяти нужными дан-

 

 

 

name[strlen(_name)] = '\0'; pass[strlen(_pass)] = '\0';

 

 

 

 

 

 

 

 

 

 

 

 

ными, даже в том случае, когда раз-

 

 

 

admin = false; }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

мер переполняемого буфера такой

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

void credentials::Administrate() {

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

маленький, как в последнем примере.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

if( !strcmp(name,"adm") && !strcmp(pass,"adm") ) {

 

 

 

 

 

 

 

 

 

 

 

 

Технология осуществления данной

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

идеи проста. Перед тем как происхо-

 

 

 

Admin(); } ....... }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

дит запись данных в файл, они про-

 

 

 

int _tmain(int argc, _TCHAR* argv[]) {

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ходят буферизацию. Адрес буфера

 

......

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

указан в самой структуре потока:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

bool TwoUsers = false;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

struct _iobuf {

 

 

 

if( argc==5 ) TwoUsers = true;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

char *_ptr; //!!

 

......

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

int _cnt;

 

 

 

credentials* cUserFirst; credentials* cUserSecond;

 

 

 

 

 

 

 

 

 

 

 

 

char *_base;//!!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

cUserFirst = new credentials();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

int _flag;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

if( TwoUsers ) cUserSecond = new credentials();

 

 

 

 

 

 

 

 

 

 

 

 

int _file;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.....

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

int _charbuf;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

int _bufsiz;

 

 

 

if( TwoUsers )cUserSecond->SetCredentials( argv[3], argv[4] );

 

 

 

 

 

 

 

 

 

 

 

 

char *_tmpfname;

 

 

 

cUserFirst->SetCredentials ( argv[1], argv[2] );

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

};

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

....

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

И если их выставить в интересую-

 

 

 

cUserFirst->Administrate();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

if( TwoUsers ) cUserSecond->Administrate();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

щие тебя адреса, то при записи в

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

файл данные перезапишут собой

 

... }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

нужные фрагменты памяти. Воссоз-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ХАКЕРСПЕЦ 08(45) 2004