[Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 31.03.12 19:21
Оценка: 20 (3)
Довольно странно, что один из классов угроз оказался знаком столь малому проценту от числа проголосовавших. Предлагаю решить небольшую задачку по устранению всех узявимостей в существующем коде, а потом расскажу подробнее, что это за класс и почему с ним наверняка сталкивались почти все разработчики, хотя видимо не знали, что оно так называется. Заодно проверим, действительно ли все знают, как появляются SQL-инъекции и как с ними бороться

Легенда: у нас есть движок форума, одна из страниц которого позволяет посмотреть список всех участников, их статистику (ну там, количество сообщений, рейтинг и т.п.) и фильтровать список по значениям этих полей (например, отобразить всех участников с количеством сообщений больше 100, но меньше 200). Вся информация о пользователе (логин, пароль, никнейм, почта, всевозможные данные его статистики и т.п.) хранятся в одной таблице Users. Ну или вьюхе — не суть. Разработчики реализовали это тривиальным образом и, кроме того, оставили за собой возможность расширять критерии SQL-запроса GET-параметром, но только в том случае, если движок собран в DEBUG-конфигурации:

var fieldName = Request["field"] ?? "Id";
var minValue  = Request["min"]   ?? Int32.MinValue;
var maxValue  = Request["max"]   ?? Int32.MaxValue;
var debugStr  = Request["debug"] ?? string.Empty;

#if DEBUG
    var debugTemplate = "{0}";
#else
    var debugTemplate = string.Empty;
#endif

var queryTemplate = string.Format(
    "SELECT Id, Nickname, Rating, MessageCount, TopicCount FROM Users WHERE {0} >= @minValue AND {0} <= @maxValue {1} ORDER BY {0}",
    fieldName.Replace("'", string.Empty).Replace(" ", string.Empty).Replace("\\", string.Empty).Replace(",", string.Empty).Replace("(", string.Empty).Replace(")", string.Empty),
    debugTemplate
);

var selectCommand = string.Format(queryTemplate, debugStr);
var cmd = new SqlCommand(selectCommand, dataConnection);
cmd.Parameters.Add(new SqlParameter("@minValue", minValue));
cmd.Parameters.Add(new SqlParameter("@maxValue", maxValue));
// Ну и так далее. Далее осуществляется SQL-запрос, и отобранные поля уходят в виде HTML-таблицы в ответ на HTTP-запрос.


Найдите все уязвимости, допущенные в этом коде

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re: [Этюд] По мотивам голосования
От: Jack128  
Дата: 31.03.12 20:10
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

ну с пьяного глаза первое(и последнее, честно говоря), что бросается в глаза — это безумно сложное выражение для escape'инга fieldName.
Если заменить его на что нить попроще, типа:


if (!fieldName.All(Char.IsLetter))
   throw new Exception();



то лично я был бы гораздо спокойнее. Ну и естественно — нуно считать, что дебаг версия — никому левому не доступна. Иначе возможно всё что угодно.
Re[2]: [Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 31.03.12 20:17
Оценка:
Здравствуйте, Jack128, Вы писали:

J>
J>if (!fieldName.All(Char.IsLetter))
J>   throw new Exception();
J>


J>то лично я был бы гораздо спокойнее.


Хм, ок. А сколько и каких уязвимостей закроет это изменение?

J>Ну и естественно — нуно считать, что дебаг версия — никому левому не доступна.


Это кстати, не всегда уязвимость, но всегда замечание. Безопасность на уровне реализации не должна зависить от уровня развертывания.

J>Иначе возможно всё что угодно.


Да тут и в релизе все, что угодно возможно

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[3]: [Этюд] По мотивам голосования
От: Jack128  
Дата: 31.03.12 21:24
Оценка: 16 (1)
Здравствуйте, kochetkov.vladimir, Вы писали:

KV>Здравствуйте, Jack128, Вы писали:


J>>
J>>if (!fieldName.All(Char.IsLetter))
J>>   throw new Exception();
J>>


J>>то лично я был бы гораздо спокойнее.


KV>Хм, ок. А сколько и каких уязвимостей закроет это изменение?

Вот честно говоря — хз. Я о безопасности раньше никогда не задумывался(не было необходимости), а вот сейчас кинули на web проэкт, а так как знаний/опыта ноль, приходится больше на интуиции работать. Ну и используя всякие LINQ'и, разоры, в надежде, что они помогут от самых распространённых касяков защитится.

KV>Да тут и в релизе все, что угодно возможно

Возможно. Опять же чисто интуитивно — мне не нравится то что MinValue/MaxValue имеют тип object, хотя судя по контексту должны иметь тип int.

вобщем если заменить исходный код на такой:


var fieldName = Request["field"] ?? "Id";
if (!fieldName.All(Char.IsLetter))
   throw new Exception();

var minValue  = Request["min"] == null ? Int32.MinValue : int.Parse(Request["min"]);
var maxValue  = Request["max"] == null ? Int32.MaxValue : int.Parse(Request["max"]);;

#if DEBUG
    var debugStr  = Request["debug"] ?? string.Empty; // в string.Format добавлять плейсхолдер для другого string.Format'a - жто шиза!!! Проще нуно быть.
#else
    var debugStr  = "";
#endif

var selectCommand = string.Format(
    "SELECT Id, Nickname, Rating, MessageCount, TopicCount FROM Users WHERE {0} >= @minValue AND {0} <= @maxValue {1} ORDER BY {0}",
    fieldName, debugStr
); 

var cmd = new SqlCommand(selectCommand, dataConnection);
cmd.Parameters.Add(new SqlParameter("@minValue", minValue));
cmd.Parameters.Add(new SqlParameter("@maxValue", maxValue));



то я больше ничего полезного для взлома не вижу. Кроме случая когда Users очень большая таблица, а фильт идет по полю без индекса. Тогда можно легко заддосить приложение.
Re[4]: [Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 31.03.12 22:05
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Вот честно говоря — хз. Я о безопасности раньше никогда не задумывался(не было необходимости), а вот сейчас кинули на web проэкт, а так как знаний/опыта ноль, приходится больше на интуиции работать. Ну и используя всякие LINQ'и, разоры, в надежде, что они помогут от самых распространённых касяков защитится.


Приходи на phdays (phdays.ru). Там будет несколько докладов и (вероятно) воркшопов/мастер-классов, которые могут быть полезны и для разработчиков. А доклад, который готовлю я, определенно будет полезен, причем именно тебе, судя по тому что ты написал выше

KV>>Да тут и в релизе все, что угодно возможно

J>Возможно. Опять же чисто интуитивно — мне не нравится то что MinValue/MaxValue имеют тип object, хотя судя по контексту должны иметь тип int.

Не, это мой косяк, к заданию не относится

J>вобщем если заменить исходный код на такой:


Я пока код комментировать не буду, вдруг кто-то еще все же решится попробовать. Подождем чуть. Скажу лишь, что он действительно закрывает большую часть уязвимостей, но не все

J>то я больше ничего полезного для взлома не вижу. Кроме случая когда Users очень большая таблица, а фильт идет по полю без индекса. Тогда можно легко заддосить приложение.


Да, но это ближе к этапу развертывания, чем к реализации. Будем считать, что здесь речь только о ней.

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[4]: [Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 31.03.12 22:07
Оценка:
Здравствуйте, Jack128, Вы писали:

KV>>Хм, ок. А сколько и каких уязвимостей закроет это изменение?

J>Вот честно говоря — хз.

Кстати, если кто-нибудь хочет сказать, какие именно уязвимости это закрыло — не стесняйтесь. Дофига ж народу отметилось, что SQL-инъекции знают. Сможете их тут показать?

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re: [Этюд] По мотивам голосования
От: M8R  
Дата: 31.03.12 22:15
Оценка: 16 (1)
Здравствуйте, kochetkov.vladimir, Вы писали:

Вот что попалось на глаза:

1.
KV>var debugStr  = Request["debug"] ?? string.Empty;

засунуть под #if DEBUG, чтобы избежать того что мы видим ниже

2.
KV>#if DEBUG
KV>    var debugTemplate = "{0}";

не нравится placeholder. Почему — см ниже.

3.
KV>    fieldName.Replace("'", string.Empty).Replace(" ", string.Empty).Replace("\\", string.Empty).Replace(",", string.Empty).Replace("(", string.Empty).Replace(")", string.Empty),

Имхо — лучше уж откидывать всё что не буквы/цифры. Т.к. у нас есть ещё и --, " и т.д.

4.
KV>var selectCommand = string.Format(queryTemplate, debugStr);

Ахтунг! А что у нас в queryTemplate и debugStr?) (см. 1 и 2)

5.
KV>cmd.Parameters.Add(new SqlParameter("@minValue", minValue));
KV>cmd.Parameters.Add(new SqlParameter("@maxValue", maxValue));

Ахтунг! И minValue и maxValue тут типа string

Собственно, вроде всё.

Владимир, давайте ещё)
Re[2]: [Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 31.03.12 22:24
Оценка:
Здравствуйте, M8R, Вы писали:

M8R>3.

M8R>
KV>>    fieldName.Replace("'", string.Empty).Replace(" ", string.Empty).Replace("\\", string.Empty).Replace(",", string.Empty).Replace("(", string.Empty).Replace(")", string.Empty),
M8R>

M8R>Имхо — лучше уж откидывать всё что не буквы/цифры. Т.к. у нас есть ещё и --, " и т.д.

Принимается, санитайзинг действительно должен осуществляться по белым спискам, как уже сказали выше.

M8R>4.

M8R>
KV>>var selectCommand = string.Format(queryTemplate, debugStr);
M8R>

M8R>Ахтунг! А что у нас в queryTemplate и debugStr?) (см. 1 и 2)

Что именно не так?

M8R>5.

M8R>
KV>>cmd.Parameters.Add(new SqlParameter("@minValue", minValue));
KV>>cmd.Parameters.Add(new SqlParameter("@maxValue", maxValue));
M8R>

M8R>Ахтунг! И minValue и maxValue тут типа string

Выше написал — это мой косяк.

M8R>Собственно, вроде всё.

M8R>Владимир, давайте ещё)

Дам, как только найдем все уязвимости Дело в том, что этот пример не вполне про SQL-инъекции (из первого абзаца же очевидно). SQLi тут так... к слову пришлись

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[3]: [Этюд] По мотивам голосования
От: M8R  
Дата: 31.03.12 22:35
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

M8R>>4.

M8R>>
KV>>>var selectCommand = string.Format(queryTemplate, debugStr);
M8R>>

M8R>>Ахтунг! А что у нас в queryTemplate и debugStr?) (см. 1 и 2)

