Task # Task #6

Discussion in 'Задания/Квесты/CTF/Конкурсы' started by crlf, 4 Apr 2019.

  1. pas9x

    pas9x Elder - Старейшина

    Joined:
    13 Oct 2012
    Messages:
    423
    Likes Received:
    585
    Reputations:
    52
    Извиняюсь, что заставил долго ждать. На выходных было много дел. Времени вообще нет. Поэтому решил сделать упрощённую версию врайтапа. Изначально я её планировал более пафосную, с картиночками и прочими ништяками. В течение недели ещё несколько раз буду редактировать и дополнять этот пост. Скажу сразу: не все из изложенных мной предположений верны (особенно про вежливость), я просто выкладываю какие мысли у меня были в голове по ходу прохождения конкурса.

    [ Флаг 0 ]
    Для начала нам надо найти точку входа в CTF. Нам известно, что она находится на сервере 94.X5Y.X5Y.96. Значит теоретический максимум для перебора - 100 комбинаций цифр. Но учитывая, что X может иметь значение от 0 до 2 диапазон поиска сужается до 30 айпишников. При этом нам не известно на каком порту работает точка входа и нет-ли в пространстве айпишников левых серверов (которые не относятся к CTF). Пробуем самый простой вариант: пишем пхп-скрипт и через fsockopen ищем сервер с открытым 80 портом. И оказывается, что 80 порт открыт только на сервере http://94.250.250.96/

    [ Флаг 1 ]
    Обходим сайт, внимательно смотрим в html-код. В коде страницы портфолио видим странный коммент. Пытаемся раскодировать его как base64, но из-под него выходит какой-то бинарный блок данных. По его виду непонятно что это за блок данных. Наугад пробуем сохранить его в файл и открыть с помощью 7zip. Оказывается, что это zip-архив с запароленым файлом 404.txt. Ищем в нэте какой-нибудь брутер zip-архивов. Я воспользовался Ultimate ZIP Cracker. Долго ждать не пришлось, пароль на архив: 123
    Но оказалось, что это просто пасхалка с текстом "Bingo! You have found an Easter egg, hurry to contact me and do not forget to send some interesting HTML materials! :)"

    Продолжаем изучать сервер. Сканим все tcp-порты nmap'ом, но оказывается что на сервере нет открытых портов кроме 22 и 80. Значит выбор у нас не велик: копать веб-сервер. Ну или попробовать брутить ssh. Ещё я поискал нмапом другие серверы с айпишником 94.X5Y.X5Y.96 но ничего стоящего не нашлось. Кстати, подключившись любым tcp-клиентом (например nc) на 22 порт во многих случаях ssh-сервер выдаёт нам название и версию операционной системы. В нашем случае ssh-сервер нам выдал: SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u4

    Копаем сайт. С помощью ffuf вдоль и поперёк сканим директории/файлы сайта, но из интересного находим только директорию /uploads. Как оказалось, в неё складывают загруженные в форму обратной связи файлы. Ничего кроме картинок и текстовых файлов в неё загрузить невозможно. При попытке загрузить файл с расширением php он там не появляется (это было-бы слишком просто). Однако если добавить php-код в конец нормальной картинки то такой файл загрузится как есть.

    На первый взгляд кажется, что тут есть дыра в параметре page. Видно, что тут как-то странно обрабатывается расширение файла. Имею право предположить, что страницы лежат на сервере локально и просто инклудятся. Былоб неплохо подинклудить jpeg-картинку с нашим пхп-кодом. Но опять-же это было-бы слишком просто. Ещё немного поигравшись с параметром page у меня начинает закрадываться подозрение, что это ловука.

    Пораскинув мозгами бросаем затею с параметром page и пробуем отправлять всякую хрень в форму обратной связи. Пасхалка нам явно говорит, что надо отправлять в неё какой-то хтмл-код. И тут я подумал: а что если на бекенде какой-то пхп-скрипт проходит по ссылкам в этом хтмл-коде? Надо попробовать накидать в форму хтмл с разными ссылками. Я отправил в форму хтмл с тегами <a>, <img> и глянул лог сайта ссылки на который засабмитил в форму. И действительно: по этим ссылкам зашёл некто с юзер-агентом Moskilla/1.337 (BeOS XS; X-Header-Powered-Browser) Pro/3.5737 с айпишника 94.250.250.96. Header-Powered-Browser нам как-бы намекает, что надо глянуть остальные заголовки клиента. Запускаем tcpdump и сниффирим трафик с 94.250.250.96. Почему tcpdump? Потому что вдруг этот сервер ещё куда-то заходит или что-то делает - я должен об этом знать. Скачиваем дамп трафика на комп и смотрим его в wireshark. Но там всего лишь одиночный http-запрос с заголовком X-Hint: If you try to be more polite, then you will get a surprise :)
    Блин. Вообщето я ожидал увидеть в заголовках флаг а сервер требует просить вежливее. Но что значит вежливее? Тут я вспомнил, что уже несколько раз сабмитил форму. Может невежливостью считается частая отправка формы или ссылки на один и тот-же сайт? Решил подождать несколько часов и засабмитить форму с другим текстом и с другими ссылками. И тут по счастливой случайности я засабмитил в форму текст "Hi" и в заголовках увидел:
    Code:
                [HTTP_X_REFERER] => http://host2.developer.skill.programmer/messages.php
                [HTTP_X_FLAG] => {skl_wh0_said_s0cial_3ngin33ring?}
                [HTTP_USER_AGENT] => Moskilla/1.337 (BeOS XS; X-Header-Powered-Browser) Pro/3.5737
    Охрененчик! Действительно, надо было не так часто самбитить форму, с разных айпишников и с разным текстом. Тут прикол в том, что на радостях я не обратил внимания на заголовок referer и побежал в личку к @crlf отправлять первый флаг :) Так как в топике что-то говорилось про социальную инженерию, да и флаг нам намекает на неё я подумал: самое время применить соц инженерию. Но в каком виде? И тогда я набравшись наглости начал взламывать соц инженерией самого автора таска. И просто задал ему вопрос: "куда копать дальше?" на что естественно получил ответ "смотри заголовки ответа". Даже как-то стыдно :)
    Но вообще этот флаг я прошёл по чистой случайности. Еслиб я не вбил в форму "hi" то вообще не прошёл-бы конкурс.

    [ Флаг 2 ]
    Прописываем в хост-файл 94.250.250.96 host2.developer.skill.programmer и заходим на сайт. Подставляя кавычки и прочую дрянь в форму естественно ничего я не добился. Глянул в хтмл и заметил странность: в форме почему-то два инпута с name=login. Т.е. какой-бы логин ты не отпраивл всёравно на бекенд уйдёт и введённый логин и login=Verify. Т.е. форма авторизации тут вообще закосячена. Ок, снова запускаем сканер директорий и находим директорию /misc, содержимое которой бесценно. Среди всего прочего в ней есть файлик README.md с флагом {skl_d1r3ctory_lis7ing_1s_a_bad_id3a.}
    В файле in.php какая-то синтаксическая ошибка благодаря которой теперь нам известен путь к директории сайта: /var/www/html/host2.developer/misc/in.php

    [ Флаг 3 ]
    Смотрим auth.php.old. С этого флага начинается проверка знания php и linux. Если первые 2 флага проверяли твою интуицию то все флаги начиная с третьего - это проверка знания php и проверка на внимательность. Начиная с этого флага нам надо тщательнейшим образом проверять все пхп-исходники, вглядываться в каждую строчку кода и перебирать варианты что с ней может быть не так. Первое на что нужно обратить внимание - это то, что файлы auth_secrets.php и auth_secrets.php.old имеют разный размер. С высокой степенью вероятности это означает то, что ключи поменялись. Я на это не обратил внимания и потратил некоторое время впустую пытаясь подставлять в куки значения сформированные с использованием auth.php.old.

    Второе на что нужно обратить внимание - это операция extract($user['auth']), что крайне небезопасно. Используя это мы можем устанавливать какие угодно переменные. В том числе мы можем переопределять суперглобальные пхпшные переменные $_GET, $_POST, $_COOKIE, $_FILES.

    Далее в операторе if нам нужно декомпозировать строку in_array($login,['admin','superuser','hackersucks'])&&strlen($user_hash)&&$auth_hash==$user_hash&&4>$auth_group&&$log=='local'&&$auth_id<=5&&strcmp($local_signature,$signature)==0 чтобы понять какого хрена в ней делается. Переводим её в человекопонятный вид и в итоге получаем следующее:
    PHP:
    $condition1 in_array($login, ['admin','superuser','hackersucks']);
    $condition2 strlen($user_hash);
    $condition3 $auth_hash == $user_hash;
    $condition4 $auth_group;
    $condition5 $log == 'local';
    $condition6 $auth_id <= 5;
    $condition7 strcmp($local_signature$signature) == 0;

    if (
    $condition1 && $condition2 && $condition3 && $condition4 && $condition5 && $condition6 && $condition7)
        
    $auth true;
    else{
        include(
    'login.php');
    }
    Т.е. чтобы пройти авторизацию нам нужно удовлетворить 7 условий. Чтобы пройти этот флаг нам надо подготовить тестовую среду в которой мы будем проводить эксперименты. Скачайте zip-архив с файлами прохождения, он содержит директорию flag3 в которой и находится тестовая среда. Ознакомьтесь с одержанием auth.php а затем выполните команду php auth.php которая покажет какие условия выполнены а какие нет.
    Сходу мы можем сформировать такую куку, которая удовлетворит все условия кроме 3 и 7:
    PHP:
    $sessionCookie = [
        
    'auth' => [
            
    'id' => 1,
            
    'user' => 'admin',
            
    'group' => 1,
            
    'hash' => 'qwe',
            
    'log' => 'local',
            
    'signature' => 'rty',
        ],
    ];
    $_COOKIE['session'] = base64_encode(json_encode($sessionCookie));
    $_COOKIE['signature'] = 'test';
    Но как выполнить 3 и 7 условие? Ведь чтобы их выполнить нам нужно правильное значение $secret, $secretx или $secrety. Если мы возьмём его из auth.php.old то конечно локально у нас все условия будут выполнены. Но на сервере эти значения другие а потому авторизация не пройдёт. Те, кто хорошо знаком с языком в курсе, что в php есть несколько операторов сравнения: https://www.php.net/manual/ru/language.operators.comparison.php
    Оператор === требует чтобы оба операнда были идентичны. Т.е. выражение (1 === '1') вычислится как false.
    Но оператор == выполняет конвертацию типов. Поэтому выражение (1 == '1') вычислится как true.
    Подробнее о том, как пхп выполняет конвертацию типов при сравнении посмотреть можно здесь: https://www.php.net/manual/ru/types.comparisons.php

    Как мы видим, в третьем условии используется оператор == , чем мы и воспользуемся. Функция sha1 и md5 возвращает строку. И если в параметре hash мы укажем неверный хеш то результат операции сравнения будет false. Т.е. при сравнении строки со строкой операция сравнения отрабатывает корректно. Но мы можем подсунуть в качестве значения hash не строку а true и тогда пхп выполнит конвертацию строки в boolean, из-за чего результатом сравнения будет true.
    Итак, подсунув в параметр hash вместо нормального хеша значение true мы добились выполнения 3 условия.

    Теперь нам надо добиться выполнения 7 условия. Здесь мы используем ту-же слабость php. Но дополнительно нам надо почитать доку по этой функции: https://www.php.net/manual/ru/function.strcmp.php
    Нужно не просто почитать доку по этой функции, но ещё и хорошо поэкспериментировать с ней. И в ходе экспериментов мы видим, что это очень проблемная функция с абсолютно непредсказуемым результатом. Одна из проблем этой функции в том, что она может возвращать null, хотя в документации об этом не сказано (хотя должно быть сказано). Если мы в аргументе $signature вместо нормальной строки подставим массив то пхп выдаст "Warning: strcmp() expects parameter 2 to be string, array given" а сама функция вернёт значение null. И, как вы уже догадались, 0 == null :)

    Если вам влом вручную формировать правильные значения то для прохождения авторизации в админке можете прописать браузеру следующие куки:
    session=eyJhdXRoIjp7ImlkIjoxLCJ1c2VyIjoiYWRtaW4iLCJncm91cCI6MSwiaGFzaCI6dHJ1ZSwibG9nIjoibG9jYWwiLCJzaWduYXR1cmUiOlsiZnVjayB5b3UiXX19
    signature=fake

    Когда мы прописали браузеру правильные куки - можем смело заходить в админку, где нас сразу встречает третий флаг: {skl_y0u_ar3_supp4_us3r!}

    [ Флаг 4 ]
    Потыкав форму находим в ней много вкусного: тут и загрузка файлов по ссылке и exec :) Естественно первым делом нас интересует exec, но поигравшись со значениями параметра command понимаем, что ни к чему хорошему нас это не приводит. Тогда играемся с формой скачивания файлов и понимаем, что с помощью неё можно получить содержимое по любой ссылке, главное чтоб в url присутствовало значение ?print=1 . Могу предположить, что для простоты содержимое ссылки скачивается функцией file_get_contents() и если это действительно так, то тогда мы можем пробовать разные пхп-шные врапперы. Первое что приходит на ум - использовать для чтения файлов схему file:// но нам мешает требование наличия ?print=1. Хм. А мешает-ли? Ведь мы можем воспользоваться нормализацией пути и использовать для этого ..
    Почти в любой unix-подобной ОС есть файлы которые лежат по одному и тому-же пути, например: /etc/hosts /etc/hostname /etc/resolv.conf /etc/os-release независимо от ОС. Доступ на чтение этих файлов есть всегда и у любого юзера, поэтому проще всего пробовать прочесть их. Пробуем получить содержимое по такому url: file:///?print=1/../etc/hosts и вуаля: мы считали хост-файл :)

    Круто. Теперь мы можем читать любые файлы на сервере. Из второго флага нам известно в какой директории лежит сайт, так что считываем файл file:///?print=1/../var/www/html/host2.developer/index.php и забираем 4 флаг: {skl_wha7_is_g0ing_0n_h3r3?}

    [ Флаг 5 ]
    Первое, что хочется сделать на этом этапе - узнать как можно больше информации о сервере. Чтобы удобно читать содержимое файлов я накатал функцию downloadFile($path) и на основе неё сделал скриптик flag5/cat.php (архив во вложении). Мы уже знаем, что на сервере стоит Debian 10. На мою удачу, на работе у меня оказался серверок с дебианом где есть nginx+php-fpm, поэтому мне не составило труда узнать точные пути к дефолтовым конфигам. Хотя еслиб такого сервера у меня не оказалось - я-бы установил дебиан и весь нужный софт на виртуалку, просто это заняло-бы больше времени. Покопавшись в конфигах не нашлось ничего интересного, конфигурация сервера почти дефолтовая. Конфиг php-fpm тоже дефолтовый. Из более-менее интересного:
    /var/backups/dpkg.status.0 - список установленного на сервер софта
    /etc/nginx/nginx.conf
    /etc/nginx/sites-enabled/default
    Из этих файлов мы можем узнать что сервер почти голый, ничего кроме nginx+php-fpm на него не установлено а у nginx всего два виртуальных хоста: skill.programmer (дефолтовый сайт) и host2.developer.skill.programmer. Всё это говорит о том, что не стоит тратить время на поиск уязвимостей конфигурации и на поиск каких-то дыр в других местах. На дефолтовом сайте index.php доставляет немного лулзов, но для нас он бесполезен. Лучше сосредоточиться на host2.developer.skill.programmer.

    На этом флаге я проделал дофигищу бесполезной работы и проверил кучу гипотез:
    1. Может быть какой-то файл в директории skillibs модифицирован и в него встроен флаг или бекдор? Первым делом я написал скрипт который с помощью вышеупомянутой функции downloadFile целиком выкачивает все файлы из skillibs, затем на своём компе выкачал компосером все те-же версии либ и сделал сравнение файлов. Но различий найдено небыло (помимо автогенерированных файлов компосера, но это ерунда).
    2. Возможно имеет место RCE при десериализации объектов? Но пробежавшись по __destuct() и __wakeup() не нашлось ничего стоящего (и в этом я ошибался).
    3. Возможно есть какая-то дыра в автолоадере и подставляя в десериализатор специально сформированные имена классов удастся подинклудить файл из uploads. Но пхп жёстко фильтрует имена классов и неймспейсы.
    4. Хинт по 5 флагу рекомендует почитать посты из блога команды взломавшей порнхаб через дыру в сборщике мусора. Почитав сам пост об этой дыре я приуныл: c++ на должном уровне я не знаю а PHP на сервере версии 7.0.30 и в ней этой уязвимости уже нет. PHP на моём сервере той-же версии и PoC-код из вышеуказанной статьи не срабатывает.
    5. Ещё один хинт гласит, что "библиотеки меняются на глазах" - не знаю как это понимать. Может в директории skillibs периодически что-то изменяется? Для уверенности что это не так я написал скрипт который каждые 30 секунд обходит все поддиректории в skillibs и смотрит не поменялся-ли размер файлов или не появилось-ли новых файлов. Через сутки естественно ничего такого не нашлось.
    6. Попытался использовать XXE с expect:// причём в форме обратной связи тоже. Однако эксперименты на своём сервере показали что без флага LIBXML_NOENT xxe в пхп не работает.
    Провал по всем направлениям очень сильно обламывает. Куча времени потрачено впустую а у тебя нет ни единой зацепки. У тебя даже интуитивно нет предположения в какую сторону копать. Я решил признать, что это не мой уровень, сдался и пошёл спать. С утра зачем-то я опять полез в код skillibs. И тут меня снова появилась надежда: в деструкторе PHPMailer вызывается метод smtpClose() а в нём $this->smtp->connected(). Если мы подсунем $this->smtp какой-то левый объект то при отсутствии метода connected() будет вызван магический метод __call(). Классов с этим методом оказалось не много и я легко пробежался по всем. С первого-же взгляда на Services_Twilio_PartialApplicationHelper меня озарило: call_user_func_array - ЭТО-ЖЕ ИМЕННО ТО, ЧТО НАДО! Я спешно склепал сериализованный объект, протестировал десериализацию и оказалось, что это реально работает. Ахаха, пятый флаг мой - подумалось. Но я ещё не знал, что ждёт меня впереди. Засабмитив сериализованный объект вместо ожидаемого OK я получил ERROR. Да, я предусмотрел что из-за preg_match надо вложить объект в массив. Но почему-то всёравно получал ERROR. Какого хрена? Пятый флаг всё ещё не мой? Я начал копать почему это происходит и обнаружил, что у меня на компе это тоже не работает. А виною всему simplexml который не хавает сериализованный объект. Как оказалось, после сериализации объекта из-под функции serialize выходит всем нам привычный сериализованный код за одним только исключением: имена всех приватных свойств объектов содержат нуль-байт. Я подумал: ну не проблема, экранируем все спецсимволы с в коды по типу &#0; но и это не проканало. Как оказалось, стандарт XML вообще запрещает в xml наличие байт с кодом ниже 9, даже если символы экранированы. И ты можешь хоть жопу рвать (и гуглить xml binary data), но нуль-байт при любом раскладе в xml не допускается. Это огорчает. Я не должен просрать в одном шаге от последнего флага из-за такой ерунды. И тут я вспомнил, что в одной статье читал о разных способах сериализации и что, вроде как, есть способ сериализовать строку с экранированием спецсимволов. В ходе экспериментов оказалось, что пхп почему-то по умолчанию не применяет такой метод и всегда сериализует строку методом s, хотя десериализация методом S также срабатывает. Я написал функцию-костыль которая корректирует данные полученные из функции unserialize и переделывает метод сериализации строк с s на S (с экранированием нуль-байта естественно). И наконец в конце всех мытарств я получил долгожданный OK. Естественно в call_user_func_array я прописал file_put_contents и благополучно залил шелл в директорию uploads :) Пробежавшись по папочкам на сервере я нашёл файл http://host2.developer.skill.programmer/flag_acd46ae078a1838e38b68b852a8aeba2ff592536.txt с флагом {skl_d3s3rial1z4ti0n_is_c00l_did_y0u_l1k3_it?}

    На этом всё, а теперь мне пора баиньки :)

    PS: архив во вложении обновил с учётом пятого флага.
     

    Attached Files:

    #41 pas9x, 22 Apr 2019
    Last edited: 22 Apr 2019
    sinusu, D3N, ZodiaX and 11 others like this.
  2. dooble

    dooble Members of Antichat

    Joined:
    30 Dec 2016
    Messages:
    231
    Likes Received:
    601
    Reputations:
    145
    SkillProgrammer, удивленный атакой скилловых хакеров, свернул свою инфраструктуру и потушил сервера.
    Надеюсь все успели проверить свои мысли и оценить полное прохождение.
    Еще раз спасибо ТС за наводку на интересный ресурс.
     
    D3N and BabaDook like this.
  3. giloo

    giloo New Member

    Joined:
    2 Feb 2019
    Messages:
    28
    Likes Received:
    3
    Reputations:
    4
    Можете пожалуйста подсказать или описать как работает уязвимость (0day для 4 флага)? Давать как есть не обязательно) мне нужна удочка
    Сейчас я изучаю методологию написания эксплоитов и техники фаззинга. По всякому стараюсь трясти бинарники. Было бы не плохо если бы профи своего дела меня направили)
     
    #43 giloo, 17 May 2019
    Last edited: 17 May 2019
  4. BabaDook

    BabaDook Well-Known Member

    Joined:
    9 May 2015
    Messages:
    1,063
    Likes Received:
    1,559
    Reputations:
    40
    Вроде все расписали.
     
  5. giloo

    giloo New Member

    Joined:
    2 Feb 2019
    Messages:
    28
    Likes Received:
    3
    Reputations:
    4
    Процитируйте, а то не вижу нигде описания 0day от D3M
     
    #45 giloo, 17 May 2019
    Last edited: 17 May 2019
  6. D3N

    D3N Member

    Joined:
    8 Dec 2018
    Messages:
    7
    Likes Received:
    19
    Reputations:
    19
    Надеюсь не слишком поздно, хочу добавить свои 5 копеек. Для прохождения 4 флага не обязательно использование врапперов. Сами по себе проверки FILTER_VALIDATE_URL и FILTER_FLAG_QUERY_REQUIRED требуют лишь наличия символов "//" и "?" в переменной $_POST['url']. Запустив код у себя и поигравшись с путём получаем такой bypass:
    Code:
    url=.://../?/../index.php
     
  7. D3N

    D3N Member

    Joined:
    8 Dec 2018
    Messages:
    7
    Likes Received:
    19
    Reputations:
    19
    Это точно не 0day) Выше расписал.
     
  8. crlf

    crlf Green member

    Joined:
    18 Mar 2016
    Messages:
    683
    Likes Received:
    1,513
    Reputations:
    460
    В рамках таска это был зиродей, так что зачёт :) В контексте пыха, больше на лулз похоже:
    Code:
    var_dump(filter_var('.://xek?kex', FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED));
    //string(11) ".://xek?kex"
    
    var_dump(filter_var('^://xek?kex', FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED));
    //bool(false)
    
    По мнению filter_var ".://" нормальная схема, но file_get_contents так не считает, вернее вообще не считает схемой :D
     
    D3N likes this.
  9. dooble

    dooble Members of Antichat

    Joined:
    30 Dec 2016
    Messages:
    231
    Likes Received:
    601
    Reputations:
    145
    Мне вот такие находки нравятся, возможно потому, что они "хакерские", а не чисто "программерские".
     
    crlf likes this.
  10. crlf

    crlf Green member

    Joined:
    18 Mar 2016
    Messages:
    683
    Likes Received:
    1,513
    Reputations:
    460
    Ещё хочется выделить пару разборов от бета-тестеров задания. Это не финальные решения, а именно процесс мозгового штурма в первые часы разбора задания. Ребята являются одними из топчиков в своём деле, с кем мне довелось пообщаться за последнее время. Думаю многим будет полезно ознакомиться с этими наработками и проследить за ходом мыслей тру хакеров :)

    - Комрад берёт первый флаг ;)
    Code:
    1. что имеем nginx1.10.3,openssh7.4,php7.0.30, ось *nix,reverse ip-ничего
    2. из скриптов сканер нашел только index.php, из параметров скрипт бечеда зафазил только page. папки uploads и misc можно сбрутить или найти в css файлах. возможно подключаются доп файлы page/contact/portfolio из неизвестной диры с неизвестным расширением
    2.1. у index.php нет бекапов,временных файлов итд .ext~,.swp
    2.2. пасхальный zip архив
    3. повтыкал в параметр page (параметр береться из $_REQUEST не только из GET)
     3.1. очень похоже на обычный basename (aaa/contact-200)
     3.2. пустой page выдает страницу home,массив page[] не выводит ничего.
     3.3. data:,x false,allow_url_iclude off или не инклуд.
     3.4. странное поведение скрипта если первые два символа точки или первый символ слеш  .a/contact(false) a.a/contact (false) aa./contact (true) a/contact (true) /contact (false) либо не basename либо в коде шаманство с первыми тремя символами
     3.5. параметр не попадает в exec функции (;id;||id||--v итд), sql injection отсутствует
     3.6. возможно скрытые значения параметра ?page=xxx (стоит пофазить)
    4. подсказки или флаги в статике исключены, чекнул даже exif теги в картинках
    5. сценарий contact
     5.1 параметры email,file, необязательные, параметр message обязательный
     5.2 email=,email[],file[] - ерроров нет,message[] - скрипт не выполняется значит конструкция не такого вида !isset($_REQUEST['message']) остальные параметры никак не фильтруются и не проверяется
     5.3 хуй знает как и откуда админ читает (если вообще читает) репорты поэтому фазить поле message не вижу смысла, единственное что следовало попробовать - запилить снифер, например через параметр <u style="background:imageurl"> в надежде получить из реферера админку если таковая есть
     5.3.1. файлы из multipart/form-data заливаются в uploads в виде md5(filename).ext
     5.3.2. файл грузится только если расширение совпадает с указанным
     5.3.3. name.php.txt такая конструкция проходит проверку, но берется последнее расширение, нет проверки по mime type, .php, .php.txt false, очень похоже на in_array
     5.3.4. md5.txt/.php ошибка доступа md5.txt%00.php 404 (а вдруг)
     5.3.5. возможна эксплуатация в связки с page,нужно еще раз повтыкать
    
    - "Разнос" флага #3 :D
    Code:
    auth.php:
    
    ...
    include('auth_secrets.php'); // подключаем файл с ключами
    // вот тут отступлением, если посмотреть в диркуторию /misc/ то в глаза бросается разница в весе файла
    // 362 байт у auth_secrets.php и 357 байт у auth_secrets.php.old
    // вероятнее всего админ изменил ключи на новые (????)
    
    switch ($login){ // свитчим переменную $login
    case 'admin': // если в ней admin
    $auth_hash = sha1($auth_id.$secret); // присваеваем переменной $auth_hash хеш значений переменной $auth_id и ключа из файла auth_secret
    $local_signature = hash('whirlpool', $id.$secret.$log.$auth_group.$auth_hash); // генерируем local_signature для сверки (??? почему блять тут $id, а выше $auth_id, по логике это должно быть одно и тоже)
    break;
    case 'superuser':
    $auth_hash = sha512($secretx.$$login); // по дефолту нет такой функции, если юзать этого пользователя то код крАшится
    $local_signature = hash('ripemd128', $auth_group.$id.$secretx.$log.$auth_hash);
    break;
    default:
    $auth_hash = md5($secrety.$group); // ну вот тут тоже тупо, почему стилистика разная, тут сначала ключ и потом $gorup, которая так же прилетела через _COOKIE, почему не $auth_group
    if($auth_id < 3 && $auth_group == 1) // (???? НАХУЯ???) если auth_id меньше 3 и auth_group равен 1
    $auth_hash .= md5($secrety.$auth_id); // дописываем в переменную еще один хеш этого же ключа и уже $auth_id, которая опять же может быть хоть чертом лысым
    $local_signature = hash('snefru256', $secrety.$log.$auth_group.$auth_hash.$id); // ну и создается сигнатура из неизвестного (??? скорее всего) нам ключа, и остальных ебливых данных
    }
    
    // ну тут идет проверка, которую можно пройти юзая старые ключи и с логинами admin и hackersucks (ибо на superuser крАшится код), но блять ключи то скорее всего другие, по этому пройти можно только на локалке
    // на таргете фейл
    // детальней разберем
    // in_array($login,['admin','superuser','hackersucks']) - $login должна присутствовать в массиве ['admin','superuser','hackersucks']
    // strlen($user_hash) - strlen должна вернуть больше 0
    // $auth_hash==$user_hash - мы влияем на auth_hash и он должен быть равен user_hash, привести к равенству нам мешает незнание ключей
    // 4>$auth_group - очевидно что auth_group должна быть меньше 4
    // $log=='local'
    // $auth_id<=5 - очевидно что auth_id должна быть меньше или равно 5
    // strcmp($local_signature,$signature)==0 - local_signature и signature (на которую мы влияем) должны быть равны, это мы можем обойти назначив переменную signature массивом
    
    /*
    что я предположил, нужно обойти strcmp что довольно просто, и остается обойти $auth_hash==$user_hash
    вот тут печаль, я предположил что мб $auth_hash = md5($secrety.$group); если $secrety содержит строку, которая конвертируется в md5 как 0eABC то можно просто заслать user_hash=0 и не слать group
    тогда все проверки пройдены и я молодец, но хуй
    */
    if(in_array($login,['admin','superuser','hackersucks'])&&strlen($user_hash)&&$auth_hash==$user_hash&&4>$auth_group&&$log=='local'&&$auth_id<=5&&strcmp($local_signature,$signature)==0)
    
     
  11. Тот_самый_Щуп

    Тот_самый_Щуп Reservists Of Antichat

    Joined:
    23 Mar 2017
    Messages:
    265
    Likes Received:
    174
    Reputations:
    119
    Мне вот любопытно, а много таких было, кому это в глаза бросилось? Или только один я лопух, проморгал?
    Это правильно. Так-же не забываем поиграться с картинкой, меняя яркость\контраст, там тоже флаги могут образовываться (3301)

    Вообще, солидарен с мнением о ребятах, кто "задротствует" сутками над тасками, именно своим упорством, и хакерским характером они в дальнейшем придут к успеху, и захватят мир.
    А так-же в очередной раз хочу поблагодарить авторов тасков: crlf, dooble, l1ght, joelblack
    Потсоны вообще ребята. Отличный креатив, прокачка мозгов, и сопернический дух.
     
    #51 Тот_самый_Щуп, 9 Jun 2019
    Last edited: 9 Jun 2019
  12. crlf

    crlf Green member

    Joined:
    18 Mar 2016
    Messages:
    683
    Likes Received:
    1,513
    Reputations:
    460
    K800, fandor9, alexzir and 3 others like this.