Эксплоит под wu-ftpd kas1e Xakep, номер #052, стр. 052-054-1 Или format string уязвимости Сегодня мы продолжим знакомство с различными уязвимостями и рассмотрим ошибку типа format string. Format string появился на свет в июне 1999 года, а уже в июне 2000 количество эксплоитов, основанных на этой уязвимости, достигло критической отметки. Они проявлялись как в маленьких утилитах, так и в огромных серверных приложениях. В течение года производители софта пытались закрывать на это глаза, но в конце концов опомнились. Как ты догадываешься, большинство ошибок в программном обеспечении объясняется кривым программированием или ленью. Format string не исключение, как станет понятно при более близком знакомстве. Итак, поехали. Работа у программиста довольно напряженная, необходимо следить за каждым байтом, оценивать ситуацию, представлять различные возможности поведения программы и т.д. К примеру, часто приходится использовать в программе 'C' строки, оканчивающиеся нулевым байтом, и не всегда есть возможность уследить за таким кодом. Отсюда возникают разные проблемы. Так и в случае с format string. Скажем, программист решает сэкономить на размере и не указывает дополнительных 'форматных' аргументов: printf(string); // выводим строку (без указания каким способом выводить) вместо: printf(%s, string); // выводим строку (с указанием способа) Видимо не все программисты знают, что если форматный аргумент не указан, то их поиск будет производиться в любом случае. И если какой-то из них будет найден, то в стеке произойдут соответствующие преобразования (из предыдущих статей по buffer overflow можно представить, чего добиваются люди, имея доступ к стеку). Однако нас больше интересует конкретный символ форматирования. Это %n. Как ни странно, при описании символов форматирования он почти никогда не упоминается. А вот что говорится о нем в манах: "Число символов, выведенных до этого момента, сохраняется по адресу целого числа, указанному аргументом-указателем типа int * (или variant). Преобразование аргументов не происходит". Что же это значит? А то, что данный аргумент позволяет производить запись в переменную-указатель, даже если она используется в функции для вывода на экран! Т.е. можно записывать данные по адресу, на который указывает второй аргумент. Кроме этого, он подсчитывает еще каждый символ, появляющийся в самой строке форматирования. Возьмем простейший пример: #include <stdio.h> void main() { int a; // целая переменная 'a' char *buff = "1111111111"; // символьный буфер 'buff' с десятью единицами printf("%s%n\n", buff, &a); // выводим строку "1111111111", // которая содержит 10 символов printf("a = %d\n", a); // выводим записанное значение (число символов) // записанное в целую переменную 'a' } Откомпилим и запустим: # gcc printf_test.c -o printf_test # ./printf_test 1111111111 a = 10 Да, %n действительно подсчитывает символы. Но есть еще один нюанс - подсчет не всегда бывает таким точным :). Сразу скажу, что %n считает количество символов, которые будут выведены предположительно. Т.е. если ты, скажем, через snprintf ограничишь буфер 100 символами, а потом повторишь строки из предыдущего примера, то переменная 'a' будет показывать вместо 10 - 100. Вот более детальное объяснение: сначала %n подсчитывает количество символов, потом записывает по адресу, указанному во втором аргументе, и только потом строка уменьшается при копировании в буфер. Т.е. сначала строка расширяется, потом считывается, а затем уменьшается ;). |