Вступление
Сегодня в условиях развития информационных технологий на рынке е-комерции особое внимание уделяеться таким видам е-бизнеса как "shared hosting". Этот сегмент рынка продолжает стремительно рости и из-за своей распространенности и прибыльности попадает в поле зрения злоумышленников различного уровня.
Обеспечение безопасности "shared hosting" имеет свои особенности. Я постараюсь обрисовать особенности защиты при проникновении через apache/mod_php.
Известно, что для построения эффективной системы защиты в первую очередь следует определить каким образом может действовать атакующий; какого вида угрозы могут иметь место и исходя из этого сделать выводы о целесообразности самой защиты.
И так, рассмотрим стандартные методы проникновения в систему.
Приблизительный план атакующего:
- поиск на сервере доменов, содержащих php скрипт с уязвимостью;
- использование скрипта для выполнения локальных команд (с уровнем nobody);
- заливка, запуск backdoor;
- вход через backdoor;
- повышение привилегий.
Учитывая, что среднестатистический сервер на базе WHM (исходя из default установки которую мало кто меняет) использует php как модуль apache (safe_mode = Off ), запускаемые через уязвимый скрипт команды имеют уровень привилегий apache (пользователь nobody).
Пример списка процессов:
Код: Выделить всё
[root@redhat root]# ps -eo %p%u%a --forest
......
23091 root /usr/local/apache/bin/httpd -DSSL
23092 nobody \_ /usr/local/apache/bin/httpd -DSSL
23093 nobody \_ uname -a
......
Исходя из вышеопределенного - выводим следующий план противодействия:
- Установка noexec nosuid на директории свалок
- ограничение директорий свалок (/tmp,/var/tmp) от запуска backdoor, bot и другой закачанной нечисти; - Контроль выполняемых процессов
- ограничение количества команд, выполняемых от пользователя nobody; - Настройка firewall.
- Настройка cPanel(WHM)+apache+php.ini.
Перейдем к непосредственному расмотрению пунктов нашего плана.
1. Установка noexec nosuid на директории свалок (/tmp, /var/tmp).
К сожалению, в предустановленном варианте /tmp раздел не всегда идет отдельным разделом, что открывает атакующему дополнительные возможности. Мы прикроем это следующим образом:
- директории-свалки сделаем отдельными файлами и будем присоединять их через mount -o loop,noexec,nosuid;
- будем проверять все директории-свалки и что-то делать с файлами с опастными правами (x, s). Этот пункт противодействия важен, так-как noexec ограничение на раздел возможно обойти;
Разобьем первый пункт на подпункты:
1.1 Определение директорий для контроля
1.2 Создание файл-разделов.
1.3 Проверка работы noexec,nosuid.
1.4 Обход ограничения noexec
1.5 Скрипт для дополнительной защиты.
1.1 Определение директорий для контроля
Определяем, для каких директорий будут создаваться файл-разделы. Обычно это /tmp, /var/tmp .
Для поиска директорий имеющих права запись/выполнение/чтение для всех + sticky bit (1**7) . можно использовать следующую команду:
Код: Выделить всё
[root@redhat root]# find / -perm -1007 -type d -print
Код: Выделить всё
[root@redhat root]# find / -perm -007 -type d -print
Код: Выделить всё
/tmp
/tmp/.font-unix
/var/tmp
/var/spool/samba
Код: Выделить всё
[root@redhat root]# rpm -qa|grep samba
Код: Выделить всё
samba-3.0.9-1.3E.2
samba-common-3.0.9-1.3E.2
samba-client-3.0.9-1.3E.2
redhat-config-samba-1.0.16-2
samba-swat-3.0.9-1.3E.2
Код: Выделить всё
[root@redhat root]# rpm -e samba samba-common samba-client redhat-config-samba samba-swat
Код: Выделить всё
[root@redhat root]# chmod 0000 /var/spool/samba
1.2 Создание файл-разделов.
Мы определили директории, для которых необходимо создать раздел-файлы. Это /tmp, /var/tmp.
Создаем директорию, где будут лежать файлы-разделы:
Код: Выделить всё
mkdir /filesystems
Код: Выделить всё
[root@redhat root]# dd if=/dev/zero of=/filesystems/tmp_fs seek=100 count=1 bs=1M
[root@redhat root]# dd if=/dev/zero of=/filesystems/var_tmp_fs seek=100 count=1 bs=1M
Я решил выделить под /tmp и /var/tmp по 100 M. Вы можете выделить больше - по Вашим потребностям.
Следующим шагом создаем в файлах ФС:
Код: Выделить всё
[root@redhat root]# mkfs.ext3 /filesystems/tmp_fs
[root@redhat root]# mkfs.ext3 /filesystems/var_tmp_fs
Код: Выделить всё
/filesystems/tmp_fs /tmp ext3 defaults, noexec, nosuid, loop 1 1
/filesystems/var_tmp_fs /var/tmp ext3 defaults, noexec, nosuid, loop 1 1
Код: Выделить всё
[root@redhat root]# mount /tmp
[root@redhat root]# mount /var/tmp
[root@redhat root]# df
Код: Выделить всё
Filesystem Size Used Avail Use% Mounted on
/dev/hda1 2.0G 988M 926M 52% /
/filesystems/tmp_fs 98M 4.1M 89M 5% /tmp
/filesystems/var_tmp_fs
98M 4.1M 89M 5% /var/tmp
Пробуем запустить suid файл из под nobody. Вот код С программы:
Код: Выделить всё
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main (int argc, const char * argv[],char * envp[])
{
uid_t uid;
uid = getuid();
printf("My id is %d\n",uid);
if(0 != setuid(0)) printf("\nError suid to root\n");
uid = getuid();
printf("After suid My id is %d\n",uid);
}
Собираем:
Код: Выделить всё
[root@redhat root]# gcc suid.c -o suid
Код: Выделить всё
[root@redhat root]# chmod 4775 suid
Код: Выделить всё
[root@redhat root]# cp suid /
Код: Выделить всё
[root@redhat root]# sudo -u nobody /suid
My id is 99
After suid My id is 0
Копируем в защищенные директории /tmp /var/tmp:
Код: Выделить всё
[root@redhat root]# cp suid /tmp/
[root@redhat root]# cp suid /var/tmp/
Код: Выделить всё
[root@redhat root]# sudo -u nobody /tmp/suid
sudo: unable to exec /tmp/suid: Permission denied
[root@redhat root]# sudo -u nobody /var/tmp/suid
sudo: unable to exec /var/tmp/suid: Permission denied
Сделаем аналогичную проверку для SheLL, Perl, PHP скриптов.
Пример скриптов:
test.php:
Код: Выделить всё
#!/usr/bin/php -q
<? echo "running\n"; ?>
test.pl:
Код: Выделить всё
#!/usr/bin/perl
print "running\n"
test.sh:
Код: Выделить всё
#!/bin/sh
echo "running"
Результатты проверок:
PHP:
Код: Выделить всё
[root@redhat root]# chmod 755 test.php
[root@redhat root]# ./test.php
running
[root@redhat root]# cp test.php /tmp
[root@redhat root]# /tmp/test.php
-bash: /tmp/test.php: /usr/bin/php: bad interpreter: Permission denied
Perl:
Код: Выделить всё
[root@redhat root]# chmod 755 test.pl
[root@redhat root]# ./test.pl
running
[root@redhat root]# cp test.pl /tmp/
[root@redhat root]# /tmp/test.pl
-bash: /tmp/test.pl: /usr/bin/perl: bad interpreter: Permission denied
Shell:
Код: Выделить всё
root@redhat root]# chmod 755 test.sh
[root@redhat root]# ./test.sh
running
[root@redhat root]# cp test.sh /tmp/
[root@redhat root]# /tmp/test.sh
-bash: /tmp/test.sh: /bin/sh: bad interpreter: Permission denied
1.4 Обход ограничения noexec .
Обход noexec при запуске исполняемого файла:
Код: Выделить всё
root@redhat root]# sudo -u nobody /lib/ld-linux.so.2 /tmp/suid
My id is 99
error suid to root
After suid My id is 99
Данный способ обхода был действителен для всех доступных мне ОС Linux RHE (<=2.4.21-27.0.4.EL - последний kernel, доступный для обновление через up2date, в момент написания статьи). В случае использование 2.6.x kernel, собранного с исходников, данный способ уже недействителен.
Код: Выделить всё
root@redhat root]# sudo -u nobody /lib/ld-linux.so.2 /tmp/suid
/tmp/suid: error while loading shared libraries:
/tmp/suid: failed to map segment from shared object: Operation not permitted
PHP:
Код: Выделить всё
[root@redhat root]# /usr/bin/php -q /tmp/test.php
running
Код: Выделить всё
[root@redhat root]# /usr/bin/perl /tmp/test.pl
running
Код: Выделить всё
[root@redhat root]# /bin/sh /tmp/test.sh
running
1.5 Скрипт для дополнительной защиты.
Как видим, одно ограничение noexec, nosuid не способно остановить вторжение, но является дополнительным сдерживающим средством. Расширим его скриптом, который будет проходить по директориям-свалкам и изменять/удалять неугодные нам файлы. Я лишь приведу пример написанный на shell. Вы можете реализовать данную методику любым удобным Вам способом.
Вот код:
Код: Выделить всё
#!/bin/sh
DIR4PROTECT="/root/dir4protect"
for dir in `cat $DIR4PROTECT`;do
FILE2DELETE=`find $dir -perm +1111 -print`
for file in `echo $FILE2DELETE`;do
if [ -f $file ];then
chmod 0000 $file
chown root.root $file
fi
done
done
Проверяем его работу:
Код: Выделить всё
[root@redhat root]# ./tmpprotect.sh
[root@redhat root]# ls -la /tmp
total 45
drwxr-xr-x 3 root root 1024 Apr 29 18:32 .
drwxr-xr-x 20 root root 4096 Apr 28 23:45 ..
---------- 1 root root 0 Apr 29 18:32 1
drwx------ 2 root root 12288 Apr 27 21:31 lost+found
---------- 1 root root 11841 Apr 29 00:36 s2
---------- 1 root root 34 Apr 29 00:10 shell.sh
---------- 1 root root 11841 Apr 29 00:00 suid
---------- 1 root root 40 Apr 29 16:30 test.php
---------- 1 root root 38 Apr 29 16:35 test.pl
---------- 1 root root 26 Apr 29 16:40 test.sh
[root@redhat root]# ls -la /var/tmp
total 29
drwxr-xr-x 3 root root 1024 Apr 29 00:00 .
drwxr-xr-x 19 root root 4096 Mar 4 23:29 ..
drwx------ 2 root root 12288 Apr 27 21:32 lost+found
---------- 1 root root 11841 Apr 29 00:00 suid
Проверяем возможно ли запустить скрипты:
Код: Выделить всё
[root@redhat root]# sudo -u nobody /usr/bin/php -q /tmp/test.php
[root@redhat root]# sudo -u nobody /usr/bin/perl /tmp/test.pl
Can't open perl script "/tmp/test.pl": Permission denied
[root@redhat root]# sudo -u nobody /bin/sh /tmp/test.sh
/tmp/test.sh: /tmp/test.sh: Permission denied
Написанный скрипт помогает блокировать файлы, которые могут быть backdoor, exploits, trojanhorse в директориях свалках. Его можно расширить автоматическим отправлением письма администратору, любыми другими пришедшими вам на ум идеями. Самый простой способ - это установить данный скрипт для автозапуска в crontab на запуск раз в минуту.
Пример содержимого файла крон службы:
Код: Выделить всё
* * * * * /root/tmpprotect.sh
Следующий эшелон защиты – это контроль выполняемых процессов. Этот способ заключается в проверке списка процессов и снятии с выполнения неугодных.
Вот пример скрипта, который, основываясь на файле-списке разрешенных процессов, убивает остальные.
/root/nobody.sh
Код: Выделить всё
#!/bin/sh
USER="nobody"
RULEFILE="/root/nobody"
if [ -f "$RULEFILE" ]; then
KPID=`ps -eo "%p%u%c"|grep $USER|grep -vi $$|grep -vif $RULEFILE |awk '{print$1}'`
for j in `echo "$KPID"`; do
kill -9 $j >> /dev/null 2>&1
done
fi
/root/nobody
Код: Выделить всё
httpd
suexec
Проверим работу скрипта. Открыв второй шелл, запустим там команду "sudo -u nobody sleep 500", а в первом – на выполнение наш скрипт.
Код: Выделить всё
[root@redhat root]# sudo -u nobody sleep 500
Killed
---
[root@redhat root]# chmod 750 nobody.sh
[root@redhat root]# ./nobody.sh
Теперь попробуем другой скрипт:
httpd:
Код: Выделить всё
#!/bin/sh
while [ "1" ];do
sleep 1
done
Код: Выделить всё
[root@redhat tmp]# sudo -u nobody ./httpd
sudo: unable to exec ./httpd: Permission denied
[root@redhat tmp]# sudo -u nobody /bin/sh ./httpd
Killed
Наш скрипт его убивает, и все потому, что он видится как процесс sh. Для примера того, что скрипт может быть не убиваем, скопируем его в / и запустим оттуда. ( в реальной жизни это будет директория какого-то пользователя, который открыл директорию на заливку (777) )
Код: Выделить всё
[root@redhat /]# sudo -u nobody ./httpd
./httpd: line 4: 4101 Killed sleep 1
./httpd: line 4: 4111 Killed sleep 1
Позапускав наш скрипт nobody.sh видим, что главный скрипт не умирает, а вот потомки снимаються с выполнения. Это из-за того, что главный скрипт подпадает под разрешенные процессы.
Приведенный вариант не панацея и является всего лишь толчком для Вашей фантазии. Вы его можете модернизировать, переделать или сделать как Вам удобнее. Главное – я показал принцип и точку, в которой можно противодействовать вторжению.
3. Настройка Firewall.
Учитывая нюансы WHM/Cpanel, перечислим порты, которые должны быть открыты на вход:
TCP 21,22,25,53,80,110,443,2082,2083,2086,2087,2089,2095,2096
UDP 53
ICMP 0,30,8
на выход:
TCP 25,53,2089
( 2089 необходим для работы WHM для авторизации)
UDP 53
ICMP 0,30,8
Напишем скрипт, который будет загружать в iptables правила для файрволинга при старте системы. Ограничивая исходящие соединения, мы тем самым уменьшаем количество возможных backconnect.
/etc/firewall.sh:
Код: Выделить всё
#/bin/sh
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 21 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 22 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 25 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 53 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 80 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 110 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 443 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 2082 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 2083 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 2086 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 2087 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 2089 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 2095 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -s 0/0 -d 10.10.10.10 --dport 2096 -j ACCEPT
/sbin/iptables -A INPUT -p udp -s 0/0 -d 10.10.10.10 --dport 53 -j ACCEPT
/sbin/iptables -A INPUT -p icmp -s 0/0 -d 10.10.10.10 --icmp-type 0 -j ACCEPT
/sbin/iptables -A INPUT -p icmp -s 0/0 -d 10.10.10.10 --icmp-type 8 -j ACCEPT
/sbin/iptables -A INPUT -p icmp -s 0/0 -d 10.10.10.10 --icmp-type 30 -j ACCEPT
/sbin/iptables -A INPUT -p all -s 0/0 -d 10.10.10.10 -j DROP
/sbin/iptables -A OUTPUT -p tcp -d 0/0 -s 10.10.10.10 --dport 25 -j ACCEPT
/sbin/iptables -A OUTPUT -p tcp -d 0/0 -s 10.10.10.10 --dport 53 -j ACCEPT
/sbin/iptables -A OUTPUT -p tcp -d 0/0 -s 10.10.10.10 --dport 2089 -j ACCEPT
/sbin/iptables -A OUTPUT -p udp -d 0/0 -s 10.10.10.10 --dport 53 -j ACCEPT
/sbin/iptables -A OUTPUT -p icmp -d 0/0 -s 10.10.10.10 --icmp-type 0 -j ACCEPT
/sbin/iptables -A OUTPUT -p icmp -d 0/0 -s 10.10.10.10 --icmp-type 8 -j ACCEPT
/sbin/iptables -A OUTPUT -p icmp -d 0/0 -s 10.10.10.10 --icmp-type 30 -j ACCEPT
/sbin/iptables -A OUTPUT -p all -d 0/0 -s 10.10.10.10 -j DROP
Сделаем скрипт запускаемым:
Код: Выделить всё
chmod 755 /etc/firewall.sh
Код: Выделить всё
/etc/firewall.sh
/etc/firewall.sh
Будте осторожны - Вы можете закрыть доступ на сервер для самого себя.
Данный скрипт лиш пример-идея, которую Вы можете изменить согласно Вашим требованиям. Главное требование - это открыть лишь то, что необходимо для функционирования сервера, а остальное - закрыть.
4. Настройка cPanel(WHM)+apache+php.ini
Есть две задачи, которые в данном случае приходится решать, идя на компромис между ними:
- максимум возможностей для пользователя,
- максимум безопасности на уровне пользователя.
Увеличивая возможности для пользователя, а, соответственно, и привлекательность хостинга, нам прийдется забыть о safe mode для PHP. Однако, чтоб немного оградить тех же пользователей от взлома их страниц, а свой хостинг от проникновения скрипт-кидди, нам необходимо ограничить опасные функции, которые обычно используются для начального взлома системы.
Действовать будем, отталкиваясь от популярных уязвимостей.
Во-первых, это внедрение PHP кода в сценарии и выполнение произвольных команд. В php.ini прописываем следующее:
- запретим открытие/включение удаленных файлов:
Код: Выделить всё
allow_url_fopen = Off
enable_dl = Off
Код: Выделить всё
disable_functions = ini_alter, curl_exec, exec, system, passthru, shell_exec, proc_open, proc_close, proc_get_status, proc_nice, proc_terminate, leak, listen, chgrp, chmod, set_time_limit, apache_note, apache_setenv, closelog, debugger_off, debugger_on, define_syslog_variables, openlog, syslog,ftp_exec,phpinfo,dl ;
Код: Выделить всё
display_errors = Off
Код: Выделить всё
expose_php = Off
Уязвимости типа SQL injection ограничим включением параметра
Код: Выделить всё
magic_quotes_gpc = on
Полезно было бы сбить с толку горе-хакеров, изменив заголовок веб-сервера, отправляемый по умолчанию. Для apache это делается следующим образом. В файле src/include/httpd.h редактируем строки типа:
Код: Выделить всё
#define SERVER_BASEPRODUCT “Apache”
#define SERVER_BASEVERSION “1.3.27”
И, наконец, зайдем в закладку "Tweak Security" в cPanel/WHM и активируем:
Php open_basedir Tweak
Compilers Tweak
Первый установит в httpd.conf на все VirtualHost open_basedir на домашнюю папку пользователя.
Второй разрешит доступ к GCC только для указанных в списке пользователей.
Эпилог
В данной статье рассмотрены методы борьбы на "передовой"; противодействие атакующему через веб-скрипты (apache/mod_php) на базе распространенной панели для shared hosting WHM/Cpanel (cpanel.net) с ОС Linux RHE. (Учитывая особенности работы данной панели, применяемые меры не идут в разрез с работой WHM/Cpanel), которые дают Вам зацепку, через которую можно выстроить достаточно плотную защиту обход которой будет не всегда соразмерен полученному результату.
P.S. Для всех используемых команд для более детального понимания прошу обращаться к man страницам.
Оригинал статьи здесь: http://www.securitylab.ru/contest/212124.php