HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > БЕЗОПАСНОСТЬ И УЯЗВИМОСТИ > Этичный хакинг или пентестинг > Задания/Квесты/CTF/Конкурсы
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 27.08.2024, 22:27
ROP
Новичок
Регистрация: 27.08.2019
Сообщений: 0
С нами: 3533622

Репутация: 0
По умолчанию

Хотели порешать эти ваши реверс-инжиниринги на CTF? Да ещё и на C++ с Windows Forms? К тому же чтобы он был не сложным и в райтапе были объяснения? Тогда вам сюда

Ссылка на задание (файлы): нажми на меня

В архиве будет лежать
Код:
cross_cr.exe
. Флаг должен быть формата
Код:
CODEBY{FLAG}
.

План статьи
  1. Статический анализ файла
    1. Detect It Easy
      • Карта памяти
      • Энтропия
      • Строки
      • Импорты
      • Анализ заголовков
      • Виртуализация
      • Ресурсы
    2. Resource Hacker
    3. Hex-редактор
  2. Динамический анализ
  3. Анализ в IDA
    1. Поиск сообщения о неправильном флаге
    2. Анализ функции и начало сбора флага
    3. Составляем "тело флага"

1. Статический анализ файла
1.1 Detect It Easy
Начнём анализ исполняемого файла с помощью утилиты Detect It Easy (далее DIE).

DIE не обнаружил никаких упаковщиков/протекторов и прочих средств защиты. Сам таск свежий, если верить отметки времени. Кроме того он с графическим интерфейсом.

Посмотрим информацию о файле через продвинутый анализ.

Карта памяти

В карте памяти аномалий не выявлено.

Энтропия

Файл кажется сжатым, но если смотреть по секциям, то наиболее сжатый по энтропии - это секция ресурсов (
Код:
.rsrc
). Пока что просто запомним это. Возможно, далее нам это пригодится.

Строки

Поиск по строкам дал нам возможные ориентиры, на места в коде с выводом сообщение о флаге и о проигрыше. Также названия некоторых функций и пока что непонятное
Код:
MASTER_OF_CODEBY
.

Импорты

Из импортов чего-то "слишком криминального" не выявлено. Но есть
Код:
IsDebuggerPresent
.

Примечание: Хоть функция
Код:
IsDebuggerPresent
и есть в программе, но она не используется в основном коде для изменения поведения таска во время отладки. Поэтому нам будет проще.

Анализ заголовков

По заголовкам всё выглядит нормально для EXE-файла.

Виртуализация

Бывает полезно посмотреть таким образом на файл. По графическому представлению видно, что ресурсы занимают больше всего места в этом исполняемом файле.

Ресурсы

Теперь понятно, откуда такой размер. Это PNG-файл. Посмотрим более детально в Resource Hacker.

1.2 Resource Hacker

Это иконка. Если смотреть на неё в Hexdump'е (Binary View) ничего странного не видно.

1.3 Hex-редактор

Иногда в конце исполняемых файлов некоторых тасков что-то можно найти. Но тут ничего странного нет.

2. Динамический анализ

Динамический анализ в Autoruns, Process Monitor, Process Hacker и Wireshark не показал ничего необычного.

3. Анализ в IDA
Примечание: далее будет использована IDA Freeware 8.4. Её хватит с головой
Но вы также можете использовать Ghidra, Cutter (Radare2) или любой другой отладчик, который вам нравится. Код в декомпиляторах будет +- похожим.

3.1 Поиск сообщения о неправильном флаге
Попробовав запустить файл и ввести что-то в поля для ввода, мы наблюдаем вызов функции
Код:
MessageBox
с текстом о неправильном флаге.

Запустим IDA и попробуем найти код, что вызывает этот
Код:
MessageBox
. Перейдём в окно Strings (Shift + F12).

Русские символы по умолчанию не отображаются. Для этого нужно нажать правую кнопку мыши (далее ПКМ), затем
Код:
Setup
и выбрать такие пункты меню.

Опция Unicode нужна, чтобы нормально отображались символы из 16-битных кодировок в стандарте Unicode. Строки в 16-битной кодировке активно используются в WinAPI.
Галочку со Strict ASCII мы убираем для поиска русских символов (в том числе для опции Unicode). Но для нормального отображения нам нужно зайти ещё сюда.

Затем тут выставляем
Код:
cp1251
. Это стандартная 8-битная кодировка для русских символов в Windows.
Далее нажимаем Rebuild и видим русские символы.

Первых символов не видно, но понять можно. Жмём на "хх..." (от Эхх...).

Нужно представить эти байты как строку. Жмём ALT+A, затем сюда.

