ОБНОВЛЕНИЕ: На закрытии, состоявшемся 25 января 2023 года, команда организаторов SANS Holiday Hack Challenge объявила победителей, а этот отчет был внесен в список Почетных упоминаний. Слава всем победителям, и надеюсь увидеть некоторых из вас на KringleCon 6, где будут шесть гусей, несущих яйца!
Это мой первый год участия в SANS Holiday Hack Challenge, и это было потрясающе. В серии из 16 испытаний, от легких до сложных, я практиковал анализ подозрительного сетевого трафика и логов PowerShell, написание правил Suricata, выход из Docker-контейнера, поиск утечек ключей для эксплуатации GitLab CI/CD пайплайна и AWS-пользователя, проведение XML External Entity атак и взлом смарт-контракта для покупки невзаимозаменяемого токена (NFT).
Самое лучшее в этом опыте — то, что он познакомил меня с новыми инструментами и технологиями, тем самым расширив мои знания в области кибербезопасности. Здесь я делюсь несколькими яркими моментами решения задач.
Ориентация
Каждый участник получает аватар для навигации в игровом окружении в браузере, расположенном на Северном полюсе:
Во время ориентации вы получаете криптовалютный кошелек, который игра использует для награждения KringleCoins за выполнение заданий, и который вы используете в последнем задании для взлома смарт-контракта. Интересно, что игра отслеживает все транзакции KringleCoin в блокчейне Ethereum, а это значит, что полный отчет о вашем прогрессе хранится и в этом блокчейне.
Переходим к первому кольцу игры.
1. Кольцо Толкина
Поиск Кольца Толкина потребовал от меня напрячь свои навыки анализа логов.
Фишинг через Wireshark
Сначала я использовал Wireshark для анализа предоставленного файла .pcap, который показал, что сервер по адресу adv.epostoday[.]uk скачивает файл Ref_Sept24-2020.zip на компьютер:
Заглянув внутрь ZIP-архива, я обнаружил исполняемый файл Ref_Sept24-2020.scr, который вызвал два срабатывания в ESET Endpoint Security: BAT/Runner.ES и Generik.TAGTBG. Этот malware в конечном итоге привел к запуску вредоносного исполняемого файла в памяти под названием config.dll, который был обнаружен Advanced Memory Scanner от ESET как Win32/Dridex.DD.
Журналы событий Windows
Далее я проанализировал предоставленный файл .evtx, содержащий логи PowerShell, с помощью Event Viewer. Хотя существуют и другие инструменты для анализа логов PowerShell, если злоумышленники знают, как использовать встроенные средства операционной системы (living-off-the-land binaries), чтобы оставаться незамеченными, то и защитники должны хорошо разбираться в нативных инструментах, предоставляемых ОС.
Поскольку логи содержали 10 434 события, я сгруппировал события по дате, а затем использовал функцию поиска, чтобы найти любые события, содержащие символ $. В PowerShell $ используется для создания и обращения к переменным. Я обнаружил атаку, произошедшую 24 декабря 2022 года, когда злоумышленник запустил следующий скрипт:
Похоже, злоумышленник нашел секретный рецепт, заменил секретный ингредиент honey на fish oil, а затем создал новый файл рецепта. Это вызвало событие с ID 4104, которое означает выполнение удаленных команд PowerShell. Поэтому я отфильтровал события по этому ID, что помогло мне быстрее находить дополнительные вредоносные события.
Suricata Regatta
Последнее упражнение для Кольца Толкина заключалось в написании четырех правил Suricata для мониторинга сетевого трафика на предмет заражения Dridex:
alert dns $HOME_NET any -> any any (msg:»Known bad DNS lookup, possible Dridex infection»; dns.query; content:»adv.epostoday.uk»; nocase; sid:1; rev:1;)
alert http 192.185.57.242 any <> any any (msg:»Investigate suspicious connections, possible Dridex infection»; sid:2; rev:1;)
alert tls any any -> any any (msg:»Investigate bad certificates, possible Dridex infection»; tls.cert_subject; content:»CN=heardbellith.Icanwepeh.nagoya»; sid:3; rev:1;)
alert http any any -> any any (msg:»Suspicious JavaScript function, possible Dridex infection»; file_data; content:»let byteCharacters = atob»; sid:4; rev:1;)
По порядку, эти правила отлавливают DNS-запросы к adv.epostoday[.]uk, соединения с IP-адресом 192.185.57[.]242, использование вредоносного сервера heardbellith.Icanwepeh[.]nagoya, идентифицированного по общему имени (CN) в TLS-сертификате, и использование JavaScript atob() функции для декодирования бинарной строки, содержащей base64-закодированные данные на клиенте.
Завершение этих трех испытаний принесло мне Кольцо Толкина:
Переходим ко второму кольцу.
2. Кольцо Эльфов
Наиболее заметными испытаниями для Кольца Эльфов были «Побег из тюрьмы» и «Веселый CI/CD».
Побег из тюрьмы
«Побег из тюрьмы» стал суровым напоминанием о том, что предоставление root-привилегий пользователю в Docker-контейнере — это то же самое, что и предоставление root-привилегий хост-системе. Задача заключалась в том, чтобы выбраться из контейнера. Ну, это легко сделать, когда ты root:
Будучи root-пользователем, я перечислил таблицы разделов устройства, а затем смонтировал файловую систему хоста, получив полный доступ к хосту. Теперь я мог искать ключ, который, как подсказывали внутриигровые подсказки, должен был находиться в домашнем каталоге:
Веселый CI/CD
Хотя это было быстро, «Веселый CI/CD» занял у меня больше всего времени. Сначала нам дали Git-репозиторий для клонирования по HTTP:
Из URL я увидел, что название репозитория было wordpress.flag.net.internal, поэтому я перешел в репозиторий и обнаружил сайт WordPress. Я проверил, работает ли сайт:
Да, сайт работал. Мне было интересно, были ли какие-либо утечки ключей в истории исходного кода. Если да, то я должен был бы иметь возможность отправлять изменения в исходный код. Поэтому я запустил git log:
Из сообщений коммитов видно, что коммит был сделан после добавления ресурсов для исправления «ой». Пора проверить коммит до «ой»:
Отлично, я нашел директорию .ssh с ключами. Давайте скопируем эти ключи и настроим SSH-агент и Git-пользователя, чтобы проверить, смогу ли я выдать себя за владельца этих ключей:
Теперь вернемся в основную ветку и проверим, можем ли мы отправить тривиальное изменение в исходный код (используя nano, я просто добавил пробел в один из файлов):
Таким образом, я выполнил первую часть задания, выдав себя за одного из разработчиков WordPress, но работал ли сайт после моего пуша?
Мой пуш что-то изменил, потому что теперь сайт перенаправлял на порт 8080.
До сих пор я игнорировал часть задания, связанную с CI/CD, которая должна была стать ключом к его завершению. Репозиторий содержит файл .gitlab-ci.yml, который предоставляет конфигурацию для пайплайна GitLab CI/CD. Каждый раз, когда вы отправляете изменения в репозиторий, запускается система CI/CD, и GitLab Runner выполняет скрипты из этого YML-файла. Я подумал, что это почти эквивалентно удаленному выполнению кода на сервере, где установлен GitLab Runner.
Присмотревшись, я увидел rsync скрипт, копирующий все файлы из Git-репозитория в директорию на веб-сервере, где размещался сайт. Сначала я попытался использовать rsync для обращения потока данных, копируя все файлы с веб-сервера в Git-репозиторий, но безуспешно.
После множества проверок гипотез я наконец-то пришел к прорыву: вместо того, чтобы пытаться «исправить» сайт WordPress или запускать вредоносные скрипты через систему сборки, я решил выдать сайт, который утекает информацию с веб-сервера. Внутри index.php (расположенного в корне репозитория) я могу закомментировать код, который загружает сайт WordPress, и запустить PHP-команды, которые сканируют веб-сервер.
Действительно, я могу даже запускать команды оболочки с помощью PHP. Я обнаружил, что passthru() работает легко.
В index.php я использовал // для комментирования двух строк и добавил passthru(‘ls -la /’); в последней строке. Это создает веб-сайт, который выводит список всех файлов в корневой директории веб-сервера:
Затем я отправил это изменение в Git-репозиторий, и система GitLab CI/CD позаботилась об обновлении сайта для меня:
Ага, Кольцо Эльфов должно быть в flag.txt! Я повторил предыдущие шаги, но на этот раз использовал passthru(‘cat /flag.txt’); раскрывая Кольцо Эльфов при следующем запросе к сайту:
Переходим к третьему кольцу.
3. Веб-кольцо
Самым интересным испытанием для меня было «Открыть дверь шахты Бории», хотя «Фонтан Галадриэль» тоже был интересным и содержал загадки.
Открыть дверь шахты Бории
В «Открыть дверь шахты Бории» нам было представлено шесть штырьков или мини-испытаний для обхода валидации ввода или Content Security Policy для соединения входных и выходных труб между штырьками, включая подбор цветов труб. Для большинства штырьков я использовал HTML для написания списка соединяющих букв ‘o’. Вот мое окончательное решение:
Штырек 1
Для Штырька 1 не было никакой валидации, поэтому это было простое дело HTML и встроенного CSS:
<p style="letter-spacing: -4px; margin: 0; padding: 0;">oooooooooooooo</p>
Штырек 2
Штырек 2 имел Content Security Policy, которая запрещала JavaScript, но разрешала встроенный CSS, так что это не было проблемой для моего метода:
<ul style="list-style: none; line-height:0.5; letter-spacing: -4px; margin: 0; padding: 0;"> <li>o</li> <li>o</li> <li>o</li> <li>o</li> <li>o</li> <li>o</li> <li>o</li> <li>o</li> <li>o</li> <li>oooooooooooooo</li> </ul>
Штырек 3
Штырек 3 имел Content Security Policy, которая запрещала CSS, но разрешала встроенный JavaScript, поэтому я использовал JavaScript для изменения стилей:
<script> document.write("" + "<ul id='o'>" + "<li>o</li>" + "<li>oooooooooooooooooooooo</li>" + "<li>o</li><li>o</li><li>o</li><li>o</li>" + "</ul>"); const o = document.getElementById('o'); o.style.color = "blue"; o.style.listStyle = "none"; o.style.lineHeight = "0.5"; o.style.letterSpacing = "-4px"; o.style.margin = "0"; o.style.padding = "0"; </script>
Штырек 4
У Штырька 4 не было Content Security Policy, но у него была функция sanitizeInput на стороне клиента, которая удаляла двойные кавычки, одинарные кавычки, левые угловые скобки и правые угловые скобки. Хитрость здесь заключалась в том, чтобы понять, что эта функция не срабатывала при отправке формы, а при событии onblur. Другими словами, перемещение мыши за пределы поля ввода вызывало событие onblur, очищая любой ввод. Решением было отправить форму, нажав клавишу Enter, при этом позаботившись о том, чтобы не выводить курсор мыши за пределы поля ввода:
<ul style="list-style: none; line-height:0.5; letter-spacing: -4px; margin: 0; padding: 0;"> <li>o</li> <li>o</li> <li>oooooooooooooo</li> <li>o</li> <li>o</li> <li>o</li> <li>o</li> <li>o</li> <li style="color: blue;">oooooooooooooo</li> </ul>
Штырек 5
Штырек 5 имел ту же функцию sanitizeInput и обход, а также Content Security Policy, запрещающую встроенный CSS, но разрешающую встроенный JavaScript:
<script> document.write("" + "<ul id='o'>" + "<li>o</li>" + "<li>o</li>" + "<li class='red'>oooooooooooooo</li>" + "<li><span class='red'>o</span></li>" + "<li><span class='red'>o</span></li>" + "<li><span class='red'>o</span>" + "<span class='blue'> ooooooooooo</span></li>" + "<li><span class='red'>o</span>" + "<span class='blue'> oo</span></li>" + "<li><span class='red'>o</span>" + "<span class='blue'> oo</span></li>" + "<li><span class='red'>o</span>" + "<span class='blue'> oo</span></li>" + "<li>o<span class='blue'> oo</span></li>" + "<li>o<span class='blue'> B</span></li>" + "</ul>"); const o = document.getElementById('o'); o.style.listStyle = "none"; o.style.lineHeight = "0.5"; o.style.letterSpacing = "-4px"; o.style.margin = "0"; o.style.padding = "0"; o.style.fontSize = "xx-large"; const reds = document.getElementsByClassName("red"); for (let red of reds) { red.style.color = "red"; } const blues = document.getElementsByClassName("blue"); for (let blue of blues) { blue.style.color = "blue"; } </script>
Штырек 6
Наконец, Штырек 6 не очищал ввод, но использовал более строгий Content Security Policy, запрещающий как встроенный CSS, так и JavaScript. Моим решением было использовать устаревший HTML для получения необходимых стилей и использовать таблицу вместо списка:
<table border="0" frame="void" rules="none" cellpadding="0" cellspacing="0" width="100%"> <tr bgcolor="#00FF00"> <td><font color="#00FF00" size="7">o</font></td> <td><font color="#00FF00" size="7">o</font></td> <td><font color="#00FF00" size="7">o</font></td> <td><font color="#00FF00" size="7">o</font></td> </tr> <tr bgcolor="red"> <td><font color="red" size="5">o</font></td> <td><font color="red" size="5">o</font></td> <td><font color="red" size="5">o</font></td> <td><font color="red" size="5">o</font></td> </tr> <tr bgcolor="blue"> <td><font color="blue" size="9">o</font></td> <td><font color="blue" size="9">o</font></td> <td><font color="blue" size="9">o</font></td> <td bgcolor="red"><font color="red" size="9">o</font></td> </tr> <tr bgcolor="blue"> <td><font color="blue" size="9">o</font></td> <td><font color="blue" size="9">o</font></td> <td><font color="blue" size="9">o</font></td> <td><font color="blue" size="9">o</font></td> </tr> </table>
Фонтан Галадриэль
Фонтан Галадриэль был возможностью попрактиковаться в XML External Entity (XXE) атаках. Понять, как определить пользовательскую XML-сущность, определить сущность, которая запрашивает файл с сервера, и добавить эту сущность в качестве полезной нагрузки в HTTP-запрос, было несложно. Самым трудным было разгадать внутриигровые загадки, чтобы определить путь к файлам, которые сервер будет утекать. Вот прорывной запрос, раскрывающий местоположение золотого кольца:
Я бы дал два урока, извлеченных из этого испытания. Во-первых, используйте расширение Content Type Converter в Burp для преобразования JSON-полезных нагрузок в XML. Во-вторых, попробуйте поместить XXE-полезную нагрузку в разные теги — мне потребовалось много времени, чтобы понять, что все, что мне нужно было сделать, это поместить &xxe; полезную нагрузку в тег reqType вместо тега imgDrop.
Переходим к четвертому кольцу.
4. Облачное кольцо
Игра за Облачное кольцо была начальным погружением в интерфейс командной строки Amazon Web Services (AWS).
Главным событием этого набора испытаний стало использование trufflehog для поиска учетных данных AWS в Git-репозитории, а затем их эксплуатации для аутентификации в качестве пользователя AWS. Злоумышленник, оказавшийся в такой ситуации, может использовать команды aws iam для запроса политик, применяемых к пользователю, и, следовательно, для определения того, какие облачные ресурсы могут быть доступны и использованы злонамеренно.
Переходим к пятому кольцу.
5. Горящее кольцо огня
Наиболее поучительной частью этого набора испытаний было изучение деревьев Меркла для эксплуатации смарт-контракта и попадания в список предварительной продажи для покупки невзаимозаменяемого токена (NFT). Здесь задача заключалась в том, чтобы обнаружить доказательственные значения, которые вместе с моим адресом кошелька и корневым значением дерева Меркла подтверждали бы мое включение в список предварительной продажи.
После нескольких безуспешных попыток предоставить доказательственные значения я понял, что никогда не смогу найти доказательственные значения для предоставленного корневого значения, поскольку не было способа узнать все листовые значения, использованные для его вычисления. Мне нужно было изменить корневое значение, чтобы я мог предоставить действительное дерево Меркла.
Используя инструмент профессора QPetabyte, я создал дерево Меркла из двух листьев, состоящих из моего адреса кошелька и адреса смарт-контракта BSRS_nft, который я нашел с помощью внутриигрового обозревателя блокчейна во втором блоке блокчейна Ethereum игры. Инструмент сгенерировал корневое значение этого дерева и доказательственное значение для моего адреса кошелька. Затем я использовал Burp, чтобы перехватить запрос к серверу и изменил значение корневого узла по умолчанию, чтобы я мог отправить действительное дерево Меркла. Вот мой NFT sporc, купленный по фиксированной цене 100 KringleCoins:
Действительно уродливый экземпляр.
Финал
Большое спасибо организаторам SANS Holiday Hack Challenge за то, что они заставили мой мозг работать по-новому и помогли углубить мои знания в области кибербезопасности. Я не только с нетерпением жду вызова в следующем году, но и собираюсь попробовать издания этого вызова 2020 и 2021 годов. И если вы раньше не участвовали в этом испытании, я надеюсь, что эти основные моменты вызвали у вас интерес.
