Обнаружение SQL инъекций в ORACLE, часть вторая

Обнаружение SQL инъекций в ORACLE, часть вторая

Простые тесты показали, что существует множество следов, оставленных в базе данных или сетевых трассировочных файлах, если была предпринята попытка SQL инъекции. Поэтому все это должно быть доступно для DBA, чтобы использовать некоторые из вышеупомянутых источников как основание для политики обнаружения. Некоторые из методов, типа анализа трассировочных файлов - явные пожиратели ресурсов. Высокоточный аудит и чтение SQL из SGA очень похожие методы, что позволяет их совместно использовать для аудита базы данных. Конечно, лучшая форма защиты - это аудит вашей базы данных и избежание использования динамического Sql.

Внутренние трассировочные файлы Oracle.

Oracle может также генерировать файлы трассировки из ядра Oracle, когда запущен SQL. Это может быть включено на уровне RDBMS, при установке параметра SQL_TRACE=true в файле init.ora. Это может также быть включено в случае следующего действия:

ALTER SYSTEM SET SQL_TRACE=TRUE;

Трассировка также может быть включена для текущего сеанса:

ALTER SESSION SET SQL_TRACE=TRUE;

Прежде, чем будет включена трассировка, параметр timed_statistics, в файле инициализации, должен быть установлен в значение TRUE. Трассировочные файлы будут записаны в файл, определенный, в файле инициализации, параметром  user_dump_dest.

Давайте создадим файл трассировки для нашего простого примера SQL инъекции и проанализируем его следующим образом:

SQL> alter session set sql_trace=true;

Session altered.

SQL> exec get_cust('x'' union select username from all_users where ''x''=''x');

PL/SQL procedure successfully completed.

SQL>

Файл трассировки создается с форматом имени: {SID}_ora_{PID).trc. Формат определяется платформой. В случае трассировочных файлов SQL*NetPID является OS PID, и снова это может быть определено с помощью запроса к представлениям v$session и v$process. Необработанный файл трассировки может быть прочитан, а рассматриваемый SQL может быть замечен следующим образом:

*** 2003-06-16 16:54:01.429

*** SESSION ID:(8.29) 2003-06-16 16:54:01.408

APPNAME mod='SQL*Plus' mh=3669949024 act='' ah=4029777240

=====================

PARSING IN CURSOR #3 len=33 dep=0 uid=17 oct=42 lid=17 tim=1055782441429190 hv=3

732290820 ad='846c85c0'

alter session set sql_trace=true

END OF STMT

EXEC #3:c=0,e=1,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=1055782441407935

*** 2003-06-16 16:54:34.876

=====================

PARSING IN CURSOR #3 len=82 dep=0 uid=17 oct=47 lid=17 tim=1055782474876639 hv=3

204430447 ad='8482555c'

BEGIN get_cust('x'' union select username from all_users where ''x''=''x'); END;

END OF STMT

PARSE #3:c=0,e=6430,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=1055782474876611

Лучшим путем является запись файла трассировки с утилитой, называемой tkprof:

oracle:jupiter> tkprof emil_ora_616.trc output.trc sys=yes

TKPROF: Release 9.0.1.0.0 - Production on Mon Jun 16 16:59:50 2003

(c) Copyright 2001 Oracle Corporation. All rights reserved.

Сгенерированный файл содержит весьма много информации, включая синхронизацию и идентификаторы пользователей. Синхронизация должна читаться в необработанном файле трассировки, но пользовательский идентификатор может быть найден в выходных данных tkprof. Преобразованные выходные данные показывают пакеты PL/SQL, называемые также динамическими SQL строками:


BEGIN get_cust('x'' union select username from all_users where ''x''=''x');

END;

call count cpu elapsed disk query current rows

------- ------ -------- ---------- ---------- ---------- ---------- ----------

Parse 1 0.00 0.00 0 0 0 0

Execute 1 0.00 0.00 0 0 0 1

Fetch 0 0.00 0.00 0 0 0 0

------- ------ -------- ---------- ---------- ---------- ---------- ----------

total 2 0.00 0.01 0 0 0 1

Misses in library cache during parse: 1

Optimizer goal: CHOOSE

Parsing user id: 17

<output snipped>

select customer_phone

from

customers where customer_surname='x' union select username from all_users

where 'x'='x'

call count cpu elapsed disk query current rows

------- ------ -------- ---------- ---------- ---------- ---------- ----------

Parse 1 0.01 0.02 0 0 0 0

Execute 1 0.01 0.00 0 0 1 0

Fetch 37 0.01 0.00 0 184 5 36

