Init. commit
This commit is contained in:
162
tesseract-reverse/README.md
Normal file
162
tesseract-reverse/README.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 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. внутри есть сравнение с правильным паролем;
|
||||
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. От найденного адреса сканирует память, собирает строки-кандидаты и ранжирует их по простым эвристикам.
|
||||
|
||||

|
||||
|
||||
в выводе видно что-то такое:
|
||||
|
||||
```
|
||||
[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
|
||||
|
||||

|
||||
|
||||
После ввода верного пароля ничего не происходит, происходит авторизация и все. Запустим 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`
|
||||
|
||||
Скрипт:
|
||||
|
||||
1. при необходимости гасит базовые антидебаг-проверки;
|
||||
2. ждет загрузку `secur32.dll` / `sspicli.dll`;
|
||||
3. ставит хук на `DecryptMessage`;
|
||||
4. на `onLeave` парсит `SecBufferDesc` и связанные `SecBuffer`;
|
||||
5. читает все буферы типа `SECBUFFER_DATA` и печатает расшифрованный текст.
|
||||
|
||||

|
||||
|
||||
В выводе получаем флаг:
|
||||
|
||||
```
|
||||
[+] FLAG: caplag{D0uble_H00k_And_Tim3_Warp_Cr4ck}
|
||||
```
|
||||
|
||||
### Запуск стадии 2
|
||||
|
||||
```powershell
|
||||
frida -f .\tesseract.exe -l .\stage_2_decryptmessage.js --runtime=v8
|
||||
```
|
||||
Reference in New Issue
Block a user