KV>Что именно не так?

Кажется я ошибся в столь ранний час) Не в DEBUG'е всё должно быть ок.
Re[5]: [Этюд] По мотивам голосования
От: M8R  
Дата: 31.03.12 22:41
Оценка: 15 (1)
Здравствуйте, kochetkov.vladimir, Вы писали:

KV>Здравствуйте, Jack128, Вы писали:


KV>>>Хм, ок. А сколько и каких уязвимостей закроет это изменение?

J>>Вот честно говоря — хз.

KV>Кстати, если кто-нибудь хочет сказать, какие именно уязвимости это закрыло — не стесняйтесь. Дофига ж народу отметилось, что SQL-инъекции знают. Сможете их тут показать?


var fieldName = Request["field"] ?? "Id";
if (!fieldName.All(Char.IsLetter))
   throw new Exception();
...
var selectCommand = string.Format(
    "SELECT Id, Nickname, Rating, MessageCount, TopicCount FROM Users WHERE {0} >= @minValue AND {0} <= @maxValue {1} ORDER BY {0}",
    fieldName, debugStr
);


Единственное, что мне тут не нравится, это то, что пользователь может подставить любое имя поля. Можеть быть это поможет узнать какие поля есть в таблице и какого типа. Хз что ещё тут можно сделать.
Re[5]: Offtop: phdays
От: Jack128  
Дата: 31.03.12 23:00
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

