Вчера наш портал, написанный на 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; }
Как видно, при использовании функции редиректа у реквеста к текущему uri применяется функция URL::site, в которой используется preg_replace с модификатором исполнения «e»: к каждому сегменту урла применяется rawurlencode, причем сегмент передается в двойных кавычках, что позволяет передать туда что-нибудь вроде (${@ phpinfo()}), и оно отработает. Таким образом, если у нас по ссылке http://site/path/param1 производится редирект, то дописав в param1 выражение вроде (${@ phpinfo()}), можно выполнить какой-нибудь код. Главное, чтобы param1 содержал еще и не ascii-символы, к примеру русские буквы. В нашем случае, после того как уязвимость была найдена ботом, за дело взялся человек, и спустя некоторое время нехитрыми манипуляциями смог залить шелл через эту дырку. Неоценимую помощь в этом оказал еще такой момент. Кохановский обработчик исключений имеет такой кусочек: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; }
Запрос имеет заголовок X-Requested-With равным XMLHttpRequest? Держите сообщение об ошибке! В трекер Kohana уже отписались.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); } // ..... // }
