http://www.securitylab.ru/ whice (RST/GHC) Особые благодарности: foster,edisan,Mik,blf,1dt.w0lf "Война на передовой в условиях шаред хостинга на базе WHM/Cpanel/RHE" Вступление Сегодня в условиях развития информационных технологий на рынке е-комерции особое внимание уделяеться таким видам е-бизнеса как "shared hosting". Этот сегмент рынка продолжает стремительно рости и из-за своей распространенности и прибыльности попадает в поле зрения злоумышленников различного уровня. Обеспечение безопасности "shared hosting" имеет свои особенности. Я постараюсь обрисовать особенности защиты при проникновении через apache/mod_php. Известно, что для построения эффективной системы защиты в первую очередь следует определить каким образом может действовать атакующий; какого вида угрозы могут иметь место и исходя из этого сделать выводы о целесообразности самой защиты. И так, рассмотрим стандартные методы проникновения в систему. Приблизительный план атакующего: поиск на сервере доменов, содержащих php скрипт с уязвимостью; использование скрипта для выполнения локальных команд (с уровнем nobody); заливка, запуск backdoor; вход через backdoor; повышение привилегий. Противодействие пунктам 1 и 5 не рассматриваются, так как предполагается, что дырявый скрипт уже найден, а также, что предполагаемые статьей мероприятия нацелены на действия, предотвращающие 5-ый пункт. Учитывая, что среднестатистический сервер на базе WHM (исходя из default установки которую мало кто меняет) использует php как модуль apache (safe_mode = Off ), запускаемые через уязвимый скрипт команды имеют уровень привилегий apache (пользователь nobody). Пример списка процессов: [root@redhat root]# ps -eo %p%u%a --forest ...... 23091root/usr/local/apache/bin/httpd -DSSL 23092nobody\_ /usr/local/apache/bin/httpd -DSSL 23093nobody\_ uname -a ...... После определения возможности запускать програмы, атакующий попытаеться залить backdoor's, exploit's, bot's .... etc с целью получить полноценный shell, повысить уровень привелегий. Исходя из вышеопределенного - выводим следующий план противодействия: Установка noexec nosuid на директории свалок - ограничение директорий свалок (/tmp,/var/tmp) от запуска backdoor, bot и другой закачанной нечисти; Контроль выполняемых процессов - ограничение количества команд, выполняемых от пользователя nobody; Настройка firewall. Настройка cPanel(WHM)+apache+php.ini. Данные меры вполне способны нарушить план атакующего, и отпугнуть многих scriptkidies. Перейдем к непосредственному расмотрению пунктов нашего плана. 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 Тоже но без sticky bit: [root@redhat root]# find / -perm -007 -type d -print Пример вывода: /tmp /tmp/.font-unix /var/tmp /var/spool/samba Так как обычно на Shared hosting не поддерживается работа samba (которая входит в установку по умолчанию), рекомендуется удалить пакет samba*. Вывести все пакеты, имеющие отношение к 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 Если не желаете удалять пакеты, тогда уберите права с директории /var/spool/samba: [root@redhat root]# chmod 0000 /var/spool/samba Если Вы таки используете samba, тогда с этой директорией поступайте так же, как дальше по статье работаем с /tmp. 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 Добавляем в /etc/fstab строки автоматического присоединения файл-разделов при старте сервера: /filesystems/tmp_fs /tmp ext3 defaults, noexec, nosuid, loop 1 1 /filesystems/var_tmp_fs /var/tmp ext3 defaults, noexec, nosuid, loop 1 1 Присоединяем и проверяем присоединились ли новые файл-разделы (не забыв удалить данные из папок /tmp /var/tmp или, если там есть нужные данные, - временно перенести в другое место, а потом положить в новые файл-разделы): [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 1.3 Проверка работы noexec,nosuid. Пробуем запустить 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 Ставим SUID на 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 Как видите, ограничение noexec было обойдено, ограничение на nosuid обойти не удалось (мне не известны способы обхода nosuid на директорию...). Данный способ обхода был действителен для всех доступных мне ОС 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/Perl/Shell: PHP: [root@redhat root]# /usr/bin/php -q /tmp/test.php running Perl: [root@redhat root]# /usr/bin/perl /tmp/test.pl running Shell: [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 2. Контроль выполняемых процессов. Следующий эшелон защиты – это контроль выполняемых процессов. Этот способ заключается в проверке списка процессов и снятии с выполнения неугодных. Вот пример скрипта, который, основываясь на файле-списке разрешенных процессов, убивает остальные. /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 *Для корректной работы WHM/Cpanel в /root/nobody необходимо внести список выполняемых файлов из директорий /scripts/ /usr/local/cpanel/bin/. Проверим работу скрипта. Открыв второй шелл, запустим там команду "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 Сохраним его в директории /tmp, сделаем запускаемым и запустим: [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 В данном скрипте 10.10.10.10 - это IP адрес Вашего сервера. Если сервер имеет более одного IP то необходимо добавить эти-же строки но с другим IP . Сделаем скрипт запускаемым: chmod 755 /etc/firewall.sh Добавим в /etc/rc.local строку: /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 ; запретим вывод php скриптами ошибок: display_errors = Off - выключим передачу информации о php в заголовках HTTP ответов: expose_php = Off Если пользователи не настаивают, то рекомендуем отключить и функции posix, с помощью которых возможен обход некоторых ограничений на выполнение команд. Уязвимости типа SQL injection ограничим включением параметра magic_quotes_gpc = on В некоторых случаях можно было бы установить mod_security. Полезный модуль, который ограничивает внедрение SQL, java script и др. кода. Полезно было бы сбить с толку горе-хакеров, изменив заголовок веб-сервера, отправляемый по умолчанию. Для apache это делается следующим образом. В файле src/include/httpd.h редактируем строки типа: #define SERVER_BASEPRODUCT “Apache” #define SERVER_BASEVERSION “1.3.27” После этого компилируем веб-сервер и устанавливаем его. Впрочем, если вы установили mod_security, это все можно сделать с его помощью, не переустанавливая веб сервер. И, наконец, зайдем в закладку "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 страницам.