Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Авторские статьи (https://forum.antichat.xyz/forumdisplay.php?f=31)
-   -   Недостатки форматной строки. (https://forum.antichat.xyz/showthread.php?t=157693)

Kerny 19.11.2009 14:43

Недостатки форматной строки.
 
Недостатки форматной строки.

Строка форматирования в языке С++, и ему подобных используется множеством программистов, по всему миру, вообще это одно из самых удобных средств форматирования выводимых данных, но и за удобство приходится платить, как и все остальное это решение придумано и реализовано человеком, а посему не лишено недостатков (как сказали в одном фильме: «Наше совершенство в нашем несовершенстве»)

http://ru.wikipedia.org/wiki/Printf

printf - обобщённое название семейства функций или методов стандартных или широкоизвестных коммерческих библиотек, или встроенных операторов некоторых языков программирования, используемых для форматного вывода — вывода в различные потоки
значений разных типов, отформатированных согласно заданному шаблону. Этот шаблон определяется составленной по специальным правилам строкой (форматной строкой).



Немного о вводе.

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

char user[13];
char pass[13];
gets(&user[0]);
gets(&pass[0]);


Фрагмент кода кажется безопасным и правильным, но на самом деле его выполнение может привести к критическим последствиям, как минимум отказ в обслуживание, как максимум захват контроля на ПК, который использует уязвимое приложение(если оно сетевое, и выше приведенный фрагмент кода , есть ничто иное как просьба авторизоваться), все дело тут в том что функция Gets, никак не проверяет количество символов введенных пользователем, для переменных выделяется по 13 байт, а если пользователь вводит больше символов, чем запланировано, то они выходят за границы буфера и затирают находящиеся за ним значения (Другие переменные, иногда даже участки кода, если user и pass, объявлены локально, то они хранятся в стеке, а как следствие, можно затереть и адрес возврата из функции, что позволяет, выполнить атаку Срыв стека). Ошибка тут вовсе не программиста, а непосредственно разработчиков функции (не сделали проверки).


Вот аналог выше приведенного кода на Delphi (Object Pascal)

var
user:string[13];
pass:string[13];
begin
readln(user);
readln(pass);
end;

Этот код полностью безопасен в отличие от своего собрата на языке С++, функция которая читает введенные пользователем данные(readln) получает в регистре ecx значение размера буфера, затем сохраняет его и сравнивает с количеством введенным пользователем символов, и берет только первые символы пока не кончится буфер. Что - бы защититься от данной «проблемы» достаточно использовать безопасный аналог функции gets, а именно fgets.


char user[13];
char pass[13];
fgets(&user[0],13,stdin);
fgets(&pass[0],13,stdin);


Защита от переполнения функции Read/Readln в Delphi

http://img6.imageshost.ru/imgs/09052...79ba/def48.jpg


Читаем стек.

Теперь перейдем непосредственно к форматной строке:

#include "stdafx.h"
int main()
{
char str[17];
fgets(&str[0],17,stdin);
printf(&str[0]);
}

С первого взгляда все в порядке, но тут кроется одна большая неприятность, если запустить
проект и ввести в поле ввода %x %x %x, то в консоль выведется вовсе не это, а 241fe4 12f7bc 7ffdd000


(У вас результат может отличаться и даже должен, это как повезет)



Обратим внимание на текст, который мы вводим, вообще — то он эквивалентен записи:

printf(“%x %x %x”);

Откомпилируйте проект и снова увидите нечто подобное первому результату, получается что мы как — бы «обманываем программу» (не находите, очень похоже на sql – инъекцию)

%x — говорит о том, что нужно достать значение из стека, и записать в буфер, а из буфера вывести на экран (ну а вообще-то переводит переменную типа int в строку).

Нормальный расклад таков:

printf(“%x %x %x”,a,b,c);

Где а,b,c – некоторые переменные, тогда на экран будут выведены значения этих переменных.

Состояние стека при нормальном раскладе:



Погрузившись в отладчике в printf, мы понимаем, что произошло, когда мы не передали ни каких аргументов, но передали три спецификатора, это значит ей нужно распечатать три аргумента, она «не знает», что аргументов ей не дали и поэтому достает их с того места где по ее мнению они должны быть.

http://img6.imageshost.ru/imgs/09052...103/t9409b.jpg




Состояние стека, когда аргументы не были переданы:

http://img6.imageshost.ru/imgs/09052...a45f/7a3b6.jpg

На месте Случайных данных, должны быть переменные, но мы их не передали, следовательно, там «валяется» разная информация - значение других переменных, адреса возврата, и т.д. Таким образом, мы получаем возможность читать значения в стеке, где при чрезмерном везение оказываются пароли и всякая полезная для взломщика информация.

Читаем код программы.

Помимо %x, у printf существует еще огромная куча спецификаторов, один из них %s, спецификатор %s указывает на строку, то есть в переменной хранится адрес, нате данные, которые нужно вывести, запустите предыдущий пример и введите %s. Посмотрим, что произошло.


Мы считали данные по адресу 7C910208, потому что функция «подумала» - это аргумент. А теперь задумаемся, что если — бы мы могли заменить этот аргумент, на свой собственный, например на адрес точки входа в программу? Да мы получили бы код программы до первого попавшегося ноля, т.к ноль завершает строку, но это не всегда возможно, как повезет.

Перезапись ячейки памяти.

Спецификатор %n, для этого как раз и предназначен, запускайте и вводите, 2222%%x%x%x%x%n, %x - «возьмут на себя» те ячейки, в которые запись запрещена, если все же попытаться записать туда, то произойдет исключение и программа «упадет», мы же записываем по адресу 12ff50, а туда запись разрешена...


Спецификаторы очень опасная вещь, хотя и очень удобна в использование, в Object Pascal ничего подобного нет и от этого программирование на нем уже более безопасно, вместо спецификаторов там используются функции, например строка

printf(“%x ”,a);

На Delphi выглядит так:

write(inttostr(a));

Можно понять, что тут используется функция inttost(), и по-моему такое решение гораздо удачливее. Как же защититься? Можно, например, фильтровать вводимые пользователем данные, как это делается в случае с sql-инъекцией, но это было - бы полным идиотизмом, лучше

printf(str);

Заменить на

printf(”%s”,str);

И все будет ок...

Сегодня мы рассмотрели недостатки строки формата, конечно с помощью них врядли удастся получить контроль над удаленной тачкой, но то что они становятся верными помощниками и друзьями взломщика сомневаться не приходится.

sa-sec.org
(с)Kerny

Жду ваших отзывов.

p.s Здесь я не сравнивал Delphi и C так, что базар по этому поводу не разводить.

_nic 19.11.2009 15:17

Цитата:

Строка форматирования в языке С++, и ему подобных используется множеством программистов
Автор путает С и С++ ,в последнем есть такая штука как #include <iostream>

Kerny 19.11.2009 15:42

Цитата:

Сообщение от _nic
Автор путает С и С++ ,в последнем есть такая штука как #include <iostream>

языке С++, и ему подобных


Время: 22:50