KV>Здравствуйте, Jack128, Вы писали:


J>>Вот честно говоря — хз. Я о безопасности раньше никогда не задумывался(не было необходимости), а вот сейчас кинули на web проэкт, а так как знаний/опыта ноль, приходится больше на интуиции работать. Ну и используя всякие LINQ'и, разоры, в надежде, что они помогут от самых распространённых касяков защитится.


KV>Приходи на phdays (phdays.ru). Там будет несколько докладов и (вероятно) воркшопов/мастер-классов, которые могут быть полезны и для разработчиков. А доклад, который готовлю я, определенно будет полезен, причем именно тебе, судя по тому что ты написал выше


Хм, можно. Опять же, "Несчастный случай" в качестве бонуса стимулирует -)

Кста, что то в программке (http://phdays.ru/%D0%9F%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%20PHD%202011.pdf) я тя в упор не вижу. И поиск тоже.
Re[6]: Offtop: phdays
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 01.04.12 02:47
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Кста, что то в программке (http://phdays.ru/%D0%9F%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%20PHD%202011.pdf) я тя в упор не вижу. И поиск тоже.


Дык это программа с мероприятия прошлого года. На этот — пока еще в процессе, ее еще не опубликовали.

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re: [Этюд] По мотивам голосования
От: MT-Wizard Украина  
Дата: 01.04.12 04:26
Оценка: 39 (2)
Здравствуйте, kochetkov.vladimir, Вы писали:

KV>Вся информация о пользователе (логин, пароль, никнейм, почта, всевозможные данные его статистики и т.п.) хранятся в одной таблице Users.

KV>Найдите все уязвимости, допущенные в этом коде

Получается, что с помощью множества запросов можно сначала подобрать имена колонок с нужной информацией (пароль, ...), а потом получать список пользователей с нужными значениями в этих колонках. Вся база как на ладони.
А ти, москалику, вже приїхав (с)
Re[2]: [Этюд] По мотивам голосования
От: Jack128  
Дата: 01.04.12 05:25
Оценка: 30 (1)
Здравствуйте, MT-Wizard, Вы писали:

MW>Здравствуйте, kochetkov.vladimir, Вы писали:


KV>>Вся информация о пользователе (логин, пароль, никнейм, почта, всевозможные данные его статистики и т.п.) хранятся в одной таблице Users.

KV>>Найдите все уязвимости, допущенные в этом коде

MW>Получается, что с помощью множества запросов можно сначала подобрать имена колонок с нужной информацией (пароль, ...), а потом получать список пользователей с нужными значениями в этих колонках. Вся база как на ладони.


Ё-ё-ё моё, какая дырень. Получаем всех пользователей, а потом половинным делением — пароль для любого заданного пользователя..
Re[6]: [Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 01.04.12 06:18
Оценка:
Здравствуйте, M8R, Вы писали:

M8R>Единственное, что мне тут не нравится, это то, что пользователь может подставить любое имя поля. Можеть быть это поможет узнать какие поля есть в таблице и какого типа. Хз что ещё тут можно сделать.


Еще можно, тем же способом, получить содержимое этих полей.

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[2]: [Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 01.04.12 06:18
Оценка:
Здравствуйте, MT-Wizard, Вы писали:

MW>Получается, что с помощью множества запросов можно сначала подобрать имена колонок с нужной информацией (пароль, ...), а потом получать список пользователей с нужными значениями в этих колонках. Вся база как на ладони.


Ну конечно. Это оно и есть

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[3]: [Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 01.04.12 06:21
Оценка:
Здравствуйте, Jack128, Вы писали:

MW>>Получается, что с помощью множества запросов можно сначала подобрать имена колонок с нужной информацией (пароль, ...), а потом получать список пользователей с нужными значениями в этих колонках. Вся база как на ладони.


J>Ё-ё-ё моё, какая дырень. Получаем всех пользователей, а потом половинным делением — пароль для любого заданного пользователя..


Да, именно так. Манипулируя значением fieldName, атакующий может получить содержимое всех полей всей таблицы банальным двоичным поиском на особый лад. Причем безотносительно санитизации параметра fieldName в плане SQL-инъекций. Этот класс атак называется Abuse of Functionality. Критерием отнесения к этому классу, является возможность атакующего, реализовать какую-либо угрозу, используя только штатный функционал приложения: получить из побочных каналов информацию, недоступную рядовым пользователям (как в данном случае), затруднить или остановить работу приложения (например, заблокировав все аккаунты пользователей, осуществив максимальное количество попыток ввода неправильного пароля. В этом случае, это также относится и классу DoS), осуществить обход аутентификации, воспользовавшись слабой процедурой восстановления пароля и т.п.

Уязвимостью в данном случае является недостаточная обработка входных данных параметра fieldName. Для ее устранения, необходим контроль этого параметра по белому списку с тем, чтобы в нем могли быть имена только тех полей, которые явным образом отбираются SELECT'ом.

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[4]: [Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 01.04.12 06:32
Оценка:
Здравствуйте, M8R, Вы писали:

KV>>Что именно не так?

M8R>Кажется я ошибся в столь ранний час) Не в DEBUG'е всё должно быть ок.

Да нет, там не все ок, только не из-за DEBUG. Если в запросе придет fieldName со значением "{0}", то содержимое debugStr попадет в текст запроса безотносительно конфигурации сборки. Это приводит к SQL-инъекции. Уявзимостью, в данном случае, является опять-таки недостаточный контроль fieldName. С устранением AoF будет устранена и эта SQLi. Хотя правильнее, конечно, реализовать эту фичу так, как предложил Jack128 (Re[3]: [Этюд] По мотивам голосования
Автор: Jack128
Дата: 01.04.12
).

Кстати, пример с подстановкой плейсхолдера для обработки в другом стрингбилдере — взят из реального проекта, хоть и шиза — да

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re: [Этюд] По мотивам голосования
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 01.04.12 06:37
Оценка: +4
По другим классам атак, были бы интересны аналогичные этюды?

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[4]: [Этюд] По мотивам голосования
От: Философ Ад http://vk.com/id10256428
Дата: 01.04.12 07:05
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

ггыы %)
понятия не имел как называется этот вид атаки, я такое и без всяких названий делал
Всё сказанное выше — личное мнение, если не указано обратное.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.