Байты представились как текст в кодировке UTF-16LE (UTF-16 Little Endian). Наводим курсор на название строки (
Код:
aE
) И нажимаем X, чтобы IDA показала нам, где используется эта строка. Жмём левой кнопкой мыши (далее ЛКМ) на пункт меню со скрина.

Видим 2 блока, ведущих к
Код:
MessageBoxW
. Обычно это блоки о неправильном/правильном флаге. Переменные
Код:
unk
на скриншотах - это русские строки. Их тоже можно представить в нормальном виде.

Через клавишу N можно поменять название ноды графа, наведя курсор на его первую инструкцию. Кроме этого можно задать цвет через ПКМ или специальную иконку.

Более того мы сразу попали на функцию проверки ввода.


3.2 Анализ функции и начало сбора флага
Составим краткий "скелет" функции через декомпилятор - клавиша F5. В IDA Freeware онлайн-декомпилятор, поэтому иногда он может быть недоступен. Учтите это, если будете пробовать сами.

Через клавишу N можно менять названия объектов.

Убедиться, что эта функция используется для проверки нашего ввода можно, если навести курсор на её название и нажать X. А далее посмотреть, где вызывается.

Эту функцию можно назвать так. Она обрабатывает сообщения от GUI-окна. По коду 0x111 (WM_COMMAND) вызывается наша функция проверки ввода.

На
Код:
check_input
и сосредоточим внимание. Вывод декомпилятора:

C++:


Код:
int
check_input
(
)
{
__int64 v0
;
// rdi
__int64 v1
;
// rax
__int64 v2
;
// rsi
WCHAR
*
v3
;
// rbx
int
v4
;
// esi
int
v5
;
// ebx
int
v6
;
// ebp
int
result
;
// eax
int
v8
;
// edx
UINT v9
;
// r9d
const
WCHAR
*
v10
;
// r8
const
WCHAR
*
v11
;
// rdx
WCHAR String1
[
256
]
;
// [rsp+20h] [rbp-418h] BYREF
WCHAR String
[
256
]
;
// [rsp+220h] [rbp-218h] BYREF
memset
(
String
,
0
,
sizeof
(
String
)
)
;
GetWindowTextW
(
hWnd
,
String
,
256
)
;
memset
(
String1
,
0
,
sizeof
(
String1
)
)
;
GetWindowTextW
(
qword_140005670
,
String1
,
256
)
;
v0
=
-
1LL
;
v1
=
-
1LL
;
do
++
v1
;
while
(
String1
[
v1
]
)
;
if
(
v1
==
27
&&
!
wcsncmp
(
String1
,
L
"CODEBY{"
,
7uLL
)
&&
String1
[
26
]
==
125
)
{
v2
=
11LL
;
v3
=
&
String1
[
13
]
;
while
(
*
(
v3
-
2
)
==
45
&&
iswdigit
(
*
(
v3
-
1
)
)
&&
iswdigit
(
*
v3
)
&&
iswdigit
(
v3
[
1
]
)
&&
iswdigit
(
v3
[
2
]
)
)
{
v2
+=
5LL
;
v3
+=
5
;
if
(
v2
>
24
)
{
v4
=
wtoi
(
&
String1
[
7
]
)
;
v5
=
wtoi
(
&
String1
[
12
]
)
;
v6
=
wtoi
(
&
String1
[
17
]
)
;
result
=
wtoi
(
&
String1
[
22
]
)
;
v8
=
result
;
while
(
String
[
v0
+
1
]
==
aMasterOfCodeby
[
v0
+
1
]
)
{
v0
+=
2LL
;
if
(
v0
==
17
)
{
if
(
(
(
(
v4
^
0xDFAF7
)
+
22098798
)
^
0x23B97B
)
==
24947582
&&
(
(
(
v5
^
0x378
)
+
1361
)
^
0xB84C
)
==
40468
)
{
result
=
(
v6
-
9283
)
^
0xA808
;
if
(
result
==
-
47487
&&
(
(
(
v8
^
0xFD836
)
-
13112
)
^
0xBC3F
)
==
988548
)
{
v9
=
0
;
v10
=
L
"Флаг!!!"
;
v11
=
L
"Флаг, поздравляю! :)"
;
return
MessageBoxW
(
0LL
,
v11
,
v10
,
v9
)
;
}
}
return
result
;
}
result
=
String
[
v0
]
;
if
(
result
!=
aMasterOfCodeby
[
v0
]
)
goto
fail
;
}
break
;
}
}
}
fail
:
v9
=
16
;
v10
=
L
"Эхх..."
;
v11
=
L
"Ну, почти..."
;
return
MessageBoxW
(
0LL
,
v11
,
v10
,
v9
)
;
}
Самые первые инструкции получают данные из полей с вводом имени и флага:

