Вчера наш портал, написанный на Kohana, подвергся успешной атаке. Мысль, что грешить надо именно на уважаемый фреймворк, безопасность в котором далеко не на последнем месте, сначала даже не обсуждалась. Программке, которой сканировали наш сайт, потребовалось порядка 95 тысяч запросов и 5 часов времени, чтобы найти эту уязвимость.Взгляните внимательно на эти две функции из ядра Коханы версии 3.2:
public function redirect($url = '', $code = 302)
{
$referrer = $this->uri();
$protocol = ($this->secure()) ? 'https' : TRUE;
if (strpos($referrer, '://') === FALSE)
{
$referrer = URL::site($referrer, $protocol, ! empty(Kohana::$index_file));
}
if (strpos($url, '://') === FALSE)
{
// Make the URI into a URL
$url = URL::site($url, TRUE, ! empty(Kohana::$index_file));
}
if (($response = $this->response()) === NULL)
{
$response = $this->create_response();
}
echo $response->status($code)
->headers('Location', $url)
->headers('Referer', $referrer)
->send_headers()
->body();
// Stop execution
exit;
}
public static function site($uri = '', $protocol = NULL, $index = TRUE)
{
// Chop off possible scheme, host, port, user and pass parts
$path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/'));
if ( ! UTF8::is_ascii($path))
{
// Encode all non-ASCII characters, as per RFC 1738
$path = preg_replace('~([^/]+)~e', 'rawurlencode("$1")', $path);
}
// Concat the URL
return URL::base($protocol, $index).$path;
}
Как видно, при использовании функции редиректа у реквеста к текущему uri применяется функция URL::site, в которой используется preg_replace с модификатором исполнения «e»: к каждому сегменту урла применяется rawurlencode, причем сегмент передается в двойных кавычках, что позволяет передать туда что-нибудь вроде (${@ phpinfo()}), и оно отработает. Таким образом, если у нас по ссылке http://site/path/param1 производится редирект, то дописав в param1 выражение вроде (${@ phpinfo()}), можно выполнить какой-нибудь код. В нашем случае, после того как уязвимость была найдена ботом, за дело взялся человек, и спустя некоторое время нехитрыми манипуляциями смог залить шелл через эту дырку. Неоценимую помощь в этом оказал еще такой момент. Кохановский обработчик исключений имеет такой кусочек:
public static function handler(Exception $e)
{
// ..... //
if (Request::$current !== NULL AND Request::current()->is_ajax() === TRUE)
{
// Just display the text of the exception
echo "n{$error}n";
exit(1);
}
// ..... //
}
Запрос имеет заголовок X-Requested-With равным XMLHttpRequest? Держите сообщение об ошибке!Интересно ваше мнение по поводу этих нюансов.
Автор: Grusho