Перевод статьи Oracle Forensics Part 2: Locating dropped objects by David Litchfield; 24th March 2007 Анатомия Oracle Часть 2 Нахождение удалённых объектов Вступление После успешного взлома базы данных сервера злоумышленник скорее всего попытается спрятать следы своей деятельности, стирая и удаляя объекты созданные или изменённые им во время взлома, например это могут быть таблицы, процедуры и функции. В этой части будет показано, что даже если объект был стёрт из памяти, то в большинстве случаев остаются хоть какие-то следы свидетельствующие об создании/удалении объектов, проанализировав которые можно выстроить если не полную, то хотя бы частичную картину действий атакующего. Есть возможность найти все данные относящиеся к удалённому объекту, всё зависит от того как быстро после взлома начать “расследование”. Блоки данных Oracle До того как мы начнём “поиск” удалённых объектов нужно разобраться в блоках данных Oracle. Каждый файл данных состоит из блоков. Блок — наименьшая единица выделения пространства в Oracle. В блоках и хранятся строки данных, индексов или промежуточные результаты сортировок. Именно блоками сервер Oracle обычно выполняет чтение и запись на диск. Блоки в Oracle бывают размером 2 Кбайта, 4 Кбайта или 8 Кбайт (хотя допустимы также блоки размером 16 Кбайт и 32 Кбайта).Размер блока в базе данных с момента ее создания — величина постоянная и задаётся параметром DB_BLOCK_SIZE, поэтому все блоки в базе данных одного размера. Каждый файл данных имеет заголовок и размер блока задаётся в 20 байтах этого заголовка; далее число блоков задаётся 24 байтами заголовка. Каждый блок разделён на секции, выглядит это примерно так: Блок данных Формат блока Заголовок блока содержит информацию о типе блока (блок таблицы, блок индекса, блок кластера и т.д.), информацию о текущих и прежних изменениях, затронувших блок, а также адрес (местонахождение) блока на диске. Каталог таблиц содержит информацию о таблицах, строки которых хранятся в этом блоке (в блоке могут храниться данные нескольких таблиц). Каталог строк содержит описание хранящихся в блоке строк. Это массив двухбайтовых указателей на строки, хранящиеся в области данных блока. Когда мы добавляем строку то, свободное пространство заполняется снизу вверх, добавляющийся же указатель на эту строку заполняет пространство сверху вниз, получается, что строковые данные и указатели на эти строки, как бы поглощают память в свободном пространстве. Когда же свободное пространство в блоке заканчивается, то сервером выделяется новый блок. Каждая строка блока содержит заголовок размером в 3 байта. Первый байт является маркером и содержит множество флагов, указывающих на состояние строки. К примеру, если у нас строка удаляется, то 5й бит байта станет равным 1, например, при значении маркера – 0х2С, оно становится 0х3С при удалении строки. Это очень важный момент, который стоит запомнить, ибо он является ключевым для обнаружения удалённых объектов. Второй байт заголовка определяет права на запись, а третий указывает на общее количество данных в строке. Если занимаемая память этими данными больше 255 байт, то в заголовок строки добавляется четвёртый байт, который позволяет определять размер строки до 65536 байт. После заголовка идут сами данные строки. Перед каждым столбцом строки данных находится байт, указывающий на размер столбца, если столбец, например пустой(по-другому говоря NULL), то в байте содержится значение 0хFF. В качестве примера можно привести следующее hex представление: Code: 189d3790h: 3C 01 11 189d37a0h: 04 C3 06 13 2F 04 C3 06 13 2F 02 C1 37 0D 4D 59 189d37b0h: 5F 54 45 4D 50 5F 54 41 42 4C 45 02 C1 02 FF 02 189d37c0h: C1 03 07 78 6B 03 17 12 08 38 07 78 6B 03 17 12 189d37d0h: 08 38 07 78 6B 03 17 12 08 38 02 C1 02 FF FF 01 189d37e0h: 80 FF 02 C1 07 02 C1 02 Начиная с заголовка строки (3C 01 11), можно заметить, что мы имеем дело с удалённой строкой, так как 5й бит имеет значение 0х3С, далее можно определить количество столбцов – 0х11 или 17 в десятичной системе. Сразу же после заголовка мы берём первый байт, как счётчик длины столбцов, отсчитывая то число байтов, которое указано в счётчике, мы доберёмся до следующего столбца, там опять в качестве счётчика возьмём первый байт, и так далее до тех пор, пока количество столбцов не будет равно числу указанному в заголовке (в нашем случае 17). На основе этого можно вывести такое постолбцовое представление: Code: Col 1 04 C3 06 13 2F Col 2 04 C3 06 13 2F Col 3 02 C1 37 Col 4 0D 4D 59 5F 54 45 4D 50 5F 54 41 42 4C 45 Col 5 02 C1 02 Col 6 FF Col 7 02 C1 03 Col 8 07 78 6B 03 17 12 08 38 Col 9 07 78 6B 03 17 12 08 38 Col 10 07 78 6B 03 17 12 08 38 Col 11 02 C1 02 Col 12 FF Col 13 FF Col 14 01 80 Col 15 FF Col 16 02 C1 07 Col 17 02 C1 02 Что эти данные представляют, нам сейчас не особо важно, но мы к этому ещё вернёмся и разберём позже. Когда строка удаляется, память занимаемая ею становится снова доступна для записи, это также отмечается каталоге строк (который как мы помним содержит ссылки на строки). Однако, до тех пор пока выделившееся место не будет заново использовано, в нём сохраняются данные строки. Будем в контексте называть такой удалённый указатель “ссылочным” (строка – “ссылочная” или строка со ссылкой), так он из каталога строк ссылается на удалённую строку. Ссылочные удалённые данные можно очень легко найти, как мы выясним в дальнейшем. Зачастую бывает так, что указатель изменяется, но это совсем не значит, что используется заново то место, куда он раньше ссылался, таким образом можно часто найти строки без указателя из каталога строк. Назовём в контексте такую строку ”плавающей”(свободной), так как она не “приякорена”(привязана) ни к одному указателю. Найти такую строку требует немного больше усилий, чем строку со ссылкой. Нахождение удалённых строк Очень важно уметь распознавать удалённые строки в ходе поиска удалённых объектов, так как когда объект удаляется, то удаляются строки относящиеся к нему. Найти удалённую строку достаточно легко. Для этого в блоке данных следует найти каталог строк и для каждого указателя просмотреть все строки, которые могут относиться к указателю. Далее выделяем из заголовка каждой строки 5й бит и если он равен 1 то значит мы нашли то, что нам надо(а именно удалённую строку) [Хотя бывают случаи, когда 5й бит не равен 1, но строка является удаленной, это происходит при удалёнии функции в OBJ$ таблице, это будет рассмотрено позже]. Каждую найденную строку расписываем по столбцам (это мы уже делали чуть выше). Когда все ссылочные строки будут “отловлены” (как блохи, простите не удержался ) у нас останутся “плавающие” строки, и более чем вероятно, что они была либо удалены, либо оставлены после предыдущего апдейта данных. Вышеуказанные действия следует проделать со всеми блоками в файле данных, который был связан с таблицей или только с объектами, которые нас интересуют, что лучше. Нахождение блоков связанных с интересующими объектами Есть несколько таблиц, которые могут заинтересовать нас, когда мы ищем удалённый объект, например таблица OBJ$. Там находятся индексы и кластеры, которые мы и будем рассматривать. На самом деле, нахождение блоков таблицы, индексов и кластеров это одно и то же самое. Как было сказано выше, каждый блок имеет заголовок, в заголовке есть параметр(ID), который указывает на определённый объект. Поэтому нахождение блоков сводится к нахождению ID в заголовках блоков нужного нам объекта, и далее анализ проводится уже только над блоками. Создание и удаление объектов Очень важно понять, что происходит, когда создаётся объект, это нам поможет определить, на что ещё стоит обратить внимание. Так вот, когда создаётся новый объект, то в OBJ$ таблицу добавляются строки. У этой таблицы есть три индекса I_OBJ1, I_OBJ2 и I_OBJ3, в каждый из них заносятся соответствующие данные при создании объекта. I_OBJ2 индексирует имя объекта и ID. В другие таблицы тоже добавляются строки и индексируются. Например, когда создаётся объект, одна строка может добавиться в TAB$ таблицу и одна или более строк в COL$ таблицу. Обе таблицы TAB$ и COL$ находятся в кластере C_OBJ# и это то место, где на самом деле создаются строки. В COL$ таблице есть индекс I_COL1 который индексирует ID объекта и имена столбцов таблицы, так что данные о таблице, как правило, находятся в этом индексе. Когда создаётся функция или процедура одна или более строк добавляются в SOURCE$, IDL_UD1$ и IDL_CHAR$ таблицы. Маленькие кусочки информации об объекте разбросаны по всех базе данных, и это - то, что дает нам зацепки при поиске удалённых объектов. Все эти таблицы, индексы и кластеры являются интересующими нас данными и должны быть хорошо изучены. Это – то, что происходит при создании объекта, что же происходит при его удалении? Когда объект удаляется, строка в OBJ$ помечается как удалённая, изменением маркера с 0х2С на 0х3С(или что тоже самое 5й байт становится равен 1) Такие данные как имя объекта и ID и д.р., всё ещё находятся в памяти, просто они помечены, как удалённые(то бишь их можно использовать ещё раз). То же самое происходит с любой таблицей (TAB$ и COL$), в которой находится информация об объекте. Также информация, находящаяся в индексе I_OBJ2 всё ещё на месте, она лишь обозначена как не существующая. Более того, если на сервере установлена версия Oracle 10g или более поздние версии и поддерживается откат, то строки сохраняются в RECYCLEBIN$. Если в какой-то момент корзина очищается, то строка в RECYCLEBIN$ помечается как удалённая, но опять же все данные остаются, только лишь маркер изменил значение с 0х2С на 0х3С. Единственный минус это то, что в один “прекрасный“ момент строки, помеченные как удалённые, могут быть заново использованы еще где-нибудь, и тогда информация, содержащаяся в них, становится недоступной навсегда. Но, учитывая то, что у нас есть очень много мест, где данные об объекте сохраняются, мы получаем неплохой шанс разузнать то, что нам надо, когда придётся разбираться, почему сервер был взломан. Поиск удалённых таблиц Рассмотрим hex представление блока ниже. Оно взято из файла SYSTEM01.DBF, который используется системным табличным пространством. Как мы можем заметить блок приписан к объекту с ID 0х12 или 18 в десятичной системе. Это OBJ$ таблица. По метке 0х189D379D мы находим маркер помеченный как удалённый 0х3С. Hex дамп блока в OBJ$ Когда мы отмечаем все указатели в каталоге строк и рассматриваем данные по каждому указателю, выясняется что, у нас имеется пара пробелов. Эти пробелы – “плавающие” строки, иными словами у нас есть данные не представленные ни одним указателем в каталоге строк. 0х3С по метке 0x189D379D как раз и есть свободная строка. С начала каталога строк по метке 0x189D2044 должен был быть указатель 0x1759, но его там нет – это говорит о том, что перед нами “плавающий” указатель. И хотя мы видим ASCII имя MY_TEMP_TABLE, мы должны вытащить остальные данные. Вытаскивание данных Заголовок строки – 3С 01 11. Последний байт говорит о том, что у нас 17 столбцов. Code: 189d3790h: 3C 01 11 189d37a0h: 04 C3 06 13 2F 04 C3 06 13 2F 02 C1 37 0D 4D 59 189d37b0h: 5F 54 45 4D 50 5F 54 41 42 4C 45 02 C1 02 FF 02 189d37c0h: C1 03 07 78 6B 03 17 12 08 38 07 78 6B 03 17 12 189d37d0h: 08 38 07 78 6B 03 17 12 08 38 02 C1 02 FF FF 01 189d37e0h: 80 FF 02 C1 07 02 C1 02 Вспоминая, то что мы уже делали в начале, а именно разбитие этого hex представления на столбцы, получаем: Code: 04 C3 06 13 2F 04 C3 06 13 2F 02 C1 37 0D 4D 59 5F 54 45 4D 50 5F 54 41 42 4C 45 02 C1 02 FF 02 C1 03 07 78 6B 03 17 12 08 38 07 78 6B 03 17 12 08 38 07 78 6B 03 17 12 08 38 02 C1 02 FF FF 01 80 FF 02 C1 07 02 C1 02 Чтобы перевести это в понятную человеку информацию, мы должны знать типы столбцов в OBJ$ таблице: Code: OBJ# NUMBER DATAOBJ# NUMBER OWNER# NUMBER NAME VARCHAR2(30) NAMESPACE NUMBER SUBNAME VARCHAR2(30) TYPE# NUMBER CTIME DATE MTIME DATE STIME DATE STATUS NUMBER REMOTEOWNER VARCHAR2(30) LINKNAME VARCHAR2(128) FLAGS NUMBER OID$ RAW(16) SPARE1 NUMBER SPARE2 NUMBER Зная это мы можем начать дамп данных: Code: 04 C3 06 13 2F = ((6-1)*10000) + ((19-1)*100) + (47-1) = 51846 02 C1 37 = 55 0D 4D 59 5F 54 45 4D 50 5F 54 41 42 4C 45 = MY_TEMP_TABLE 02 C1 02 = 1 FF = NULL 02 C1 03 = 2 07 78 6B 03 17 12 08 38 = 23/03/2007 17:07:37 07 78 6B 03 17 12 08 38 = 23/03/2007 17:07:37 07 78 6B 03 17 12 08 38 = 23/03/2007 17:07:37 02 C1 02 = 1 FF = NULL FF = NULL 01 80 = 0 FF = NULL 02 C1 07 = 6 02 C1 02 = 1 Мы можем заметить, что таблица MY_TEMP_TABLE была создана пользователем с ID 55 в 17:07:37 23го марта 2007года. В таблице есть ID объекта – 51846. Ища 04 C3 06 13 2F(закодированный ID) по файлу данных, мы можем обнаружить его в таблице MON_MODS$, I_MON_MODS$_OBJ и RECYCLEBIN$_OBJ – индексы, а C_FILE#_BLOCK# - кластер. Найдя какие-то удалённые части, мы находим всю удалённую таблицу и смотрим нет ли в ней чего интересного. Нахождение удалённых функций Как ни странно, когда удаляется функция, заголовок строки блока из OBJ$ таблицы не изменяется, то есть 5й бит маркера не равен 1. Однако последние два байта столбца STIME приобретают значение 0х3С. Это то же самое, что если бы маркер заголовка изменился. Почему это так – неизвестно, однако эта особенность позволяет достаточно легко выявить удалённые функции. Для интереса давайте предположим, что у нас нет никаких следов удалённых функций ни в одном блоке связанным с таблицей OBJ$. Значит ли это, что функция не создавалась вовсе или же память занимаемая ею использована заново? Что бы найти ответ на вопрос, давайте рассмотрим таблицы SOURCE$ и IDL_UB1$. Hex дамп блока в SOURCE$ В этом представлении мы можем найти большое количество “ссылочных” удалённых строк: Code: 0x1D39805C + 0x1306 = 0x1D399362 0x1D39805C + 0x12EC = 0x1D399348 0x1D39805C + 0x12D8 = 0x1D399334 0x1D39805C + 0x1250 = 0x1D3992AC 0x1D39805C + 0x123E = 0x1D39929A 0x1D39805C + 0x1213 = 0x1D39926F 0x1D39805C + 0x1204 = 0x1D399260 0x1D39805C + 0x11E4 = 0x1D399240 0x1D39805C + 0x11AB = 0x1D399207 Все они относятся к одному объекту с ID C3 06 13 33 (или 51850 в десятичной). Мы можем получить текстовую информацию из удалённых данных: Code: FUNCTION EXTRACT_SYS_PASSWORD RETURN VARCHAR AUTHID CURRENT_USER IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN EXECUTE IMMEDIATE 'INSERT INTO SCOTT.MY_TEMP_TABLE VALUES ((SELECT PASSWORD FROM SYS.DBA_USERS WHERE USERNAME = ''SYS''))'; COMMIT; RETURN 'FOO'; Выглядит достаточно подозрительно. Это код функции EXTRACT_SYS_PASSWORD. Пока нам не известно, кто создал функцию, так как никаких следов в блоках связанных с OBJ$ таблицей нет, мы можем заметить, что есть кое-какая информация в таблице SCOTT.MY_TEMP_TABLE, которая, как мы выяснили ранее, была удалена. Кстати, данная функция берёт хеш пароля SYS пользователя и подставляет его в эту таблицу. Была ли атака успешной, мы узнаем позже, для начала посмотрим, что у нас есть в таблице IDL_UB1$. Эта таблица содержит объект с ID 73. Мы выбираем все блоки относящиеся к этому объекту и получаем следующее: Hex дамп блока в таблице IDL_UB1$ Здесь мы находим ещё несколько удалённых “ссылочных” указателей относящихся к тому же объекту. Также можно заметить довольно большие куски текста функции, и конечно главная часть этого всего – вставка хеша пароля SYS пользователя в MY_TEMP_TABLE. Нахождение MY_TEMP_TABLE Из информации полученной в OBJ$ таблице нам известно, что ID удалённого объекта – 51846. Мы ищем блоки этой таблицы в файлах данных относящихся к табличному пространству USERS, ища ID объекта в заголовках блоком. Ниже дано hex представление как раз такого блока, и так как каталог строк не пуст, то мы можем увидеть нужные нам данные. Они находятся в конце блока, и как можно заметить они то и являются хешом SYS пароля: Hex дамп блока в MY_TEMP_TABLE И так, какой вывод можно сделать? В 17:07:37 23го апреля 2007года, пользователь SCOTT создал таблицу MY_TEMP_TABLE. Так же мы может сказать, что в некоторый момент вызывалась функция EXTRACT_SYS_PASSWORD, которая была закодирована для вставки хеша пароля в таблицу. В таблице как раз есть хеш SYS пароля и пока кажется, что функция использовалась, что бы выдрать этот хеш, но мы не можем это утверждать точно. Есть такая возможность, что функция создавалась лишь для проведения PL/SQL-inj атаки. Возможно redo логии смогут разъяснить картину. Заключение Профессиональный хакер скорее всего, не оставит много следов за собой, вероятно что ему даже не потребуется создавать и удалять объекты(например атака cursor-inj). Но в большинстве случаев сталкиваясь с неопытными взломщиками, можно будет достаточно легко отследить то, что они делали, не смотря на то, что объекты удалены, и казалось бы никакой опасности для хакера нет. _________________________________________________ перевод by VERte][
Супер! Спасибо) Прошу модераторов перенести статью на некоторое время в корень раздела "Статьи", чтоб автору можно было + поставить) Спасибо.