Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #45, АВГУСТ 2004 г.

Ломаем структуры

Мысла Владислав

Спецвыпуск Xakep, номер #045, стр. 045-036-3


Переполнение файлового потока

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

Листинг

Пример 2. Псевдофункция дозаписи файла

......

void CreateLog(char* szUser)

{ FILE *hFile;

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

char szUserName[0xFF];

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

strcpy( szUserName, szUser );

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

...... }

Если ты внимательно изучил предыдущий пример, то, наверно, заметил, что в нем используется небезопасная функция strcpy(), которая, как известно, может привести к перезаписи памяти, что, собственно, и произошло. Осталось только разобраться, как ведет себя приложение после переполнения. Если передать функции строки длиной меньше 255, она корректно выполнит и без проблем завершит свою работу. Но, если ей передать в качестве параметра строку из букв «А» длиной 255+8, она, вместо того чтобы записать в файл строку «File created successful», запишет «ААААА». Более того, если передать строку длиной хотя бы 255+255, то функция просто аварийно завершит свою работу. Давай посмотрим внимательнее, что происходит. Касательно записи в файл строки «ААААА», то причина в том, что, переполняя переменную szUserName, ты перезаписываешь значение переменной szHeader. А если увеличить размер передаваемой строки, то в результате будет перезаписан указатель на файл. Что это нам дает? Все очень просто. Если раньше программа ввела логин в защищенном файле, то теперь ты можешь перенаправить ее вывод в любой другой файл или на стандартные потоки stdout или srderr! Для этого необходимо просто перезаписать указатель на файл на какой-либо другой указатель, который ссылается на поддельную структуру файла. Но и это еще не все! Раз есть возможность подделки потока, то можно его заставить перезаписать любой участок памяти нужными данными, даже в том случае, когда размер переполняемого буфера такой маленький, как в последнем примере. Технология осуществления данной идеи проста. Перед тем как происходит запись данных в файл, они проходят буферизацию. Адрес буфера указан в самой структуре потока:

Листинг

struct _iobuf {

char *_ptr; //!!

int _cnt;

char *_base;//!!

int _flag;

int _file;

int _charbuf;

int _bufsiz;

char *_tmpfname;

};

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

Are you mind overflows?

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

Назад на стр. 045-036-2  Содержание  Вперед на стр. 045-036-4