------- ------ -------- ---------- ---------- ---------- ---------- ----------

total 39 0.03 0.03 0 184 6 36

Misses in library cache during parse: 1

Optimizer goal: CHOOSE

Parsing user id: 17 (recursive depth: 1)

Rows Row Source Operation

------- ---------------------------------------------------

36 SORT UNIQUE

36 UNION-ALL
<output snipped>

Пользовательский идентификатор можно прочесть в базе данных следующим образом:

<output snipped>
SQL> select username,user_id

2 from dba_users

3 where user_id=17;

USERNAME USER_ID

------------------------------ ----------

DBSNMP 17

SQL>

У трассировочных файлов, несомненно, есть перспектива в осуществлении системы обнаружения SQL инъекций, но у них также существуют некоторые серьезные проблемы:

  • Трассировка должна быть постоянно включена.
  • Генерация трассировок потребляет системные ресурсы; количество потребляемых ресурсов зависит от типа базы данных и приложения.
  • Огромное количество трассировочных файлов быстро потребляет дисковое пространство. Поэтому просто осуществить атаку "отказ в обслуживании".
  • Как и в других методах, для выделения SQL и пользовательской информации, необходимы синтаксический анализатор или программа-фильтр. А затем необходимо анализировать была ли в SQL попытка SQL инъекции.

·         Поскольку файлы трассировки тоже генерируются основанными на OS PID, то их обслуживание в реальном времени, для гарантирования от злоупотребления ресурсами, является очень трудным. Любые долго запущенные сеансы очень легко переполняют диск.

Информация в трассировочных файлах хороша и пригодна к использованию, но производительные проблемы с генерацией трассировки постоянно наводят на мысль о том, чтобы не использовать этот метод. Как и в случае с SQL*NET эти файлы могут использоваться более расчетливо.

Чтение SQL из System Global Area (SGA)

Это может быть наиболее перспективным методом выделения и анализа SQL, в случае если была сделана попытка SQL инъекции. Причины этого исключительно из-за того, что - основной элемент Oracle RDBMS, весь SQL и запущенный PL/SQL, проводит некоторое время в SQL области в SGA. Однако существует несколько проблем, чтобы быть уверенным в этом. Во-первых - то, что запрос к SQL области может интенсивно использовать ресурсы и заставить работать систему на критическом уровне, а во-вторых, может отсутствовать уже выполненный SQL. Если база данных запускает тысячи различных SQL, а память, выделенная для SQL области - не большая, то нечасто используемый SQL (однажды или дважды) может очень быстро покинуть SQL область.

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

Снова, как с другими источниками информации, фильтр или синтаксический анализатор  действительно необходимы для анализа SQL, извлеченного для показа некоторых признаков того, что является легальным, а что нет. Для начала можно использовать сценарий, проверяющий только существование union в SQL, фильтрующий некоторых возможных пользователей и сохраняющий результаты в итоговой таблице, используя команду create table as select, а далее фильтрующий определенные связанные таблицы. Хорошее первое приближение было бы должно выдвинуть на первый план любой SQL, произведенный не sys пользователем с предложением union, обращающимся к таблице или представлению, принадлежащему sys. Наш пример делает запрос к представлению all_users, принадлежащему sys.

Ниже простой пример запроса, извлекающего из SGA, SQL, содержащий в себе предложение union. Это должно дать читателю идеи о том, как могут быть прочитаны данные. Далее мы проводим фильтрацию, как описано выше.

select  a.address address,
        s.hash_value hash_value,
        s.piece piece,
        s.sql_text sql_text,
        u.username parsing_user_id,
        c.username parsing_schema_id
from    v$sqlarea a,
        v$sqltext_with_newlines s,
        dba_users u,
        dba_users c
where a.address=s.address
and a.hash_value=s.hash_value
and a.parsing_user_id=u.user_id
and a.parsing_schema_id=c.user_id
and exists (select 'x'
        from v$sqltext_with_newlines x
        where x.address=a.address
        and x.hash_value=a.hash_value
        and upper(x.sql_text) like '%UNION%')
order by 1,2,3
/

Running this gives: 


<output snipped>
QL_TEXT                            PARSING_USER_ID PARSING_SCHEMA_
----------------------------------- --------------- ---------------
and a.parsing_schema_id=c.user_id
and upper(s
.sql_text) like '%UNION%'           SYSTEM          SYSTEM
order by 1,2,3

