Init. commit

This commit is contained in:
Caplag
2026-03-02 21:44:22 +03:00
committed by Ivan Z
commit 9511b38280
38 changed files with 4397 additions and 0 deletions

162
tesseract-reverse/README.md Normal file
View File

@@ -0,0 +1,162 @@
# Tesseract
Забудьте про статику: истина доступна только в динамике.
## Решение
Подсказка в задании сразу задает направление: **"забудь про статику, смотри на динамику"**.
![Интерфейс задания](image.png)
![Подсказка](image-1.png)
Первым делом загружаем сэмпл в **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`
## Что видно при запуске
После старта видим простое окно:
![Окно программы](image-2.png)
- поле ввода
- кнопка `Check`
- подпись `status:`
Если вводить случайные строки и нажимать `Check`, появляется сообщение **`Invalid Password`**.
Из этого делаем два практических вывода:
1. внутри есть сравнение с правильным паролем;
2. правильная строка где-то в процессе все-таки появляется, хотя бы на короткое время.
## Стадия 1: достаем пароль динамически
### Почему не `strings` и не статический реверс
Если мы запустим программу, укажем любой пароль и посмотрим какие строки лежат в памяти процесса, то ничего полезного не увидем. Сам верный пароль может лежать в памяти, но не долго.
В задачах такого типа пароль часто:
- вычисляется на лету;
- расшифровывается в память на долю секунды;
- сразу затирается;
- скрывается за виртуализацией/обфускацией.
Поэтому здесь быстрее работать через рантайм.
### Идея
Мы не знаем пароль, но знаем строку, которая точно рисуется на экране: `Invalid Password`.
План такой:
1. поймать момент, когда приложение рисует `Invalid Password`;
2. получить указатель на эту строку;
3. найти memory range, где лежит этот указатель;
4. просканировать соседнюю область памяти на строки-кандидаты;
5. выбрать наиболее похожую на пароль.
В этом таске строка рисуется через `user32!DrawTextExW`, поэтому это удобная точка для хука.
### Как автоматизировать клики
Чтобы не нажимать кнопку вручную, скрипт:
- находит `EDIT` и кнопку `Check`;
- шлет `WM_SETTEXT` в поле;
- шлет `BM_CLICK` в кнопку;
- повторяет это циклом.
Поиск контролов сделан через `EnumWindows` и `EnumChildWindows`.
### Что делает `stage_1_findpassword.js`
По сути в нем четыре шага:
1. Минимально отключает антидебаг (`IsDebuggerPresent`, `CheckRemoteDebuggerPresent`).
2. Автоматически гоняет ввод и нажатие `Check`.
3. Хукает `DrawTextExW` и ловит вызов с текстом `Invalid Password`.
4. От найденного адреса сканирует память, собирает строки-кандидаты и ранжирует их по простым эвристикам.
![Результат первой стадии](image-3.png)
в выводе видно что-то такое:
```
[CAND 1] ... VMP_Is_Watching_Y0u
[+] BEST GUESS: VMP_Is_Watching_Y0u
```
Эту строку и вводим в GUI как пароль.
### Запуск стадии 1
```powershell
frida -f .\tesseract.exe -l .\stage_1_findpassword.js --runtime=v8
```
Дальше ждем строку `BEST GUESS`.
## Стадия 2: достаем флаг при TLS и pinning
![Сетевое поведение](image-4.png)
После ввода верного пароля ничего не происходит, происходит авторизация и все. Запустим proc hacker и видим, что после ввода пароля приложение создает какое-то подключение. Так же это видно через API Monitor
Интуитивный вариант - снять трафик в Wireshark/Burp/mitmproxy - здесь не помогает:
- трафик зашифрован TLS;
- pinning ломает MITM даже с подставленным сертификатом.
### Идея
Смотрим не в сеть, а в процесс. Любой TLS-клиент внутри себя в какой-то момент получает уже расшифрованные данные.
В Windows это обычно связка SSPI/Schannel. В API Monitor это видно по вызовам:
- `InitializeSecurityContextW` (handshake);
- `DecryptMessage` (расшифровка прикладных данных).
![Следы Schannel в API Monitor](image-5.png)
Значит, хукаем `DecryptMessage`, и после вызова читаем буферы `SECBUFFER_DATA`. Там лежит plaintext, который приложение уже расшифровало для себя.
Так мы обходим pinning без MITM: просто забираем готовые данные из памяти процесса.
### Что делает `stage_2_decryptmessage.js`
Скрипт:
1. при необходимости гасит базовые антидебаг-проверки;
2. ждет загрузку `secur32.dll` / `sspicli.dll`;
3. ставит хук на `DecryptMessage`;
4. на `onLeave` парсит `SecBufferDesc` и связанные `SecBuffer`;
5. читает все буферы типа `SECBUFFER_DATA` и печатает расшифрованный текст.
![Результат второй стадии](image-6.png)
В выводе получаем флаг:
```
[+] FLAG: caplag{D0uble_H00k_And_Tim3_Warp_Cr4ck}
```
### Запуск стадии 2
```powershell
frida -f .\tesseract.exe -l .\stage_2_decryptmessage.js --runtime=v8
```