Вот шикарная новость от пользователя ValdikSS — Новый Userland-RootKit Azazel. Позволю себе процитировать первый абзац:
Таким образом, в рутките используется штатная возможность подгружать через
Чтобы не быть многословным, сразу представлю простое решение для ядра Linux, которое, перехватывая функцию
Итак, решение оформлено в виде модуля ядра. Перехват функции основывается на методах, описанных ранее в статьях:
Для перехвата функции sys_execve используется следующий код:
Непосредственно перед осуществлением оригинального вызова перехваченной функции
Как видно, при её запуске выделяется память и сканируются все возможные строковые элементы переменных окружения. В завершении сканирования, память соответственно освобождается. В случае, если требуемая строка была обнаружена в журнал ядра будет выведено предупреждение.
Для проверки работоспособности модуля, после его сборки, можно выполнить следующее:
Таким образом, можно сказать, что для параноидально настроенных пользователей есть решение, позволяющее, при желании, защититься от неприятностей, связанных с возможностями
Для тех, кто настроен менее панически данный материал может служить источником информации о том, как работать с ядром, а именно перехватывать его функции и модифицировать поведение системы.
Код модуля доступен на github.
Возможно вы слышали про руткиты Jynx и Jynx2. Это так называемые userland-руткиты, они используют возможность переменной LD_PRELOAD, которая позволяет подгружать любые библиотеки до того, как будет запущена программа. Они уже относительно старые, но все еще хорошо работают.
2 дня назад, Github-пользователь Chokepoint выложил rootkit Azazel. Он основан на исходном коде Jynx и имеет много новых фич:
Антиотладочные механизмы
Скрытие от unhide, lsof, ps, ldd
Скрытие файлов и директорий
Скрытие удаленных подключений
Скрытие процессов
Скрытие логинов
Скрытие от локального сниффинга трафика через PCAP
2 бекдора с полноценными шеллами (с PTY):
— Crypthook accept()-бекдор
— Обычный accept()-бекдор
PAM-бекдор для аутентификации под любым пользователем
Очистка логов utmp/wtmp для PTY
Обфускация строк скомпилированной библиотеки через xor.
Таким образом, в рутките используется штатная возможность подгружать через
LD_PRELOAD
любую библиотеку. Встаёт вопрос, а можно ли это как-то контролировать?Чтобы не быть многословным, сразу представлю простое решение для ядра Linux, которое, перехватывая функцию
execve
(а точнее — sys_execve), сканирует параметры списка переменных окружения (известного как envp
) на наличие в них определённой строки, а именно той самой LD_PRELOAD
.Итак, решение оформлено в виде модуля ядра. Перехват функции основывается на методах, описанных ранее в статьях:
- Управляемый PageFault в ядре Linux
- Перехват функций ядра Linux с использованием исключений (kprobes своими руками)
Для перехвата функции sys_execve используется следующий код:
DECLARE_KHOOK(sys_execve);
long khook_sys_execve( const char __user * file,
const char __user * const __user * argv,
const char __user * const __user * envp )
{
long result;
KHOOK_USAGE_INC(sys_execve);
scan_env_for((void *)file, (void *)envp, env_token);
result = KHOOK_ORIGIN(sys_execve, file, argv, envp);
KHOOK_USAGE_DEC(sys_execve);
return result;
}
Непосредственно перед осуществлением оригинального вызова перехваченной функции
sys_execve
выполняется поиск соответствия переменных окружения заданной строке env_token
. Это делает простая функция scan_env_for:void scan_env_for(char * file, char * envp[], const char * token)
{
int i;
char * string;
char * string_ptr;
if (!envp || !token)
return;
string = kmalloc(MAX_ARG_STRLEN + 1, GFP_KERNEL);
if (!string) {
debug("Can't get memory for the environ string\n");
return;
}
for (i = 0; i < MAX_ARG_STRINGS; i++) {
if (get_user(string_ptr, envp + i)) {
debug("Can't get user pointer value\n");
goto out_kfree;
}
if (string_ptr == NULL)
goto out_kfree;
if (strncpy_from_user(string, string_ptr, MAX_ARG_STRLEN) == -EFAULT) {
debug("Can't get user string\n");
goto out_kfree;
}
string[MAX_ARG_STRLEN] = 0;
if (strncmp(string, token, strlen(token)) == 0) {
char * filename;
filename = kmalloc(PATH_MAX + 1, GFP_KERNEL);
if (filename) {
strncpy_from_user(filename, file, PATH_MAX + 1);
filename[PATH_MAX] = 0;
}
debug("Attention, task \"%s\" trying to execute \"%s\" with \"%s\"\n", \
current->comm, filename ? filename : "(unknown)", string);
kfree(filename);
goto out_kfree;
}
}
out_kfree:
kfree(string);
}
Как видно, при её запуске выделяется память и сканируются все возможные строковые элементы переменных окружения. В завершении сканирования, память соответственно освобождается. В случае, если требуемая строка была обнаружена в журнал ядра будет выведено предупреждение.
Для проверки работоспособности модуля, после его сборки, можно выполнить следующее:
$ sudo insmod envscan.ko env_token=\"LD_PRELOAD=\"
$ dmesg | grep envscan
[37713.809903] [envscan] Symbol "module_free" found @ ffffffff810bdcd0
[37713.810190] [envscan] Symbol "module_alloc" found @ ffffffff810407e0
[37713.810523] [envscan] Symbol "sort_extable" found @ ffffffff81048a90
[37713.810524] [envscan] Hunting for "LD_PRELOAD="
[37713.811798] [envscan] Symbol "sys_execve" found @ ffffffff8119ba30
$ LD_PRELOAD=/lib/ld-linux.so.2 ls
[37743.786499] [envscan] Attention, task "bash" trying to execute "/bin/ls" with "LD_PRELOAD=/lib/ld-linux.so.2"
Таким образом, можно сказать, что для параноидально настроенных пользователей есть решение, позволяющее, при желании, защититься от неприятностей, связанных с возможностями
LD_PRELOAD
.Для тех, кто настроен менее панически данный материал может служить источником информации о том, как работать с ядром, а именно перехватывать его функции и модифицировать поведение системы.
Код модуля доступен на github.