Gray Hat C#, SQL-Injection, Программирование

#21 Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности. Автоматический фаззинг конечной точки SOАP на предмет уязвимостей SQL-инъекций.

Здравствуйте, дорогие друзья.

Теперь, когда строительные блоки фаззера WSDL созданы, мы можем приступить к разработке действительно интересного инструмента. Используя класс WSDL, мы можем взаимодействовать с данными в WSDL объектно-ориентированным способом, что значительно упрощает фаззинг конечной точки SOAP. Мы начнем с написания нового метода Main(), который принимает один аргумент (URL-адрес конечной точки SOAP), который можно создать в отдельном файле внутри собственного класса Fuzzer, как показано в листинге ниже.

The Main() method of the SOAP endpoint fuzzer

Сначала мы объявляем пару статических переменных на уровне класса перед методом Main(). Эти переменные будут использоваться во всех методах, которые мы пишем. Первая переменная — это класс WSDL [1], а вторая хранит URL-адрес конечной точки SOAP [2].
В методе Main() мы присваиваем переменной _endpoint значение первого аргумента, переданного в фаззер [3]. Затем мы печатаем дружественное сообщение, предупреждающее пользователя о том, что мы собираемся получить WSDL для службы SOAP.
После сохранения URL-адреса конечной точки мы создаем новый HttpWebRequest для получения WSDL из службы SOAP, добавляя ?WSDL в конец URL-адреса конечной точки. Мы также создаем временный XmlDocument для хранения WSDL и передачи конструктору класса WSDL. Передавая поток ответа HTTP методу XmlDocument Load() [4], мы загружаем XML, возвращенный HTTP-запросом, в XML-документ. Затем мы передаем полученный XML-документ конструктору класса WSDL для создания нового объекта WSDL. Теперь мы можем перебирать каждую из служб конечной точки SOAP и фаззить службу. Цикл foreach перебирает объекты в свойстве Services класса WSDL и передает каждый сервис методу FuzzService(), который мы напишем в следующем разделе.


Фаззинг отдельных сервисов SOAP

Метод FuzzService() принимает SoapService в качестве аргумента, а затем определяет, нужно ли нам фаззить сервис, используя параметры SOAP или HTTP, как показано в листинге ниже.

The FuzzService() method used to determine how to fuzz a given SoapService

После печати текущего сервиса мы приступим к фаззингу: мы перебираем каждый порт SOAP в свойстве сервиса Ports. Используя метод Single() языкового запроса (LINQ) [1], мы выбираем одну привязку SoapBinding, соответствующую текущему порту. Затем мы проверяем, является ли привязка простым HTTP или SOAP на основе XML. Если привязка является HTTP-привязкой [2], мы передаем ее методу FuzzHttpPort() для фаззинга. В противном случае мы предполагаем, что привязка является привязкой SOAP, и передаем ее методу FuzzSoapPort().
Теперь давайте реализуем метод FuzzHttpPort(). Два типа возможных портов HTTP при работе с SOAP — это GET и POST. Метод FuzzHttpPort() определяет, какая из HTTP-команд будет использоваться при отправке HTTP-запросов во время фаззинга, как показано в листинге ниже.

The FuzzHttpPort() method

Метод FuzzHttpPort() очень прост. Он проверяет, равно ли свойство Verb SoapBinding GET или POST, а затем передает привязку соответствующему методу — FuzzHttpGetPort() или FuzzHttpPostPort() соответственно. Если свойство Verb не равно GET или POST, генерируется исключение, предупреждающее пользователя о том, что мы не знаем, как обрабатывать данный HTTP-глагол. Теперь, когда мы создали метод FuzzHttpPort(), мы реализуем метод FuzzHttpGetPort().


Создание URL-адреса для фаззинга

Оба метода фаззинга HTTP немного сложнее, чем предыдущие методы фаззера. Первая половина метода FuzzHttpGetPort(), представленная в листинге ниже, создает исходный URL-адрес для фаззинга.

The first half of the FuzzHttpGetPort() method, where we build the initial URL to fuzz

