Дерни printf за хвост Крис Касперски aka мыщъх Спецвыпуск Xakep, номер #045, стр. 045-032-3 Теперь введем несколько спецификаторов "%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 B3 00. Откуда взялся нуль? Это ASIIZ-строка, и нуль служит ее завершителем. Если бы его здесь не оказалось, спецификатор "%s" вывел бы на экран намного больше информации. Фактически мы реализовали аналог бейсик-функции peek, судьбоносность которой уже обсуждалась в другой статье, однако не спеши открывать на радостях пиво. Данная реализация peek'а очень ограничена в возможностях. Указатель, сформированный в начале буфера, не может содержать в себе символ нуля, а потому первые 17 Мбайт адресного пространства недоступны для просмотра. Указатель, сформированный в конце буфера, может указывать практически на любой адрес, поскольку старший байт адреса удачно совпадает с символом завершающего нуля, однако, чтобы дотянуться до такого указателя, потребуется пересечь весь буфер целиком, а это не всегда возможно. |