Напомним, что при обработке спецификации преобразования %n
длина сформированной строки в формате целого числа будет записана по указанному адресу памяти. При вызове функции printf адрес области памяти, в которую будет помещена длина сформированной строки, должен быть записан в область стека, отведенную параметру функции printf, который соответствует спецификации преобразования %n. Для изменения содержимого любой доступной области памяти злоумышленник должен узнать ее адрес, подготовить свою форматирующую строку, разместив в нужной позиции спецификацию преобразования %n, и поместить в стеке ее и параметр функции printf, соответствующий спецификации преобразования %n. Иногда это возможно, если знать места размещения в стеке локальных переменных или характерные для программы признаки размещения в стеке контролируемых пользователем данных.Обычно злоумышленнику доступен более простой способ определения искомой позиции в стеке. В большинстве уязвимых программ форматирующая строка, передаваемая функции printf,
сама хранится в стеке как локальная переменная. Из-за того, что обычно в стеке хранится не так много локальных переменных, форматирующая строка расположена недалеко от стекового фрейма вызванной уязвимой функции printf. Злоумышленник может воспользоваться уязвимой функцией для записи данных в нужные ему адреса памяти, если он включит их в список параметров функции printf, а в форматирующей строке разместит в нужные позиции спецификации преобразования %n. Злоумышленник всегда сможет определить, откуда из стека функция printf
считывает свой параметр, соответствующий спецификации преобразования %n. Используя такие спецификации формата, как, например, %x или %p, он может воспользоваться функцией printf для перемещения по стеку до адреса, помещенного злоумышленником в стек. Предполагая, что при записи в стек данные пользователя не обрезались, злоумышленник с помощью функции printf сможет считывать данные из стека до тех пор, пока не доберется до нужного ему адреса в стеке. После этого останется только разместить в форматирующей строке спецификацию преобразования %n для записи данных по адресу, заданному злоумышленником....
Примечание
Если в форматирующую строку помещен адрес, то в нем не должно быть никаких нулевых байтов, за исключением последнего. Любой нулевой байт в форматирующей строке воспринимается как признак конца строки, поскольку любая строка в языке C является массивом символов, признаком окончания которого служит нулевой байт. Это не означает, что адреса, содержащие нулевые байты, никогда не могут использоваться. Часто адреса помещаются в стек отдельно от форматирующей строки. В этих случаях злоумышленник может записывать данные в область памяти, в адресе которой содержатся нулевые байты.
Например, если злоумышленник захочет использовать адрес из стека, хранящийся в 32 байтах от первого параметра функции printf,
то он должен определить в своей форматирующей строке 8 спецификаций преобразования %x. Спецификация преобразования %x – это спецификация вывода шестнадцатеричного целого числа без знака длиной в слово. На 32-разрядной платформе Intel длина слова равна 4 байтам. Каждый раз, встречая в форматирующей строке спецификацию преобразования %x, функция printf считывает из стека очередные четыре байта, в которых хранится переменная, соответствующая найденной спецификации. Кроме рассмотренной, для чтения данных из стека можно воспользоваться и другими спецификациями преобразования, а для записи данных в нужную область памяти существует спецификация преобразования %n. После нахождения нужного злоумышленнику адреса он может быть указан как значение переменной, соответствующей спецификации преобразования %n.
В результате в область памяти по указанному адресу будет записано количество символов отформатированной строки. Если найденный адрес правильный и память доступна для записи, то старое содержимое найденной области памяти будет перезаписано.Построение величин
Перед записью в память злоумышленник может прибегнуть к различным ухищрениям для получения нужного значения записываемой переменной целого типа. Для этого он может задать в спецификации преобразования ширину выводимого поля, увеличивая число символов в отформатированной строке.
int main
{
// test.c
printf(“start: %10i end\n”,10);
}