BEGIN dbsnmp.get_cust('x'' union    SYS             SYS
select username from all_users
where ''x''=''x'); END;             SYS             SYS

select customer_phone from          SYS             SYS
customers where
customer_surname='x'
union select username from          SYS             SYS
all_users where 'x'='x'

BEGIN get_cust('x'' union select    DBSNMP          DBSNMP
username from all_users where '
'x''=''x'); END;                    DBSNMP          DBSNMP

select                              SYS             SYS
tc.type#,tc.intcol#,tc.position#,c.
type#, c.length,c.scal
<output snipped>

Пример был выполнен, как SYS пользователь и как DBSNMP пользователь, пока эта база данных неоднократно была в выводе(output). Этот метод пока, кажется, самый простой в использовании и вероятно самый легкий способ реализации полного средства/сценария для анализа SQL инъекций. Также он имеет наименьшее количество плохих сторон!

Использование аудита Oracle.

Возможности, доступные в стандартном Oracle аудите - богаты и различны, но существует множество проблем с его использованием, для обнаружения SQL инъекций.

  • Аудит не работает на уровне строк, только на уровне доступа или в сеансе.
  • Не фиксируется SQL, используемый во время выполнения.
  • Нет никакого простого способа обнаружить использование union (как в нашем примере) или в любом из других случаев, определенных выше - кроме распознавания синтаксическим анализатором SQL текста.

Аудит должен использоваться независимо от использования любого из других методов. Это - устойчивая и полезная система может быть полезной для обнаружения нападений.

Не так легко обнаружить SQL инъекцию с помощью аудита, скорее можно отследить явные улики. Например, если аудит был включен для select access в таблицах DBSNMP.customers и SYS.all_users, тогда контрольный след должен показать проникновение, в случае использования, такой как описано в нашем примере, попытки SQL инъекции. Мы не можем определенно говорить, что использовался union, но доступ с помощью DBSNMP к ALL_USERS должен быть видим.

Когда хакер пытается предположить, как добавить union, подзапрос или  усекающий комментарий, к существующей SQL строке, то могут быть сгенерированы SQL ошибки, если он делает это неправильно. И снова это является явной уликой, а детальный анализ ошибок в контрольном следе и другие действия тем же самым пользователем в том же самом сеансе могут указывать на то, что случилось.

Ниже находиться пример использования аудита, для выполняющейся SQL инъекции. Сначала включите аудит в представлении ALL_USERS и таблице CUSTOMERS.

SQL> audit select on sys.all_users by access;

Audit succeeded.

SQL> audit select on dbsnmp.customers by access;

Audit succeeded.

Проверьте контрольный след со следующим SQL:

 SQL> audit select on sys.all_users by access;

Audit succeeded.

SQL> audit select on dbsnmp.customers by access;

Audit succeeded.

Все это показывает доступ к представлению ALL_USERS и таблице CUSTOMERS пользователями DBSNMP в одном и том же сеансе. Это также является простым в написании части SQL, для анализа ошибок в контрольном следе.

Аудит, несомненно, может использоваться, для обнаружения SQL инъекций, но он требует значительных усилий при определении "хороших" SQL запросов и параметров настройки аудита для надежного "предположения" при попытках SQL инъекций.

Триггеры базы данных.

Триггеры базы данных обычно являются следующей линией защиты, когда используется внутренний аудит Oracle. Нормальные средства аудита работают на уровне object, или привилегированном уровне, и не могут быть полезны при определения случившегося на уровне строки. Для этого могут использоваться триггеры. Их использование влечет за собой программирование триггеров для каждой таблицы и для каждого действия типа insert, update или delete. Главный недостаток триггеров это - то, что они не могут быть написаны для запуска, если команда select используется против таблицы или представления.

Выделение фактического SQL, используемого для update, delete , или insert - не возможно. Возможно, создать SQL, который будет использоваться при запуске триггера изменения таблицы, читая прежде и после значения для каждой строки и создать триггер запускающийся для каждой строки.

Ввиду этих ограничений, триггеры базы данных, не всегда полезны в поисках попыток SQL инъекций.

Высокоточный аудит

С версии 9i, в Oracle представлен высокоточный аудит (FGA). Эта функция основана на внутренних триггерах, запускающихся при каждом анализе SQL выражений. Как и высокоточный контроль доступа, (FGA) основывается на определении предикатов, для ограничения SQL, который будет ревизоваться. Используя аудитную политику, аудит может быть сфокусирован на небольшом поднаборе действий, выполненных против определенного набора данных.

Единственное преимущество, которое дает нам эта функция это способность контролировать команды select на уровне строк. Стандартная обрабатывающая функция, также фиксирует SQL, так что она может быть хорошим средством, для проверки SQL инъекций. Действительно, Oracle заявляет в своей on-line документации этого пакета, что он - идеальный инструмент для осуществления IDS системы.

Давайте установим простую политику и посмотрим, можем ли мы отловить инъекцию в нашем примере. Первое требование - то, что таблицы, с установленным против них FGA, должны генерировать аналитическую статистику. Также должен использоваться cost-based оптимизатор. Существует множество проблем с FGA, которые являются факторами неправильного запуска триггеров, например, если в SQL используется правило проверки RULE.

Сначала проверьте оптимизатор и проанализируйте пример таблицы:

SQL> analyze table dbsnmp.customers compute statistics;

Table analyzed.

SQL> select name,value from v$parameter
  2  where name='optimizer_mode';

NAME
----------------------------------------------------------------
VALUE
--------------------------------------------------------------------------------
optimizer_mode
CHOOSE

Затем, создайте политику, которая будет каждый раз выполнять выражение, запущенное против таблицы (в этом случае DBSNMP.CUSTOMERS).

SQL> set serveroutput on size 100000
SQL> exec get_cust('x'' union select username from all_users where ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' union
select username from all_users where 'x'='x'
::AURORA$JIS$UTILITY$
::AURORA$ORB$UNAUTHENTICATED
::CTXSYS
::DBSNMP
::EMIL
<records snipped>
::SYS
::SYSTEM
::WKSYS
::ZULIA

Затем, выполните пример SQL инъекции, и проверьте контрольный след для любых входов:

SQL> col db_user for a15
SQL> col object_name for a20
SQL> col sql_text for a30 word_wrapped
SQL> select db_user,timestamp,object_name,sql_text
  2  from dba_fga_audit_trail
  3  order by timestamp;

DB_USER         TIMESTAMP OBJECT_NAME          SQL_TEXT
--------------- --------- -------------------- ------------------------------
DBSNMP          16-JUN-03 CUSTOMERS            select customer_phone from
                                               customers where
                                               customer_surname='x' union
                                               select username from all_users
                                               where 'x'='x'

SQL>

Отлично, успех! Динамический SQL показывается, включая предложение union, присоединенное к системной таблице ALL_USERS. Использование FGA должно быть расширено к каждой таблице в прикладном проекте. Тогда должны записываться сообщения, это возвращало бы только те входы, которые нарушают некоторые из основных правил, определенных вначале, типа SQL с предложением union, или SQL со строкой типа 'x' = 'x'.

Вся политика может быть легко сгенерирована, используя следующий SQL:

 select 'exec dbms_fga.add_policy(object_schema=>'''||owner||''',object_name=>'''
||table_name||''',policy_name=>'''||table_name||''',audit_condition=>''1=1'',aud
it_column=>null,handler_schema=>null,handler_module=>null,enable=>true);'
from dba_tables
where owner not in ('SYS','SYSTEM')
/
 

Защита лучше чем обнаружение

Некоторые решения по защите от SQL инъекций, можно узнать из  предыдущих статей, но для совершенства, несколько главных идей снова здесь включены:

  • Не используйте динамический SQL, который использует конкатенацию. Если это очень необходимо, тогда очень тщательно фильтруйте ввод.
  • Если возможно, не используйте в приложении динамический PL/SQL. Найдите другое решение. Если динамический PL/SQL необходим, тогда используйте bind переменные.
  • Используйте AUTHID CURRENT_USER в PL/SQL так, чтобы это выполнялось как “текущий пользователь”, а не “владелец”.
  • Используйте принцип наименьшего количества привилегий, и разрешайте только необходимые привилегии.

Выводы

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

Как я и заявлял ранее, одно простое решение - не соединять вашу базу данных Oracle с Internet. Во-вторых, не используйте динамический SQL или PL/SQL, а если вы это все- таки делаете, то используйте bind переменные. Также контролируйте и защищайте данные, насколько это возможно в ОС и сетевой защите.

Простые тесты, показанные выше, показали, что действительно существует множество следов, оставленных в базе данных или сетевых трассировочных файлах, если была предпринята попытка SQL инъекции. Поэтому все это должно быть доступно для DBA, чтобы использовать некоторые из вышеупомянутых источников как основание для политики обнаружения. Некоторые из методов типа трассировочных файлов - явные пожиратели ресурсов. Высокоточный аудит и чтение SQL из SGA очень похожие методы, что позволяет их совместно использовать для аудита.

Конечно, лучшая форма защиты это аудит защиты вашей базы данных и избежание использования динамического SQL!!!

Квантовый кот Шрёдингера ищет хозяина!

Живой, мертвый или в суперпозиции? Узнайте в нашем канале

Откройте коробку любопытства — подпишитесь