Идеальный шелл (часть 1)

2010-07-09T00:00:00
ID RDOT:255
Type rdot
Reporter Grey
Modified 2010-07-09T00:00:00

Description

Идеальный шелл (часть 1)

Количество шеллов огромно, и каждый задавался вопросом: какой же из них лучше? Для этого нужно определиться с тем, каким требованиям должен удовлетворять шелл.
И наверное почти каждый пытался написать свой шелл, неважно с какой целью - в попытке сделать лучше или просто для знакомства с конструкциями языка с целью его изучения.
Надеюсь эта статья поможет вам не допустить наиболее распространённые ошибки.

Критичных требований к шеллу можно выделить только два, а именно:

1. Максимальная работоспособность.

Тут можно выделить три подпункта:

a) Минимальная зависимость конструкций языка, используемых в шелле, от версии интерпретатора и других условий.

Одно дело, сильные ограничения в системе, не позволяющие использовать все функции шелла, другое - ограничения из-за неразумного использования конструкций.
Пример такой конструкции:

PHP код:

foreach ($_GET as &$value) {
$value = stripslashes($value);
}

Назначение конструкции - удаление экранирования некоторых символов при magic_quotes_gpc = On.
Неуместность такой конструкции для шелла очевидна - использование "&" для сохранения значения переменной доступно начиная только с 5 версии php.

Примером "других условий" может послужить вариант запуска php как cgi, при котором скрипту не передаются необходимые для авторизации переменные.
А значит делать авторизацию нужно c использованием куков, а не через header().

Менее критичной ошибкой может послужить не правильное построение пути директории для просмотра.
Варианта два: /var/www/site.ru/ и /var/www/site.ru. Первый вариант предпочтительнее, т.к. при open_basedir = /var/www/site.ru/ обратиться к директории по адресу /var/www/site.ru будет невозможным.

b) Максимальное количество аналогичных функций.

Чем больше ограничения в системе, тем больше нужно вариантов их обойти. Речь идет не сколько об обходе safe_mode и open_basedir, а о примитивном отключение каких либо функций (system(), exec() и т.д.)
Как показала практика отключение некоторых функций, отвечающих за определение параметров системы может не только свести работу шелла на нет, но и значительным образом затруднить работу с шеллом.
К примеру отключение функции scandir полностью лишает шелл "wso" версии 2.4 работоспособности.

Из того что может быть полезным - для определения параметров системы, нашел 3 функции (ini_get, ini_get_all, get_cfg_var):

PHP код:

if(@function_exists("ini_get")) {
function my_ini_get($option) {
return ini_get($option);
}
} elseif(@function_exists("ini_get_all")) {
function my_ini_get($option) {
$array = ini_get_all();
return $array[$option]["local_value"];
}
} elseif(@function_exists("get_cfg_var")) {
function my_ini_get($option) {
return get_cfg_var($option);
}
} else {
function my_ini_get($option) {
return "???";
}
}

c) Минимальная зависимость определения параметров системы от числа переменных и функций.

Речь идет о излишних проверках перед использованием какой либо функции, к примеру:

PHP код:

if(@function_exists("system") && $safe_mode == "off") {
system($exec);
}

Будем считать что значение переменной $safe_mode определяется до использования этой конструкции.
Но не важно насколько качественно определяется параметр safe_mode - его не правильное определение (к примеру заблокированы все возможные функции для определения значения параметров) не даст возможность выполнять команды и тем самым сократит функциональность шелла.

Для определения возможности использования какой либо функции, лучше ограничиться проверкой с помощью какой то одной функции (причем функция не должна зависеть от версии php, а вероятность отключения этой функции должна быть минимальна).
Я остановился на function_exists: доступна начиная с 4 версии php, её отключение крайне мало вероятно т.к. она применяется в очень большом количестве различных cms. Кстати возвращать false она будет не только в случае более ранней версии php, чем требует проверяемая функция, но и в случае если функция была отключена принудительно.
Кстати сказать в случаях когда отключены все функции через которые можно было бы определить параметры системы, для хоть какого то ориентирования в среде, можно использовать function_exists:

PHP код:

$function_names = array("system", "exec", .......); // Как можно больший список имён функций

function my_existing_functions() {
global $function_names;
echo("<table width='100%'>");
foreach($function_names as $function_name) {
echo("<tr><td>{$function_name}</td><td>"); if(@!function_exists($function_name)) { echo("no "); } echo("exists"); echo("</td></tr>");
}
echo("</table>");
}

Следует упомянуть, что использование функции function_exists допустимо только для определения доступности какой либо функции для вызова, а вот определять параметры системы через неё не корректно. Пример такой ошибки - утверждение о Safe_mode = On при недоступности функций для выполнения команд (system, exec и т.д.). Дело в том, что проверяемые функции могут быть просто отключены, а сам по себе Safe_mode = Off, таким образом будет выведена не точная информация, что так же не допустимо - шелл должен выводить только достоверные данные, а не вводить в заблуждение.

2. Минимальное количество оставляемых следов.

Тут можно выделить 2 подпункта:

a) Записи в логах.

Здесь главным является предотвращение возникновения ошибок, т.к. логи ошибок просматриваются в первую очередь в отличие от на порядки больших по объёму логах с посещениями.
При правильной настройки системы логи с ошибками имеют минимальный размер и в полне возможно что ошибки с залитого шелла будут единственными, а это гарантия спалить шелл.

Далее... Достаточное весомое замечание, сделанное BlackSun - не оправданное использование метода передачи данных POST.
Пример простой - шелл замаскированный под картинку. Запись в логах с обращением к картинки методом POST не может не вызвать подозрений.
С другой стороны предавать данные методом GET ещё более не разумно - по логам можно будет спалить не только шелл, но и действия которые на нем выполнялись.
Помимо замечания, BlackSun предложил и выход, а именно передача данных через cookie, с пример такой реализации можно ознакомиться в теме:

<https://rdot.org/forum/showthread.php?t=65>

Очень советую ознакомиться с заметкой от .Slip (она так же поможет не наследить в логах):

<https://rdot.org/forum/showthread.php?p=2170>

Нельзя не упомянуть и про функцию phpinfo() - она оставляет характерные записи в логах:

Код:

127.0.0.1 - - [09/Jul/2010:22:51:35 +0400] "GET /1/test.php HTTP/1.1" 200 52278
127.0.0.1 - - [09/Jul/2010:22:51:36 +0400] "GET /1/test.php?=PHPE9568F34-D428-11d2-A769-00AA001ACF42 HTTP/1.1" 200 2524
127.0.0.1 - - [09/Jul/2010:22:51:36 +0400] "GET /1/test.php?=PHPE9568F35-D428-11d2-A769-00AA001ACF42 HTTP/1.1" 200 2146

Решение проблемы предложил che:

PHP код:

function ob_phpinfo(){
ob_start();
ob_implicit_flush (0);
phpinfo();
$info=ob_get_contents();
ob_end_clean();
$info=preg_replace('#<img border="0" src="(.*?)" \/>#si','',$info);
print($info);
}

Кстати, как аналог phpinfo() можно сделать самописную функцию, работающую через функции (ini_get, ini_get_all, get_cfg_var) и другие функции для определения других параметров системы (версии php и т.д.). Не следует забывать и о выводе массивов $_SERVER и $_ENV - они несут в себе много ценной информации.

Примеры таких аналогов:

<https://rdot.org/forum/showthread.php?p=2249>

b) Следы в директории с шеллом.

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

© Grey
thanks: che, oRb, BlackSun