D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Недавно со мной связался коллега по поводу ошибки , которую я обнаружил пару лет назад (CVE-2021-27198). Уязвимость заключалась в проблеме загрузки произвольных файлов без аутентификации в версиях 11.0–11.0b программного обеспечения Visualware MyConnection Server. В то время я фактически не доказал удаленное выполнение кода, хотя и оценивал эту ошибку как критическую. Поэтому, когда мой коллега спросил меня, как я это эксплуатировал, я почувствовал, что должен показать, что это возможно. Эта попытка оказалась одновременно сложной и поучительной, поэтому я решил поделиться своим опытом.
Программное обеспечение MyConnection Server написано на языке Java и предназначено для бесперебойной работы на различных платформах. В этом случае запись в произвольный файл была привилегированной, что давало файловому серверу повышенные разрешения: «SYSTEM» в Windows и «root» в Linux. При привилегированной записи файла добиться выполнения кода зачастую несложно. В этом посте Дойенсек описаны наиболее распространенные подходы, которые можно условно разделить на две группы: специфичные для веб-приложений и специфичные для операционной системы. Методы, специфичные для веб-приложения, включают поиск способов инициировать выполнение внутри процесса веб-сервера. Примеры включают загрузку файлов конфигурации веб-платформы или исходного кода веб-приложения. С другой стороны, методы, специфичные для операционной системы, включают поиск триггеров выполнения, контролируемых самой операционной системой, таких как службы, запланированные задачи, задания cron и т.д.
К сожалению, в случае этой конкретной ошибки я не могу напрямую атаковать сам веб-сервер, поскольку это чистая реализация Java, а не использование инфраструктуры веб-сервера, такой как Apache или Nginx. В результате наше внимание в первую очередь сместится в сторону изучения возможностей конкретных операционных систем или более инновационных подходов.
Windows RCE
Помня об этом, я начал исследовать возможные методы достижения выполнения кода исключительно посредством записи файла. В среде Windows моя обычная стратегия включала бы нацеливание на уязвимые приложения, подверженные традиционному перехвату DLL или фантомному перехвату DLL , используя привилегии, предоставляемые такой записью.
Я открыл инструмент Sysinternals Procmon и начал искать результаты «ИМЯ НЕ НАЙДЕНО» или «ПУТЬ НЕ НАЙДЕН» для CreateFile. Обычно я ищу экземпляры исполняемых файлов или DLL. Просматривая результаты, я заметил, что каждую минуту Java-процесс MCS пытается открыть несколько JAR-файлов «rtaplugin», некоторые из которых не существуют.
Судя по полученным данным, сервер динамически загружает эти JAR-файлы с диска в Java-процесс. В этом случае я смогу добиться выполнения произвольного кода, просто поместив собственный JAR-файл в определенное место файловой системы. Я решил открыть JAR MCS в JD-GUI для расследования.
Когда я искал «rtaplugin», я нашел класс с именем RTAPlugin, у которого есть функция, которая загружает файл с диска, создает собственный ClassLoader, загружает класс из файла, а затем создает новый экземпляр класса. Это именно то, что мне нужно!
Чтобы подтвердить выполнение, я создал простой POC, который запускает Calc.exe при создании экземпляра класса.
Затем я просто вручную скопировал JAR в соответствующий каталог, чтобы проверить, загружен ли он. Я был очень рад видеть, что это сработало! Теперь мне просто нужно было протестировать его, используя уязвимость загрузки файлов.
Я загрузил Burp и вставил JAR-файл в тело запроса на загрузку файла. К сожалению, когда я отправил его, я не увидел Calc.exe в списке процессов.
Когда я открыл файл журнала для сервера MyConnection, я обнаружил следующее исключение при попытке разархивировать JAR.
Я взял исходную полезную нагрузку JAR и сравнил ее с той, которая была загружена в каталог rtaplugin. Я обнаружил, что некоторые байты были повреждены. Я снова открыл JD-GUI, чтобы поближе рассмотреть код, выполняющий запись файла.
Что не было сразу очевидно (по крайней мере для меня), так это то, что при вызовах String.valueOf и String.getBytes происходило подразумеваемое кодирование/декодирование. В результате некоторые диапазоны байтов были повреждены. В Windows я обнаружил, что байты между 0x80 и 0x9f заменялись другими значениями. Это означало, что мне пришлось немного повозиться, чтобы заставить полезную нагрузку работать.
Немного погуглив, я нашел статью Юдая Фудзивары, в которой возникла аналогичная проблема с кодированием. В статье предоставлен код для создания zip-файла, который содержит только байты в диапазоне ASCII: 0x0 – 0x7f. Сценарий в первую очередь фокусировался на двух структурах протокола zip, которые должны были быть свободны от байтов, отличных от ascii, CRC для заархивированных файлов и полей любой длины.
Скрипт подбирает допустимое значение ASCII для поля CRC, итеративно изменяя внутренний файл. В испытании CTF zip-архив содержал сценарий ASCII, тогда как JAR содержал файл двоичного класса. Я обновил алгоритм, чтобы выполнить аналогичную модификацию исходного кода Java, а затем перекомпилировать файл класса Java на каждой итерации, чтобы обновить CRC.
Java: Скопировать в буфер обмена
После создания полезной нагрузки ascii-zip я загрузил ее, используя уязвимую конечную точку. Как и ожидалось, файл был загружен и выполнен как SYSTEM на целевом сервере. Чтобы избежать необходимости перекомпилировать JAR-файл для выполнения различных команд, я создал JAR-файл, который будет выполнять сценарий в известном месте. Затем я мог бы отдельно загрузить этот сценарий с новой командой всякий раз, когда мне нужно было выполнить команду. Я добавил директиву setExecutable, чтобы убедиться, что она работает и в Linux.
Linux RCE
При работе с операционными системами на базе Linux одной из основных проблем при использовании уязвимостей записи произвольных файлов является обеспечение правильной настройки прав доступа к файлам. Даже если файл будет выполнен, он не будет работать, если не помечен как исполняемый. Чтобы преодолеть это препятствие, я нацеливаюсь на файлы, у которых уже установлен бит выполнения.
Как я и ожидал, демонстрация эксплуатации уязвимостей в Linux оказалась довольно простой: нужно просто перезаписать сценарии в каталогах /etc/cron.*. В дистрибутивах Red Hat можно добиться относительно своевременного выполнения, перезаписав сценарий /etc/cron.hourly/0anacron. Недостаток таргетинга на задания cron заключается в том, что нет никакой гарантии существования конкретных сценариев, а их перезапись может привести к нестабильности системы.
Предполагая, что исходящее сетевое подключение не заблокировано, простая обратная оболочка, вероятно, является самой простой полезной нагрузкой для задания cron. Если это не сработает, задание cron можно изменить, чтобы оно содержало копию немодифицированного JAR-файла rtaplugin в кодировке Base64, которая затем копируется в правильный каталог. Тот же метод, что описан выше, можно затем использовать для повторного выполнения кода, загружая разные версии сценария в /tmp/b.bat.
Нет лицензии – нет работы!
После всех усилий, затраченных на разработку эксплойта rtaplugin, я был разочарован, обнаружив, что rtaplugins (и связанный с ним поток, который периодически их загружает) активны только тогда, когда веб-сервер имеет действующую лицензию. Я не знал об этом, поскольку мой тестовый экземпляр все еще находился на стадии пробного использования. Я решил еще раз взглянуть и посмотреть, можно ли снова использовать нашу уязвимость привилегированной записи файлов (CVE-2021-27198), но на этот раз, чтобы обмануть сервер, заставив его думать, что он лицензирован. Здесь мы надеемся найти файл или запись в базе данных, которые можно изменить с помощью нашего эксплойта для загрузки файлов, чтобы обойти лицензирование. Я хочу обязательно пояснить здесь: ничто из того, что я здесь опишу, не может быть использовано для нарушения или взлома лицензии на это программное обеспечение в полностью исправленной системе. Цель состоит в том, чтобы использовать наш уже привилегированный доступ к файловой системе, чтобы обойти любые проверки лицензии.
Перейдя на домашнюю страницу веб-приложения, вы увидите меню слева, содержащее ссылку «Лицензирование». Можно с уверенностью предположить, что это подходящее место для поиска.
К сожалению, когда вы нажимаете на нее, вам открывается страница входа в систему. Если вам повезло и пароль администратора не был изменен, следующая страница, с которой вы столкнетесь, показана ниже.
Если вы зашли так далеко или случайно угадали некоторые учетные данные, у вас будет достаточно разрешений для доступа к конечной точке лицензирования сервера. О формате ожидаемого ключа особо нечего сказать, кроме намека на то, что он начинается с MCS.
Я снова открыл JAR и нашел код, отвечающий за активацию лицензии. Обработчик выполняет ряд проверок и преобразований перед отправкой веб-запроса в домен визуального ПО для проверки лицензии.
Если это не удается (в виде исключения), сервер пытается проверить лицензию, отправляя специально созданный DNS-запрос.
Используя возможность привилегированной записи файлов CVE-2021-27198, в моем распоряжении есть несколько способов обойти лицензирование без необходимости прямой расшифровки лицензионного ключа. Поскольку программное обеспечение инициирует сетевой запрос для проверки введенного ключа, его можно перенаправить на сервер под моим контролем для проверки подлинности предоставленного ключа.
Самый простой способ добиться этого — изменить файл хостов, файл, который содержит сопоставления IP-адресов с именами хостов и обычно является первым местоположением, которое операционная система проверяет при разрешении IP-адреса имени хоста. В системах Windows вы можете найти этот файл по адресу C:\Windows\System32\drivers\etc\hosts, а в системах *nix он находится по адресу /etc/hosts. Чтобы манипулировать процессом проверки лицензии, я могу просто вставить новую запись в файл хостов, направив домен сервера лицензий на IP-адрес сервера, находящегося под моим контролем.
Альтернативный подход, специфичный для систем *nix, включает изменение файла /etc/resolv.conf, в котором указан IP-адрес DNS-сервера, используемый для разрешения домена. Изменив адрес DNS-сервера на сервер, которым я управляю, я могу гарантировать, что любые DNS-запросы к серверу лицензий будут разрешаться на IP-адрес моего поддельного сервера лицензий.
ВНИМАНИЕ: Следует отметить, что перезапись файла /etc/hosts или /etc/resolv.conf может вызвать нестабильность системы, если ожидается, что определенные конфигурации будут правильно разрешать пользовательские записи DNS или преобразователи.
Мой следующий шаг — отследить функцию активации до конечной веб-точки, которая может ее активировать. К сожалению, похоже, что ключ, введенный в веб-форму, имеет не тот формат, который отправляется на сервер лицензий. После выполнения пары проверок на основе строк вызывается функция проверки предоставленного лицензионного ключа. Если вы внимательно посмотрите на эту функцию, она может показаться вам знакомой. Похоже, он реализует созданную вручную версию RSA.
С точки зрения безопасности, обоснование необходимости использования зашифрованного лицензионного ключа в качестве входных данных в веб-приложение не имеет смысла. Прежде всего, поскольку незашифрованный лицензионный ключ впоследствии передается по сети в незашифрованном виде, его можно легко перехватить с помощью такого инструмента, как Wireshark. Во-вторых, действительный лицензионный ключ проверяется на сервере поставщика, поэтому определить, как генерируется лицензионный ключ, непрактично. Однако, к сожалению для меня, это создало небольшое препятствие на пути к функции сетевой активации, подробно описанной в предыдущем разделе.
Проницательный читатель, возможно, уже заметил интересную деталь о параметрах RSA. Этот открытый ключ выглядит ужасно маленьким. Чуть больше 256 бит. Похоже, наша задача CTF продолжается…
Для тех самозванцев из сообщества информационной безопасности, которые утверждают, что игра в CTF не дает реального опыта, я могу с уверенностью сказать, что вы никогда не искали настоящие ошибки. Слишком часто я сталкиваюсь с цепочками эксплойтов, которые выглядят так, как будто кто-то аккуратно настроил каждый примитив, чтобы я мог его обнаружить. Лично я не трачу много времени на крипто-задачи, играя в CTF, в основном потому, что я недостаточно умен. К счастью для меня, этот пример хорошо вписывается в первую категорию. Имея такой маленький открытый ключ, я смогу найти описание CTF, которое поможет мне разбить его на два простых числа. Спустя несколько поисков я нашел статью Денниса Юричева, в которой демонстрируется использование инструмента под названием CADO-NFS для этой цели. Примерно через 5 минут мне был представлен ответ.
Имея в руках два простых числа, я попытался восстановить закрытый ключ, используя сценарий, представленный в сообщении Юричева. К сожалению, криптографические библиотеки RSA в Python не очень хорошо работали с предоставленными простыми числами. А именно потому, что размер блока округлился до 257 бит и он жаловался на усечение. Я также обнаружил, что специальная реализация RSA сервера не поддерживает заполнение. Чтобы обойти эти проблемы, я написал код для выполнения вычислений вручную, а не с использованием криптобиблиотеки.
Java: Скопировать в буфер обмена
Я использовал свой сценарий для создания зашифрованного большого двоичного объекта с помощью производного закрытого ключа RSA и отправил веб-запрос с готовым лицензионным ключом. Имейте в виду, поскольку я уже перезаписал файл хостов, мне нужно только пройти проверку расшифровки, так как ключ не будет передан на сервер поставщика для проверки. К моей радости, это работает!
Заключение
Мой путь к разработке работающего эксплойта для CVE-2021-27198 наконец-то подходит к концу. То, что началось как простое упражнение, превратилось в настоящее приключение. Кому интересно, я выложил здесь свой эксплойт - https://github.com/rwincey/CVE-2021-27198.
Программное обеспечение MyConnection Server написано на языке Java и предназначено для бесперебойной работы на различных платформах. В этом случае запись в произвольный файл была привилегированной, что давало файловому серверу повышенные разрешения: «SYSTEM» в Windows и «root» в Linux. При привилегированной записи файла добиться выполнения кода зачастую несложно. В этом посте Дойенсек описаны наиболее распространенные подходы, которые можно условно разделить на две группы: специфичные для веб-приложений и специфичные для операционной системы. Методы, специфичные для веб-приложения, включают поиск способов инициировать выполнение внутри процесса веб-сервера. Примеры включают загрузку файлов конфигурации веб-платформы или исходного кода веб-приложения. С другой стороны, методы, специфичные для операционной системы, включают поиск триггеров выполнения, контролируемых самой операционной системой, таких как службы, запланированные задачи, задания cron и т.д.
К сожалению, в случае этой конкретной ошибки я не могу напрямую атаковать сам веб-сервер, поскольку это чистая реализация Java, а не использование инфраструктуры веб-сервера, такой как Apache или Nginx. В результате наше внимание в первую очередь сместится в сторону изучения возможностей конкретных операционных систем или более инновационных подходов.
Windows RCE
Помня об этом, я начал исследовать возможные методы достижения выполнения кода исключительно посредством записи файла. В среде Windows моя обычная стратегия включала бы нацеливание на уязвимые приложения, подверженные традиционному перехвату DLL или фантомному перехвату DLL , используя привилегии, предоставляемые такой записью.
Я открыл инструмент Sysinternals Procmon и начал искать результаты «ИМЯ НЕ НАЙДЕНО» или «ПУТЬ НЕ НАЙДЕН» для CreateFile. Обычно я ищу экземпляры исполняемых файлов или DLL. Просматривая результаты, я заметил, что каждую минуту Java-процесс MCS пытается открыть несколько JAR-файлов «rtaplugin», некоторые из которых не существуют.
Судя по полученным данным, сервер динамически загружает эти JAR-файлы с диска в Java-процесс. В этом случае я смогу добиться выполнения произвольного кода, просто поместив собственный JAR-файл в определенное место файловой системы. Я решил открыть JAR MCS в JD-GUI для расследования.
Когда я искал «rtaplugin», я нашел класс с именем RTAPlugin, у которого есть функция, которая загружает файл с диска, создает собственный ClassLoader, загружает класс из файла, а затем создает новый экземпляр класса. Это именно то, что мне нужно!
Чтобы подтвердить выполнение, я создал простой POC, который запускает Calc.exe при создании экземпляра класса.
Затем я просто вручную скопировал JAR в соответствующий каталог, чтобы проверить, загружен ли он. Я был очень рад видеть, что это сработало! Теперь мне просто нужно было протестировать его, используя уязвимость загрузки файлов.
Я загрузил Burp и вставил JAR-файл в тело запроса на загрузку файла. К сожалению, когда я отправил его, я не увидел Calc.exe в списке процессов.
Когда я открыл файл журнала для сервера MyConnection, я обнаружил следующее исключение при попытке разархивировать JAR.
Я взял исходную полезную нагрузку JAR и сравнил ее с той, которая была загружена в каталог rtaplugin. Я обнаружил, что некоторые байты были повреждены. Я снова открыл JD-GUI, чтобы поближе рассмотреть код, выполняющий запись файла.
Что не было сразу очевидно (по крайней мере для меня), так это то, что при вызовах String.valueOf и String.getBytes происходило подразумеваемое кодирование/декодирование. В результате некоторые диапазоны байтов были повреждены. В Windows я обнаружил, что байты между 0x80 и 0x9f заменялись другими значениями. Это означало, что мне пришлось немного повозиться, чтобы заставить полезную нагрузку работать.
Немного погуглив, я нашел статью Юдая Фудзивары, в которой возникла аналогичная проблема с кодированием. В статье предоставлен код для создания zip-файла, который содержит только байты в диапазоне ASCII: 0x0 – 0x7f. Сценарий в первую очередь фокусировался на двух структурах протокола zip, которые должны были быть свободны от байтов, отличных от ascii, CRC для заархивированных файлов и полей любой длины.
Скрипт подбирает допустимое значение ASCII для поля CRC, итеративно изменяя внутренний файл. В испытании CTF zip-архив содержал сценарий ASCII, тогда как JAR содержал файл двоичного класса. Я обновил алгоритм, чтобы выполнить аналогичную модификацию исходного кода Java, а затем перекомпилировать файл класса Java на каждой итерации, чтобы обновить CRC.
Java: Скопировать в буфер обмена
Код:
while True:
cmd = ['/opt/jdk1.7.0_80/bin/javac','-cp','/opt/mcs/java.jar', '/opt/mcs/error.java']
output = subprocess.check_output(cmd)
class_file_contents = ''
with open("/opt/mcs/error.class", "rb") as f:
class_file_contents = f.read()
class_file_len = len(class_file_contents)
if class_file_len > 0:
crc32 = binascii.crc32(class_file_contents)
logger.info("CRC: " + hex(crc32))
if all([(crc32 >> i) & 0xff < 0x80 for i in range(0, 32, 8)]):
if all([(class_file_len >> i) & 0xff < 0x80 for i in range(0, 32, 8)]):
break
else:
new_needle = needle + random.choice(string.ascii_letters)*0x2
else:
new_needle = needle + random.choice(string.ascii_letters)
# Add more data to the src
with open("/opt/mcs/error.java", "r") as f:
y = f.read()
new_data= y.replace(needle, new_needle)
with open("/opt/mcs/error.java", "w") as f:
f.write(new_data)
# Update needle
needle = new_needle
После создания полезной нагрузки ascii-zip я загрузил ее, используя уязвимую конечную точку. Как и ожидалось, файл был загружен и выполнен как SYSTEM на целевом сервере. Чтобы избежать необходимости перекомпилировать JAR-файл для выполнения различных команд, я создал JAR-файл, который будет выполнять сценарий в известном месте. Затем я мог бы отдельно загрузить этот сценарий с новой командой всякий раз, когда мне нужно было выполнить команду. Я добавил директиву setExecutable, чтобы убедиться, что она работает и в Linux.
Linux RCE
При работе с операционными системами на базе Linux одной из основных проблем при использовании уязвимостей записи произвольных файлов является обеспечение правильной настройки прав доступа к файлам. Даже если файл будет выполнен, он не будет работать, если не помечен как исполняемый. Чтобы преодолеть это препятствие, я нацеливаюсь на файлы, у которых уже установлен бит выполнения.
Как я и ожидал, демонстрация эксплуатации уязвимостей в Linux оказалась довольно простой: нужно просто перезаписать сценарии в каталогах /etc/cron.*. В дистрибутивах Red Hat можно добиться относительно своевременного выполнения, перезаписав сценарий /etc/cron.hourly/0anacron. Недостаток таргетинга на задания cron заключается в том, что нет никакой гарантии существования конкретных сценариев, а их перезапись может привести к нестабильности системы.
Предполагая, что исходящее сетевое подключение не заблокировано, простая обратная оболочка, вероятно, является самой простой полезной нагрузкой для задания cron. Если это не сработает, задание cron можно изменить, чтобы оно содержало копию немодифицированного JAR-файла rtaplugin в кодировке Base64, которая затем копируется в правильный каталог. Тот же метод, что описан выше, можно затем использовать для повторного выполнения кода, загружая разные версии сценария в /tmp/b.bat.
Нет лицензии – нет работы!
После всех усилий, затраченных на разработку эксплойта rtaplugin, я был разочарован, обнаружив, что rtaplugins (и связанный с ним поток, который периодически их загружает) активны только тогда, когда веб-сервер имеет действующую лицензию. Я не знал об этом, поскольку мой тестовый экземпляр все еще находился на стадии пробного использования. Я решил еще раз взглянуть и посмотреть, можно ли снова использовать нашу уязвимость привилегированной записи файлов (CVE-2021-27198), но на этот раз, чтобы обмануть сервер, заставив его думать, что он лицензирован. Здесь мы надеемся найти файл или запись в базе данных, которые можно изменить с помощью нашего эксплойта для загрузки файлов, чтобы обойти лицензирование. Я хочу обязательно пояснить здесь: ничто из того, что я здесь опишу, не может быть использовано для нарушения или взлома лицензии на это программное обеспечение в полностью исправленной системе. Цель состоит в том, чтобы использовать наш уже привилегированный доступ к файловой системе, чтобы обойти любые проверки лицензии.
Перейдя на домашнюю страницу веб-приложения, вы увидите меню слева, содержащее ссылку «Лицензирование». Можно с уверенностью предположить, что это подходящее место для поиска.
К сожалению, когда вы нажимаете на нее, вам открывается страница входа в систему. Если вам повезло и пароль администратора не был изменен, следующая страница, с которой вы столкнетесь, показана ниже.
Если вы зашли так далеко или случайно угадали некоторые учетные данные, у вас будет достаточно разрешений для доступа к конечной точке лицензирования сервера. О формате ожидаемого ключа особо нечего сказать, кроме намека на то, что он начинается с MCS.
Я снова открыл JAR и нашел код, отвечающий за активацию лицензии. Обработчик выполняет ряд проверок и преобразований перед отправкой веб-запроса в домен визуального ПО для проверки лицензии.
Если это не удается (в виде исключения), сервер пытается проверить лицензию, отправляя специально созданный DNS-запрос.
Используя возможность привилегированной записи файлов CVE-2021-27198, в моем распоряжении есть несколько способов обойти лицензирование без необходимости прямой расшифровки лицензионного ключа. Поскольку программное обеспечение инициирует сетевой запрос для проверки введенного ключа, его можно перенаправить на сервер под моим контролем для проверки подлинности предоставленного ключа.
Самый простой способ добиться этого — изменить файл хостов, файл, который содержит сопоставления IP-адресов с именами хостов и обычно является первым местоположением, которое операционная система проверяет при разрешении IP-адреса имени хоста. В системах Windows вы можете найти этот файл по адресу C:\Windows\System32\drivers\etc\hosts, а в системах *nix он находится по адресу /etc/hosts. Чтобы манипулировать процессом проверки лицензии, я могу просто вставить новую запись в файл хостов, направив домен сервера лицензий на IP-адрес сервера, находящегося под моим контролем.
Альтернативный подход, специфичный для систем *nix, включает изменение файла /etc/resolv.conf, в котором указан IP-адрес DNS-сервера, используемый для разрешения домена. Изменив адрес DNS-сервера на сервер, которым я управляю, я могу гарантировать, что любые DNS-запросы к серверу лицензий будут разрешаться на IP-адрес моего поддельного сервера лицензий.
ВНИМАНИЕ: Следует отметить, что перезапись файла /etc/hosts или /etc/resolv.conf может вызвать нестабильность системы, если ожидается, что определенные конфигурации будут правильно разрешать пользовательские записи DNS или преобразователи.
Мой следующий шаг — отследить функцию активации до конечной веб-точки, которая может ее активировать. К сожалению, похоже, что ключ, введенный в веб-форму, имеет не тот формат, который отправляется на сервер лицензий. После выполнения пары проверок на основе строк вызывается функция проверки предоставленного лицензионного ключа. Если вы внимательно посмотрите на эту функцию, она может показаться вам знакомой. Похоже, он реализует созданную вручную версию RSA.
С точки зрения безопасности, обоснование необходимости использования зашифрованного лицензионного ключа в качестве входных данных в веб-приложение не имеет смысла. Прежде всего, поскольку незашифрованный лицензионный ключ впоследствии передается по сети в незашифрованном виде, его можно легко перехватить с помощью такого инструмента, как Wireshark. Во-вторых, действительный лицензионный ключ проверяется на сервере поставщика, поэтому определить, как генерируется лицензионный ключ, непрактично. Однако, к сожалению для меня, это создало небольшое препятствие на пути к функции сетевой активации, подробно описанной в предыдущем разделе.
Проницательный читатель, возможно, уже заметил интересную деталь о параметрах RSA. Этот открытый ключ выглядит ужасно маленьким. Чуть больше 256 бит. Похоже, наша задача CTF продолжается…
Для тех самозванцев из сообщества информационной безопасности, которые утверждают, что игра в CTF не дает реального опыта, я могу с уверенностью сказать, что вы никогда не искали настоящие ошибки. Слишком часто я сталкиваюсь с цепочками эксплойтов, которые выглядят так, как будто кто-то аккуратно настроил каждый примитив, чтобы я мог его обнаружить. Лично я не трачу много времени на крипто-задачи, играя в CTF, в основном потому, что я недостаточно умен. К счастью для меня, этот пример хорошо вписывается в первую категорию. Имея такой маленький открытый ключ, я смогу найти описание CTF, которое поможет мне разбить его на два простых числа. Спустя несколько поисков я нашел статью Денниса Юричева, в которой демонстрируется использование инструмента под названием CADO-NFS для этой цели. Примерно через 5 минут мне был представлен ответ.
Имея в руках два простых числа, я попытался восстановить закрытый ключ, используя сценарий, представленный в сообщении Юричева. К сожалению, криптографические библиотеки RSA в Python не очень хорошо работали с предоставленными простыми числами. А именно потому, что размер блока округлился до 257 бит и он жаловался на усечение. Я также обнаружил, что специальная реализация RSA сервера не поддерживает заполнение. Чтобы обойти эти проблемы, я написал код для выполнения вычислений вручную, а не с использованием криптобиблиотеки.
Java: Скопировать в буфер обмена
Код:
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import inverse
def encrypt_with_private_key_raw(data):
p = 624106295606602100995951586143562696483
q = 264089186086669371634156709929132346711
e = 17
n = p * q
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
# Convert data to an integer and perform raw RSA encryption
m = bytes_to_long(data)
ciphertext = pow(m, d, n)
return ciphertext
Я использовал свой сценарий для создания зашифрованного большого двоичного объекта с помощью производного закрытого ключа RSA и отправил веб-запрос с готовым лицензионным ключом. Имейте в виду, поскольку я уже перезаписал файл хостов, мне нужно только пройти проверку расшифровки, так как ключ не будет передан на сервер поставщика для проверки. К моей радости, это работает!
Заключение
Мой путь к разработке работающего эксплойта для CVE-2021-27198 наконец-то подходит к концу. То, что началось как простое упражнение, превратилось в настоящее приключение. Кому интересно, я выложил здесь свой эксплойт - https://github.com/rwincey/CVE-2021-27198.