Раздел: Документация
0 ... 35 36 37 38 39 40 41 ... 102 - ----.-JHytpj, Большинство авторов ограничивается исключительно вопросами нослед0Ва тельного переполнения автоматических буферов, оттесняя остальные вцдЫг]е рснолнсний на задний план, в результате чего у многих читателей создаеТ[, выхолощенное представление о проблеме. На самом деле мир переполняющие буферов значительно шире, многограннее и интереснее, в чем мы сейчас и убе димся. ПОХОРОНЕННЫЙ ПОД ГРУДОЙ РАСПЕЧАТОК ИСХОДНОГО И ДИЗАССЕМБЛЕРНОГО КОДА Как происходит поиск переполняющихся буферов и как осуществляется проектирование shell-кода? Первым делом выбирается объект нападения, роль которого играет уязвимое приложение. Если вы хотите убедиться в собственной безопасности или атаковать строго определенный узел, вы должны исследовать конкретную версию конкретного программного пакета, установленного на конкретной машине. Если же вы стремитесь прославиться на весь мир или пытаетесь сконструировать мощное оружие, дающее вам контроль над десятками тысяч, а то и миллионами машин, Bain выбор становится уже не таким однозначным. Прежде всего это должна быть широко распространенная и по возможности малоисследованная программа, исполняющаяся с наивысшими привилегиями и сидящая на портах, которые не так-то просто закрыть. Разумеется, с точки зрения межсетевого экрана все порты равноценны и ему абсолютно все равно, что закрывать. Однако пользователи сетевых служб и администраторы придерживаются другого мнения. Если от 135-го порта, используемого червем Love San, в подавляющем большинстве случаев можно безболезненно отказаться (лично автор «Записок...» именно так и поступил), то без услуг того же web-сервера обойдешься едва ли. Чем сложнее и «монстроузнее» исследуемое приложение, тем больше вероятность обнаружить в нем критическую ошибку. Следует также обращать внимание и на формат представления обрабатываемых данных. Чаще всего переполняющиеся буферы обнаруживаются в синтаксических анализаторах, выполняющих парсинг текстовых строк, однако большинство этих ошибок уже давно обнаружено и устранено. Лучше искать переполняющиеся буфера там, где до вас их никто не искал. Народная мудрость утверждает: хочешь что-то хорошо спрятать — положи это на самое видное место. На фоне нашумев ших эпидемий Love Sanа и Slappera с этим трудно не согласиться. Кажется невероятным, что такие очевидные переполнения до последнего времени оста вались необнаруженными! Наличие исходных текстов одновременно желательно и нежелательно. Же-па телыю — потому что они существенно упрощают и ускоряют поиск переполня ющихся буферов, а нежелательно... по той же самой причине! Как говори больше народу — меньше кислороду. Действительно, трудно рассчитывать паи что-то новое в исходнике, зачитанном всеми до дыр. Отсутствие исходи ьгх теК стов существенно ограничивает круг исследователей, отсекая многочислен») армию прикладных программистов и еще большую толпу откровенных непр0 -- в Здесь, в прокуренной атмосфере ассемблерных команд, выжива-lpt,cc"oHT0T кто программирует быстрее, чем думает, а думает быстрее, чем го-fT jiiiiub можст удержать в голове сотни структур данных, буквально на ilT Ч от • вор11 м уровне ощущая пх взаимосвязь и каким-то шестым чувством уга-фи3"4 ]СаКОу] направлении нужно копать. Собственный программистский опыт ДЬ,ваЯ 1ЬКО приветствоваться. Так лете вжиться в привычки, характер и об-мо*еТ нИЯ разработчика исследуемого приложения. Задумайтесь, а как бы РазМ „и данную задачу, окажись на его месте? Какие бы могли допустить ВЫ си? Где бы проявили непростительную небрежность, соблазнившись ком-""ктностью кода и элегантностью листинга? Кстати об элегантности. Бытует мнение, что неряшливый стиль программного кода неизбежно провоцирует программиста на грубые ошибки (и ошибки переполнения в том числе). Напротив, педантично причесанная программа ошибок, скорее всего, не содержит и анализировать ее — означает напрасно тратить свое время. Как знать... Автору приходилось сталкиваться с вопиюще небрежными листингами, которые работали как часы, потому что были сконструированы настоящими профессионалами, наперед знающими, где подстелить соломку. Встречались и по-академически аккуратные программы, дотошно и не по одному разу проверяющие все, что только можно проверить, но буквально нашпигованные ошибками переполнения. Тщательность сама по себе еще ни от чего не спасает. Для предотвращения ошибок нужен богатый программистский опыт, — и опыт, оставленный граблями в том числе. Но вместе с опытом зачастую появляется и вальяжная небрежность — своеобразный «отходяк» от юношеского увлечения эффективностью и оптимизацией. Признаком откровенного непрофессионализма является пренебрежение «аеппами или безграмотное использование последних. В частности, если размер буфера buff определяется через MAXBUFSIZE, то и размер копируемой в него строки должен ограничиваться им же, а не MAXSTRSIZE, заданным в отдельном define. Обращайте внимание и на характер аргументов функций, Работающих с блоками данных. Передача функции указателя без сообщения размера блока — частая ошибка начинающих, равно как и злоупотребление функциями strcpy/strncpy. Первая — небезопасна (отсутствует возможность огра-на ( ТЬ ПРедельно Допустимую длину копируемой строки), вторая — ненадеж- утствует возможность оповещения о факте «обрезания» хвоста строки, и1еСТившегося в буфер, что само по себе может служить весьма нехилым ником ошибок). АйроЩО n f сембте ка 11еРеполнения найдена. Что дальше? А дальше только дизас-Te.nbHv3 е пытаитесь выжать из исходных текстов хоть какую-то доиолни-Леци ИнФормацию. Порядок размещения переменных в памяти не опреде-МожетРаКТИЧеСКИ ,1ИКОГДа не совпадает с порядком их объявления в программе. Нет, и 0,Сазаться так, что большинства из этих неременных в памяти попросту МИзат*1 эазме1цены компилятором в регистрах либо же вовсе отброшены оп-тиНГ1) °Р°м как ненужные (попутно заметим, что все демонстрационные лис-ся в "веденные в этой главе, рассчитывают, что переменные расиолагают-чти в порядке их объявления). Впрочем, не будем забегать вперед. Дезассемблирование — это отдельная тема которой посвящены книги «Фундаментальные основы хакерства» и «Образ мышления IDA», так что не будем лишний раз повторяться. ЦЕЛИ И ВОЗМОЖНОСТИ АТАКИ Конечная цель любой атаки — заставить систему сделать что-то «нехорошее» Такое, чего нельзя добиться легальным путем. Существует, по меньшей мере четыре различных способа реализации атаки: 1.Чтение секретных переменных. 2.Модификация секретных переменных. 3.Передача управления на секретную функции программы. 4.Передача управления на код, переданный жертве самим злоумышленником. ЧТЕНИЕ СЕКРЕТНЫХ ПЕРЕМЕННЫХ На роль секретных переменных в первую очередь претендуют пароли на вход в систему, а также пароли доступа к конфиденциальной информации. Все они так или иначе содержатся в адресном пространстве уязвимого процесса, зачастую располагаясь по фиксированным адресам (под «входом в систему» здесь подразумевается имя пользователя и пароль, обеспечивающие удаленное управление уязвимым приложением). Еще в адресном пространстве процесса содержатся дескрипторы секретных файлов, сокеты, идентификаторы TCP/IP-соединений и многое другое. Разумеется, вне текущего контекста они не имеют никакого смысла, но могут быть использованы кодом, переданным жертве злоумышленником и осуществляющим, например, установку «невидимого» TCP/IP-соединения, прячась под «крышей» уже существующего. Ячейки памяти, хранящие указатели на другие ячейки, «секретными», строго говоря, не являются, однако знание их содержимого значительно облегчает атаку. В противном случае атакующему придется определять опорные адреса вслепую. Допустим, в уязвимой программе содержится следующий код. char *р = maHoc(MAX BUF SIZE), где р - указатель на буфер, содержащий секретный пароль. Допустим также, что в программе имеется ошибка переполнения, позволяющая злоумышленнику читать содержимое любой ячейки ад ресного пространства. Весь вопрос в том, как этот буфер найти. Сканировать всю кучу целиком не только долго, но и небезопасно, так как можно легко натолкнуться на невыделенную страницу памяти, и тогда выполнение про цесса аварийно завершится. Автоматические и статические переменны в этом отношении более предсказуемы. Поэтому атакующий должен сначала прочитать содержимое указателя р, а уже затем — секретный пароль, накото рый он указывает. Разумеется, это всего лишь пример, которым возможно переполняющего чтения не ограничиваются. Само же переполняющее чтение реализуется, по меньшей мере, четырьмя дующими механизмами: «потерей» завершающего нуля в строковых буфер 0 ... 35 36 37 38 39 40 41 ... 102
|