C++:


Код:
memset
(
String
,
0
,
sizeof
(
String
)
)
;
GetWindowTextW
(
hWnd
,
String
,
256
)
;
memset
(
String1
,
0
,
sizeof
(
String1
)
)
;
GetWindowTextW
(
qword_140005670
,
String1
,
256
)
;
В дизассемблированном виде код выглядит так (вывод декомпилятора полезно проверять на листинге дизассемблера):

Сразу дадим нормальные имена, чтобы повысить читаемость. Через отладку можно понять, где будет что.

В данном случае в поле имени было введено "qwe".

Следующая часть кода выполняет подсчёт длины строки с флагом:

C++:


Код:
v0
=
-
1LL
;
v1
=
-
1LL
;
do
++
v1
;
while
(
flag
[
v1
]
)
;
if
(
v1
==
27.
.
.
Листинг дизассемблера:

Флаг должен быть длиною в 27 символов.

Следующий кусок кода:

Первое условие из выделения со скрина проверяет, что начальные 7 символов флага - это
Код:
CODEBY{
. Второе условие из выделения проверяет, что код символа с индексом 26 (по счёту 27-ой) равен 125. В таблице ASCII это символ
Код:
}
.

Листинг дизассемблера:

Таким образом сейчас у нас есть следующее представление флага:

Следующий блок кода:

Листинг дизассемблера:

Данный блок кода проверяет часть флага между фигурными скобками {}. Она должна быть такого вида:

Код:


Код:
-XXXX-XXXX-XXXX
Где
Код:
X
- это цифра в 10-ой системе.

Немного странно, что в этой проверке упущена часть перед первым
Код:
-
, это мы поймём далее.

Затем идёт такой блок:

Он преобразует строку с числом между символами
Код:
-
в число.

Листинг дизассемблера:

Следующий блок кода сравнивает введённое имя со строкой
Код:
MASTER_OF_CODEBY
. Это будет правильным именем для дальнейшего решения таска.

Листинг дизассемблера:


3.3 Составляем "тело флага"
Посмотрим на следующий блок:

Он выполняет шифрование чисел через простые операции и сравнивает их с нужными.

Листинг дизассемблера:

Перепишем это в более понятном виде:

C++:


Код:
first_decimal
^=
0xDFAF7
;
first_decimal
+=
0x151336E
;
first_decimal
^=
0x23B97B
;
// first_decimal должен быть 0x17CAB7E
second_decimal
^=
0x378
;
second_decimal
+=
0x551
;
second_decimal
^=
0xB84C
;
// second_decimal должен быть 0x9E14
third_decimal
-=
0x2443
;
third_decimal
^=
0xA808
;
// third_decimal должен быть -47487 (0xFFFF4681)
fourth_decimal
^=
0xFD836
;
fourth_decimal
-=
0x3338
;
fourth_decimal
^=
0xBC3F
;
// fourth_decimal должен быть 0xF1584
Для удобства я не использовал копии чисел, как показал декомпилятор.

Теперь останется просто применить противоположные операции в обратном порядке к нужным значениям.
  • Для операции + обратная операция -.
  • Для операции - обратная операция +.
  • Для операции xor обратная операция xor.

C++:


Код:
first_decimal
=
0x17CAB7E
;
first_decimal
^=
0x23B97B
;
first_decimal
-=
0x151336E
;
first_decimal
^=
0xDFAF7
;
// (((0x17CAB7E ^ 0x23B97B) - 0x151336E) ^ 0xDFAF7) = 9312
second_decimal
=
0x9E14
;
second_decimal
^=
0xB84C
;
second_decimal
-=
0x551
;
second_decimal
^=
0x378
;
// (((0x9E14 ^ 0xB84C ) - 0x551 ) ^ 0x378) = 8831
third_decimal
=
0xFFFF4681
;
third_decimal
^=
0xA808
;
third_decimal
+=
0x2443
;
// ((0xFFFF4681 ^ 0xA808) + 0x2443) = 4294972108
fourth_decimal
=
0xF1584
;
fourth_decimal
^=
0xBC3F
;
fourth_decimal
+=
0x3338
;
fourth_decimal
^=
0xFD836
;
// (((0xF1584 ^ 0xBC3F) + 0x3338) ^ 0xFD836) = 1221
Итого наши части флага:

Код:


Код:
9312-8831-4294972108-1221
Общий флаг:

Код:


Код:
CODEBY{9312-8831-4294972108-1221}
Третья часть очень выделяется. И проверку не пройдёт. Если посмотреть листинг дизассемблера для функции шифрования, то данные там хранятся в 4-байтовых регистрах.

Представим число 4294972108 в 16-ом виде:

Код:


Код:
0x1000012CC
0x01 00 00 12 CC (по байтам)
Это 5-байтовое число. А регистры 4-байтовые. 5-байтовое число в 4-байтовый регистр никак не влезает. Поэтому в процессе шифрования первые лишние байты просто отсекаются. В данном случае это байт 0x01 в самом начале:

Код:


Код:
0x00 00 12 CC (по байтам)
Получили 0x000012CC. Незначащие байты (0x0000) можно убрать и получить 16-ое число. Это 16-ое число переводим в 10-е. Далее оформляем флаг и сдаём.

Делиться открытым флагом не буду, дальше вы сами добьёте таск!

Спасибо за прочтение статьи!
 
Ответить с цитированием

  #2  
Старый 29.08.2024, 00:07
abstract abstract
Новичок
Регистрация: 18.12.2023
Сообщений: 0
С нами: 1267841

Репутация: 0
По умолчанию

О, классно!
Не знал как русские символы отобразить и поэтому не мог найти шифрование флага, а дальше сам всё решил не читая дальше статью
Но за раскрытие темы спасибо!
 
Ответить с цитированием

  #3  
Старый 29.08.2024, 00:15
abstract abstract
Новичок
Регистрация: 18.12.2023
Сообщений: 0
С нами: 1267841

Репутация: 0
По умолчанию

На счёт кстати третьего ключа, решил брутфорсом. Метод глупый конечно, но рабочий

Python:


Код:
>>
>
for
i
in
range
(
0
,
9999
)
:
.
.
.
if
hex
(
(
0x100000000
+
i
-
9283
)
^
0xa808
)
==
'0xffff4681'
:
.
.
.
print
(
i
)
.
.
.
 
Ответить с цитированием

  #4  
Старый 29.08.2024, 00:16
ROP
Новичок
Регистрация: 27.08.2019
Сообщений: 0
С нами: 3533622

Репутация: 0
По умолчанию

Цитата:

abstract abstract сказал(а):

На счёт кстати третьего ключа, решил брутфорсом. Метод глупый конечно, но рабочий

Python:


Код:
>>
>
for
i
in
range
(
0
,
9999
)
:
.
.
.
if
hex
(
(
0x100000000
+
i
-
9283
)
^
0xa808
)
==
'0xffff4681'
:
.
.
.
print
(
i
)
.
.
.
Главное - решил! =D
 
Ответить с цитированием

  #5  
Старый 29.08.2024, 17:13
Niapoll
Новичок
Регистрация: 14.11.2023
Сообщений: 0
С нами: 1316119

Репутация: 0
По умолчанию

Цитата:

Mogen сказал(а):

Перепишем это в более понятном виде

Пользователей дизассемблера Ghidraбудет ждать приятный сюрприз (в данном случае):
P. S. Поэтому я даже сначала и не понял о каком "шифровании" вообще идет речь в статье
 
Ответить с цитированием

  #6  
Старый 29.08.2024, 20:25
ROP
Новичок
Регистрация: 27.08.2019
Сообщений: 0
С нами: 3533622

Репутация: 0
По умолчанию

Цитата:

Niapoll сказал(а):

Пользователей дизассемблера Ghidraбудет ждать приятный сюрприз (в данном случае):
P. S. Поэтому я даже сначала и не понял о каком "шифровании" вообще идет речь в статье
Ого! Но такое и в IDA бывает, к сожалению. Декомпилятор не идеален и нужно в дизассемблере проверять
 
Ответить с цитированием

  #7  
Старый 30.08.2024, 05:49
nsk
Новичок
Регистрация: 17.04.2022
Сообщений: 0
С нами: 2146278

Репутация: 0
По умолчанию

Цитата:

abstract abstract сказал(а):

На счёт кстати третьего ключа, решил брутфорсом. Метод глупый конечно, но рабочий

Python:


Код:
>>
>
for
i
in
range
(
0
,
9999
)
:
.
.
.
if
hex
(
(
0x100000000
+
i
-
9283
)
^
0xa808
)
==
'0xffff4681'
:
.
.
.
print
(
i
)
.
.
.
или так: v6=(0x4681 ^ 0xA808) + 0x2443 & 0xFFFF
 
Ответить с цитированием
Ответ





Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.

×

Внести депозит

Введите сумму USDT:

Принимается только USDT TRC20. Fake/Flash USDT не засчитывается.

×

Вывести депозит

Сумма USDT:

Ваш USDT TRC20 кошелек:

Заявка будет отправлена администратору.