Уязвимость в Kohana?

    Вчера наш портал, написанный на 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()}), можно выполнить какой-нибудь код. Главное, чтобы param1 содержал еще и не ascii-символы, к примеру русские буквы. В нашем случае, после того как уязвимость была найдена ботом, за дело взялся человек, и спустя некоторое время нехитрыми манипуляциями смог залить шелл через эту дырку. Неоценимую помощь в этом оказал еще такой момент. Кохановский обработчик исключений имеет такой кусочек:
    
    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? Держите сообщение об ошибке! В трекер Kohana уже отписались.
    Share post

    Comments 19

      +13
      Вы уже запостили баг в их трекер?
        –13
        Нет пока :)
          +8
          так не делают.
          каким бы ни был глупым баг, сначала надо отрепортить, а потом писать в твиттер и на хабр.
          желательно еще подождать пока его исправят, особенно если баг касается безопасности.
            +1
            >> желательно еще подождать пока его исправят, особенно если баг касается безопасности.
            Не желательно, а обязательно подождать пока его исправят.
        +5
        отличный репорт!
          –2
          Ваш пост похож на хвастовство… «Эй, смотрите, я нашел дыру в Kohana! Ха-ха». Почему нельзя просто исправить и сделать PullRequest?
            –3
            Это конечно правильно, но то что этот парень здесь запостил — отдельная благодарность должна быть, нет?
              –2
              Не вижу аргументов против, нету?
                +4
                От обнародования этой ошибки пострадают люди. Достаточно понятно?
                  +1
                  Люди чаще читают хабр, чем багтрекер коханы.
            0
            Моментально понял, где ошибка, как только зацепился глазом за preg_replace с флагом e.
            Не понимаю, зачем использовать такой код, когда есть preg_replace_callback.
              +1
              Grusho, это познавательно и очень интересно, но лучше спрятать топик до исправления бага.
                +2
                Чтобы исправить надо изменить функцию site
                c $path = preg_replace('~([^/]+)~e', 'rawurlencode("$1")', $path);
                на $path = preg_replace('~([^/]+)~e', 'rawurlencode(\'$1\')', $path);
                0
                Еще в книге «PHP 5» Котеров советовал не использовать «е», а брать вместо него preg_replace_callback…
                  0
                  С дыркой вроде все ясно. А почему у вас возможны такие значения для param1? Все-таки первичный контроль УРЛы должны проходить еще при обработке роутов, используя заданные регекспы для сегментов адреса.
                    0
                    Вопрос закономерный. В некоторых ситуациях допускаем такую вольность. Согласен, возможно и зря, но, согласитесь, это не делает эту уязвимость более оправданной.
                    0
                    Вчера поправили — forum.kohanaframework.org/discussion/11050/kohana-v3-2-2-released
                    Всем обновляться.
                      0
                      Подскажите, что это за программка которая сканировала?

                      Only users with full accounts can post comments. Log in, please.