Первое, что мы делаем в методе FuzzHttpGetPort(), — используем LINQ [1] для выбора типа порта из нашего класса WSDL, который соответствует текущей привязке SOAP. Затем мы перебираем свойство Operations текущей привязки, которое содержит информацию о каждой операции, которую мы можем вызвать, и о том, как вызвать данную операцию. Во время итерации мы печатаем, какую операцию мы будем фаззить. Затем мы создаем URL-адрес, который будем использовать для выполнения HTTP-запроса для данной операции, добавляя свойство Location текущей операции к переменной _endpoint, которую мы установили в самом начале метода Main() [2]. Мы выбираем текущую SoapOperation (не путать с SoapBindingOperation!) из свойства Operations порта Type с использованием метода LINQ Single(). Мы также выбираем SoapMessage, используемый в качестве входных данных для текущей операции, используя тот же метод LINQ, который сообщает нам, какую информацию текущая операция ожидает при вызове.
Получив информацию, необходимую для настройки URL-адреса GET, мы создаем словарь для хранения имен параметров HTTP и типов параметров, которые мы будем отправлять. Мы перебираем каждую из входных частей, используя цикл foreach. Во время итерации мы добавляем в словарь имя каждого параметра и тип, который в данном случае всегда будет строкой. После того, как мы сохраним все имена наших параметров и их соответствующие типы, сохраненные рядом друг с другом, мы можем создать исходный URL-адрес для фаззинга.
Для начала мы определяем логическое значение first [3], которое будем использовать, чтобы определить, является ли параметр, добавленный к URL-адресу операции, первым параметром. Это важно, поскольку первый параметр строки запроса всегда отделяется от базового URL-адреса вопросительным знаком (?), а последующие параметры отделяются амперсандом (&), поэтому нам нужно быть уверенными в различии. Затем мы создаем список Guid, который будет содержать уникальные значения, которые мы отправляем вместе с параметрами, чтобы мы могли ссылаться на них во второй половине метода FuzzHttpGetPort().
Далее мы перебираем словарь параметров, используя цикл foreach.
В этом цикле foreach сначала мы проверяем, является ли тип текущего параметра строкой. Если это строка, мы создаем новый Guid, который будет использоваться в качестве значения параметра; затем мы добавляем новый Guid в созданный нами список, чтобы иметь возможность ссылаться на него позже. Затем мы используем оператор +=x [4], чтобы добавить параметр и новое значение к текущему URL-адресу. Используя тернарную операцию [5], мы определяем, следует ли ставить перед параметром вопросительный знак или амперсанд. Вот как параметры строки запроса HTTP должны быть определены, в соответствии с протоколом HTTP. Если текущий параметр является первым параметром, перед ним ставится вопросительный знак. В противном случае перед ним ставится амперсанд. Наконец, мы устанавливаем для параметра значение false, чтобы к последующим параметрам добавлялся правильный разделительный символ.


Фаззинг созданного URL


После создания URL-адреса с параметрами строки запроса, мы можем выполнять HTTP-запросы, систематически заменяя значения параметров испорченными значениями, которые могут вызвать ошибку SQL на сервере, как показано в листинге ниже. Эта вторая половина кода завершает метод FuzzHttpGetPort().

The second half of the FuzzHttpGetPort() method, sending the HTTP requests
The second half of the FuzzHttpGetPort() method, sending the HTTP requests

Теперь, когда у нас есть полный URL-адрес, который мы будем фаззить, мы распечатываем его, чтобы пользователь мог его увидеть. Мы также объявляем целое число k, которое будет увеличиваться по мере перебора значений параметров в URL-адресе, чтобы отслеживать потенциально уязвимые параметры. Затем, используя цикл foreach, мы перебираем список Guid, который использовали в качестве значений для наших параметров. В цикле foreach первое, что мы делаем, — это заменяем текущий Guid в URL-адресе на строку «fd’sa» с помощью метода replace() [1], который должен испортить любые SQL-запросы, использующие это значение, без надлежащей очистки. Затем мы создаем новый HTTP-запрос с измененным URL-адресом и объявляем пустую строку с именем resp, которая будет содержать ответ HTTP.
В блоке try/catch мы пытаемся прочитать ответ на HTTP-запрос от сервера с помощью StreamReader [2]. Чтение ответа вызовет исключение, если сервер вернет ошибку 500 (что произойдет, если на стороне сервера возникнет исключение SQL). Если генерируется исключение, мы перехватываем его в блоке catch [3], и пытаемся снова прочитать ответ с сервера. Если ответ содержит синтаксическую ошибку строки, мы печатаем сообщение, предупреждающее пользователя о том, что текущий параметр HTTP может быть уязвим для SQL-инъекции. Чтобы точно сообщить пользователю, какой параметр может быть уязвимым, мы используем целое число k в качестве индекса списка Parts [4], и извлекаем имя текущего свойства. Когда все сказано и сделано, мы увеличиваем целое число k на 1 и возвращаемся к началу цикла foreach, с новым значением для проверки.
Это полный метод фаззинга портов HTTP GET SOAP. Далее нам нужно реализовать FuzzHttpPostPort() для фаззинга портов POST SOAP.

На этом все. Всем хорошего дня!

Цикл статей по Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности.