Проведення SQL ін'єкцій в Oracle
[Вступ]
Останнім часом при дослідженні різних веб проектів на уразливості, став натикатися на sql ін'єкції в Oracle. Хоча в даний час рідко можна зустріти використання цієї СУБД в Веб програмуванні, але все-таки таке трапляється. Всі дослідження закінчувалися простим виявленням баги, що робити далі було незрозуміло. В ході пошуку статті, добре описує практичні аспекти експлуатації даної уразливості в Oracle, такий як статті cash і spyder, що описують ін'єкції в MSSQL і PostgreSQL, знайти не вдалося.
В результаті пошуку було знайдено, тільки лише серія статей k00p3r, причому повністю копіпастнутих зі сторонніх джерел і є простим перекладом, прочитання яких не давало чіткого уявлення про проведення вилиці в Oracle, тому що статті по суті мають великий обсяг, носять теоретичний характер, і здебільшого містять воду.
В кінцевому підсумку довелося згадувати інститутські навички роботи з Oracle і перечитувати купу розрізненої інформації в інтернет. У цій статті хотілося б поділитися з вами тим, що мені вдалося накопати по проведенню sql injection в Oracle і постаратися об'єднати це в одне ціле.
Ну і спробувати заповнити прогалину, і доповнити серію статей cash і spyder.
[Особливості Oracle]
Спочатку наведу деякі властивості, які необхідно враховувати при проведенні ін'єкції в Oracle. Відразу хочу обмовитися, що в статті розглядається проведення ін'єкцій в операторі SELECT. Хоча проведення ін'єкцій в операторах INSERT, UPDATE і DELETE, так само можливо.
Так само важливий факт, що в статті розглядаються питання проведення ін'єкції саме в запитах SQL Oracle, а не в процедурах PL / SQL Oracle. Істотна відмінність ін'єкцій у процедурах PL / SQL полягає в можливості використання роздільника запитів - символу крапки з комою ";". Але про це имхо потрібно писати окрему статтю, з описом усіх випливають наслідків. Тим більше автор вважає що в Веб додатках найбільш часто зустрічаються ін'єкції саме в запитах SQL (принаймні я стикався тільки з такими).
У Oracle, так само як і в MySQL і в PostgreSQL, ін'єкція проводиться шляхом використання оператора UNION, тобто зі складанням об'єднання двох запитів (далі по тексту для простоти розуміння використовується термін - підзапит). Але крім збігу кількості стовпців в основному запиті і підзапиті необхідно враховувати, що Oracle не провадить автоматичного приведення типів в підзапит. Тому при підборі стовпців необхідно підставляти null, на відміну, наприклад від MySQL.
Так само дуже важливою властивістю є те, що всі запити SELECT повинні проводитися з якоїсь таблиці, тобто синтаксис запиту завжди повинен містити слово FROM і ім'я таблиці. Для простих арифметичних обчислень або інших операцій, що не вимагають реальну таблицю, в Oracle існує псевдо таблиця SYS.DUAL.
Важливим властивістю є відсутність оператора LIMIT.
Для усічення запиту використовуються символи коментарів "-" (два тире) і "/ *" (прямий слеш і зірочка) в SQL Oracle. Перший тип коментарів -однострочний. Другий тип - багаторядковий.
Немає можливості використання в SQL Oracle декількох запитів із застосуванням роздільника ";", на відміну від процедур на PL / SQL.
При виявленні помилки можна однозначно ідентифікувати Oracle, по присутності слова ORA в тексті повідомлення про помилку, наприклад:
Macromedia][Oracle JDBC Driver][Oracle]ORA-00933: SQL command not properly ended
Не завжди в тексті помилки присутнє слово Oracle, наприклад
Warning: OCIStmtExecute: ORA-01722: invalid number in
[Підбір стовпців]
Нехай помилка присутня в параметрі id:
www.site.com/view.php?id=1'
Визначення кількості стовпців присутніх в основному запиті відбувається так само, як і в MySQL. Так як оператор UNION вимагає однакової кількості шпальт, як в основному запиті, так і в підзапиті нам потрібно кількість цих стовпців визначити. При неправильному зазначенні стовпців в підзапиті виводиться стандартне повідомлення про помилку:
ORA-XXXXX: query block has incorrect number of result columns
Для підбору стовпців існує 2 відомих способу і добре описаних в статті spyder. Але наведу їх ще раз, щоб читачеві не бігати за статтями:
1. Простий перебір.
Складемо наступний запит
www.site.com/view.php?id=-1+union+select+null+from+sys.dual--
Якщо помилка з'явилася, збільшуємо кількість стовпців на один
www.site.com/view.php?id=-1+union+select+null, null+from+sys.dual--
і так поки не зникне помилка.
2. Використання оператора ORDER BY
Другий спосіб набагато швидше і приємніше, якщо кількість стовпців досить велика. Складемо наступний запит
www.site.com/view.php?id=-1+order+by+1--
якщо помилки немає, значить стовпців 1 або більше 1
www.site.com/view.php?id=-1+order+by+99999--
При такому запиті повинна з'явитися помилка, що означає стовпців менше 99999. Далі таким же чином звужує кордону обраного інтервалу зліва і справа і в кінцевому підсумку визначаємо реальна кількість стовпців в основному запиті.
[Визначення прінтабельних стовпців]
Припустимо, ми визначили точну кількість стовпців в основному запиті, припустимо їх 4.
www.site.com/view.php?id=-1+union+select+null, null, null, null+from+sys.dual--
Тепер нам необхідно визначити ті стовпці, які виводяться на сторінку. Зазвичай у висновку беруть участь стовпці з типами даних int, char і data. Нам буде достатньо прінтабільних стовпців з типами int і char, їх і будемо шукати.
Як було зазначено раніше, Oracle не провадить автоматичного приведення типів в підзапит. Тому при спробі підстановки в якій або стовпець значення невідповідного типу ми отримаємо таку помилку невідповідності типів
ORA-XXXXX: expression must have same datatype as corresponding expression
Далі ми починаємо складати запити, по черзі замінюючи кожен стовпець на будь-яке число
www.site.com/view.php?id=-1+union+select+123, null, null, null+from+sys.dual--
....
www.site.com/view.php?id=-1+union+select+null, 123, null, null+from+sys.dual--
Таким чином, ми виявимо прінтабельние стовпці з типом int. У тому випадку якщо ми отримаємо помилку невідповідності типів, ми можемо скористатися функціями перетворення типів to_char (), to_date () і виявити прінтабельние стовпці з типами char і data.
www.site.com/view.php?id=-1+union+select+null, to_char(123), null, null+from+sys.dual--
Для довідки приведу синтаксис функції to_char ():
to_char (value, [format_mask], [nls_language])
[Отримання інформації]
Після того як ми дізналися кількість шпальт і які з них прінтабельни, ми можемо сміливо переходити до отримання необхідної інформації з бази. Добре якщо нам відомі певні таблиці в базі і стовпці в них, тоді отримання інформації не складе особливих труднощів. Наприклад, якщо існує таблиця USERS зі стовпцями ID, LOGIN і PASSWORD, то запит на отримання цих даних буде виглядати наступним чином
www.site.com/view.php?id=-1+union+select+null, login, password, null+from+users+where+id=123--
Так само як і в MySQL, для зручності відображення і подолання різних проблем з кодуваннями можна скористатися функціями concat (), to_char ().
Для подолання фільтрації лапок або інших необхідних символів, існує функція chr ().
[Таблиці та стовпці]
Якщо для користувача таблиці нам невідомі, то ми можемо отримати різну інформацію з відомих системних таблиць Oracle.
Дізнатися ім'я користувача, під яким працює інтерфейс, а значить і ви, можна викликавши функції user або sys.login_user
www.site.com/view.php?id=-1+union+select+null, user, null, null+from+sys.dual--
Отримати список сесій можна ось так: select * from V $ session
Великий інтерес представляють таблиці SYS.USER_TABLES і SYS.USER_TAB_COLUMNS, які містять всі таблиці і їх стовпці доступні користувачеві. Витягуємо імена таблиць і стовпців:
www.site.com/view.php?id=-1+union+select+null, table_name, null, null+from+sys.user_tables--
www.site.com/view.php?id=-1+union+select+null, column_name, null, null+from+sys.user_tab_columns--
Так само, на мій погляд, в таблиці SYS.USER_TABLES крім table_name, викликають інтерес такі стовпці: tablespace_name, num_rows, freelist_groups.
Але, на жаль, складені вище запити виведуть нам лише по одній - першого запису з усієї таблиці. Виникає непереборне бажання скористатися оператором LIMIT, як в MySQL або в PostgreSQL. На превеликий загальне розчарування даний оператор не підтримується в Oracle, і більше того не має гідного еквівалента у вигляді іншого оператора.
"ВСЕ ПРОПАЛО !!!" - скажете ви.
"НІ !!!" - відповім я вам.
Помучивши неабияк google, я все-таки знайшов можливість скласти складний запит хоч якось віддалено реалізує смислове навантаження оператора LIMIT. На жаль, не вдалося відновити його можливості в повному обсязі.
www.site.com/view.php?id=-1+union+select+null, table_name, null, null+from+sys.user_tables+where+rownum+<=+5--
Таким чином, перебираючи різну кількість записів у вибірці, ми можемо подивитися по черзі всі імена таблиць. Ту саму конструкцію ми можемо використовувати при перегляді таблиці SYS.USER_TAB_COLUMNS, при отриманні всіх імен стовпців доступних користувачеві.
Так само в Oracle існує поняття префікса об'єкта (таблиця є об'єктом), який присутній в назві або імені таблиці:
ALL_ - всі доступні користувачеві (власником може і не бути),
USER_ - об'єкти, чиїм власником цей користувач є.
Отже, ми можемо спростити собі задачу і витягнути імена тільки тих таблиць, до яких ми маємо доступ
www.site.com/view.php?id=-1+union+select+null, table_name, null, null+from+sys.all_tables
Інтерес так само може представляти інформація з наступних стандартних таблиць: SYS.USER_OBJECTS, SYS.USER_VIEWS, SYS.USER_VIEWS, SYS.USER_CATALOG, SYS.USER_TRIGGERS, SYS.TAB.
[Паролі]
Якщо нам пощастило і користувач, під яким ми працюємо з базою, має права sysdba, то ми можемо отримати хеши всіх користувачів бази.
Основне місце зберігання згортки пароля (хеш) - таблиця словника-довідника SYS.USER $. Над цією таблицею як базової побудована похідна, SYS.DBA_USERS. Якщо в профілі користувача включений параметр PASSWORD_REUSE_TIME, згортки пароля також зберігаються в SYS.USER_HISTORY $. Дістати хеші та імена користувачів можна ось так
www.site.com/view.php?id=-1+union+select+null, username, password, null+from+sys.dba_users
Для повноти інформації представлю ще й алгоритм обчислення згортки пароля, про всяк випадок, може кому і стане в нагоді:
1. До імені користувача приклеюється праворуч текст пароля.
2. У вийшла рядку буквах підвищується регістр.
3. Символи рядка переводяться в багатобайтових формат доповненням зліва нульовим значенням 0x00 (для символів ASCII), і справа рядок дописується нульовими байтами до загальної довжини 80.
4. Отримана рядок шифрується алгоритмом DES в режимі зчеплення блоків шифротекста (CBC) ключем 0x0123456789ABCDEF.
5. З останнього блоку результату видаляються розряди парності і отриманий рядок (56 розрядів) використовується для нового шифрування заданої стрічки тим же способом.
6. Останній блок результату переводиться в знаки шістнадцятковій арифметики і оголошується кінцевим результатом - сверткой.
Коментарі
Коментуючи, пам'ятайте про те, що зміст і тон Вашого повідомлення можуть зачіпати почуття реальних людей, проявляйте повагу та толерантність до своїх співрозмовників навіть у тому випадку, якщо Ви не поділяєте їхню думку, Ваша поведінка за умов свободи висловлювань та анонімності, наданих інтернетом, змінює не тільки віртуальний, але й реальний світ. Всі коменти приховані з індексу, спам контролюється.