Tesseract
Забудьте про статику: истина доступна только в динамике.
Решение
Подсказка в задании сразу задает направление: "забудь про статику, смотри на динамику".
Первым делом загружаем сэмпл в Detect It Easy. Видим VMProtect, anti-debug. Т.к. это .NET, открываем в DnSpy, понимаем что смылсла в статике нет. Везде сильная обфускация, Control-Flow + Сильная анти-дебаг защита. Поэтому лучше смотреть, что происходит во время работы программы: какие строки появляются в памяти и через какие WinAPI проходят данные.
Ниже разберем решение пошагово, тем же путем, которым обычно идет участник.
Что понадобится
fridaиfrida-toolsна Windows.- PowerShell
- Process Hacker / Process Explorer - посмотреть TCP-соединения.
- WinAPI Monitor - понять, какие WinAPI реально вызываются (SSPI/Schannel, Winsock и т.п.).
Готовые скрипты решения:
- стадия 1 (получение пароля):
stage_1_findpassword.js - стадия 2 (получение флага):
stage_2_decryptmessage.js
Что видно при запуске
После старта видим простое окно:
- поле ввода
- кнопка
Check - подпись
status:
Если вводить случайные строки и нажимать Check, появляется сообщение Invalid Password.
Из этого делаем два практических вывода:
- внутри есть сравнение с правильным паролем;
- правильная строка где-то в процессе все-таки появляется, хотя бы на короткое время.
Стадия 1: достаем пароль динамически
Почему не strings и не статический реверс
Если мы запустим программу, укажем любой пароль и посмотрим какие строки лежат в памяти процесса, то ничего полезного не увидем. Сам верный пароль может лежать в памяти, но не долго.
В задачах такого типа пароль часто:
- вычисляется на лету;
- расшифровывается в память на долю секунды;
- сразу затирается;
- скрывается за виртуализацией/обфускацией.
Поэтому здесь быстрее работать через рантайм.
Идея
Мы не знаем пароль, но знаем строку, которая точно рисуется на экране: Invalid Password.
План такой:
- поймать момент, когда приложение рисует
Invalid Password; - получить указатель на эту строку;
- найти memory range, где лежит этот указатель;
- просканировать соседнюю область памяти на строки-кандидаты;
- выбрать наиболее похожую на пароль.
В этом таске строка рисуется через user32!DrawTextExW, поэтому это удобная точка для хука.
Как автоматизировать клики
Чтобы не нажимать кнопку вручную, скрипт:
- находит
EDITи кнопкуCheck; - шлет
WM_SETTEXTв поле; - шлет
BM_CLICKв кнопку; - повторяет это циклом.
Поиск контролов сделан через EnumWindows и EnumChildWindows.
Что делает stage_1_findpassword.js
По сути в нем четыре шага:
- Минимально отключает антидебаг (
IsDebuggerPresent,CheckRemoteDebuggerPresent). - Автоматически гоняет ввод и нажатие
Check. - Хукает
DrawTextExWи ловит вызов с текстомInvalid Password. - От найденного адреса сканирует память, собирает строки-кандидаты и ранжирует их по простым эвристикам.
в выводе видно что-то такое:
[CAND 1] ... VMP_Is_Watching_Y0u
[+] BEST GUESS: VMP_Is_Watching_Y0u
Эту строку и вводим в GUI как пароль.
Запуск стадии 1
frida -f .\tesseract.exe -l .\stage_1_findpassword.js --runtime=v8
Дальше ждем строку BEST GUESS.
Стадия 2: достаем флаг при TLS и pinning
После ввода верного пароля ничего не происходит, происходит авторизация и все. Запустим proc hacker и видим, что после ввода пароля приложение создает какое-то подключение. Так же это видно через API Monitor
Интуитивный вариант - снять трафик в Wireshark/Burp/mitmproxy - здесь не помогает:
- трафик зашифрован TLS;
- pinning ломает MITM даже с подставленным сертификатом.
Идея
Смотрим не в сеть, а в процесс. Любой TLS-клиент внутри себя в какой-то момент получает уже расшифрованные данные.
В Windows это обычно связка SSPI/Schannel. В API Monitor это видно по вызовам:
InitializeSecurityContextW(handshake);DecryptMessage(расшифровка прикладных данных).
Значит, хукаем DecryptMessage, и после вызова читаем буферы SECBUFFER_DATA. Там лежит plaintext, который приложение уже расшифровало для себя.
Так мы обходим pinning без MITM: просто забираем готовые данные из памяти процесса.
Что делает stage_2_decryptmessage.js
Скрипт:
- при необходимости гасит базовые антидебаг-проверки;
- ждет загрузку
secur32.dll/sspicli.dll; - ставит хук на
DecryptMessage; - на
onLeaveпарситSecBufferDescи связанныеSecBuffer; - читает все буферы типа
SECBUFFER_DATAи печатает расшифрованный текст.
В выводе получаем флаг:
[+] FLAG: caplag{D0uble_H00k_And_Tim3_Warp_Cr4ck}
Запуск стадии 2
frida -f .\tesseract.exe -l .\stage_2_decryptmessage.js --runtime=v8






