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

  #1  
Старый 25.03.2024, 16:40
Kevgen
Новичок
Регистрация: 11.05.2023
Сообщений: 0
С нами: 1585741

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

Всем привет!
Сегодня закончился CTF NeoQuest, и я хочу показать решение одной задачи из школьного трека.

Название: Коридоры замка
Описание: "Из странных писем складывается различимое послание. Кажется, я могу прочитать. «Странник! Пишет тебе князь Ксеритха. Много лет назад, на охоте, я подстрелил черную птицу. Она оказалась подручной ведьмы, и та прокляла город. С тех пор мы не можем разговаривать, не способны покинуть Ксеритх и живем во тьме. Многие пытались спасти меня – приглашенные маги, инженеры, рыцари. Но ведьма сказала, что лишь тот, кто придет из другого мира, сможет снять проклятие. Если справишься, сделаю для тебя что угодно. Чтобы начать, найди придворного мага. Он подскажет путь». Не могу поверить. Что еще за Ксеритх? Посмотреть бы в поисковике, но мой телефон не работает. Странный сбой. Серый экран, дата и время: 17 февраля 2024 года 14:12. И больше ничего. Осматриваю себя. Я как-то изменился… Меч, высокие сапоги, перчатки. Ладно… Допустим… Я – и спасение целого города? Вот к такому повороту событий привыкнуть сложнее. Я, может быть, и не хочу никого спасать. Я этот Ксеритх впервые вижу! Сдался он мне. Как до этого мага дойти-то вообще? У меня даже карты нет."
Дан файл: easy_chipher.exe (приложил)

Запустим его:



Видно, что сообщение шифруется с динамичным ключом, так как на один ввод разные ответы.

Посмотрим его в DetectItEasy:



exe'шник упакован с помощью Pyinstaller, значит язык - Python.

Достаем исходный код
Попробуем достать .pyc файл из exe.
Можно использовать pyinstxtractor, но я декомпилировал онлайн:



Скачиваем архив. Нам нужен файл "easy_chipher.pyc". Если его запустить, то мы получим тот же самый функционал, как и в exe.
Открыть в редакторе его не получится, так как ".pyc" файлы - это скомпилированный байткод скрипта.

Попробуем декомпилировать в `.py` файл с помощью "decompyle3":



Видим ошибку, так как "decompyle3"не работает с версией python 3.12.
Путем проб и ошибок стало ясно, что, так как 3.12 - это новая версия Python, то большинство декомпиляторов её ещё не поддерживают.
К счастью я наткнулся на статью, в которой описывается подобный случай.

Для декомпиляции будем использовать pycdc, которая активно поддерживается сообществом. Для использования надо скомпилировать "pycdc.exe" из исходников.
1. Создаем папку, например "another";
2. Копируем в него репозиторий:
Код:
git clone https://github.com/zrax/pycdc.git
3. Компилируем:
Код:
cmake pycdc
4. Выходим из папки "another":
Код:
cd ..
5. Ещё раз компилируем:
Код:
cmake --build another
6. В папке ".\another\Debug\" лежит "pycdc.exe".

Запускаем:
Код:
.\pycdc.exe .\easy_chipher.pyc
:



Уже лучше! Получаем исходный код программы, правда не полный.
Видны 2 ошибки:
Код:
Unsupported opcode: BINARY_SLICE
и
Код:
Unsupported opcode: JUMP_BACKWARD
.
Ошибки в байткоде обрабатываются pycdc в файле
Код:
ASTree.cpp
, строки 2466-2471:

C++:


Код:
default
:
fprintf
(
stderr
,
"Unsupported opcode: %s\n"
,
Pyc
::
OpcodeName
(
opcode
&
0xFF
)
)
;
cleanBuild
=
false
;
return
new
ASTNodeList
(
defblock
->
nodes
(
)
)
;
Вставим break в обработчик:

C++:


Код:
default
:
fprintf
(
stderr
,
"Unsupported opcode: %s\n"
,
Pyc
::
OpcodeName
(
opcode
&
0xFF
)
)
;
break
;
// 
nodes
(
)
)
;
Теперь заново компилируем "pycdc.exe" и запускаем.
Получаем сий код:

Python:


Код:
import
random
import
time
def
encoding
(
string
,
key
)
:
Unsupported opcode
:
BINARY_SLICE
Unsupported opcode
:
JUMP_BACKWARD
Unsupported opcode
:
END_FOR
Unsupported opcode
:
JUMP_BACKWARD
Unsupported opcode
:
END_FOR
Warning
:
block stack
is
not
empty!
    str_encode
=
''
for
i
in
range
(
0
,
len
(
string
)
,
6
)
:
block
=
i
+
6
encode_block
=
''
for
j
in
range
(
len
(
block
)
)
:
temp
=
ord
(
block
[
j
]
)
^
key
[
j
]
encode_block
+=
chr
(
temp
)
str_encode
+=
encode_block
return
str_encode
.
encode
(
)
def
getkey
(
)
:
Unsupported opcode
:
JUMP_BACKWARD
Unsupported opcode
:
END_FOR
Warning
:
block stack
is
not
empty!
    random
.
seed
(
int
(
time
.
time
(
)
)
)
key
=
[
]
for
i
in
range
(
0
,
6
)
:
temp
=
random
.
randint
(
0
,
256
)
key
.
append
(
temp
)
return
key
print
(
'[*] Hey guys! This is cipher program'
)
print
(
'[*] Write the message:'
)
message
=
str
(
input
(
)
)
if
message
.
encode
(
)
==
b'\r\xc2\xa9\xc3\xae\xc3\x97\xc2\x85~t\xc2\x9d\xc2\xb9\xc3\x95\xc3\x93/u\xc3\x8f\xc2\xb9\xc3\x9f\xc2\x84xt\xc2\x9a\xc2\xb9\xc3\x97\xc3\x93+\'\xc3\x8f\xc2\xbe\xc3\x92\xc2\x8f+%\xc2\x9a\xc3\xad\xc3\x9f\xc2\x80ru\xc3\x8b\xc2\xb9\xc2\x83\xc2\x80,w\xc2\x9d\xc3\xaa\xc3\x95\xc3\x96,{\xc3\x8d\xc3\xaf\xc3\x9f\xc2\x87}!\xc3\x88\xc2\xb9\xc2\x84\xc3\x93{%\xc3\x8a\xc3\xaf\xc2\x84\xc3\x96r"\xc3\x81\xc3\xac\xc3\x9e'
:
print
(
'[ERORR] You want to hack me!? Nope.'
)
else
:
encode
=
encoding
(
message
,
getkey
(
)
)
print
(
'[*] Encoding message: '
,
encode
)
print
(
'[*] Press enter to exit'
)
input
(
)
Отлично, теперь функции encoding()" и "getkey()" обработались до конца.

Анализируем код
Первым делом приведем код в нормальный вид (исправим паддинги и другие ошибки):

Python:


Код:
import
random
import
time
def
encoding
(
string
,
key
)
:
str_encode
=
''
for
i
in
range
(
0
,
len
(
string
)
,
6
)
:
# block = i + 6 # <--- неправильная декомпиляция
block
=
string
[
i
:
i
+
6
]
# <--- правильный вариант
encode_block
=
''
for
j
in
range
(
len
(
block
)
)
:
temp
=
ord
(
block
[
j
]
)
^
key
[
j
]
encode_block
+=
chr
(
temp
)
str_encode
+=
encode_block
return
str_encode
.
encode
(
)
# <--- return вернул из циклов for
def
getkey
(
)
:
random
.
seed
(
int
(
time
.
time
(
)
)
)
key
=
[
]
for
i
in
range
(
0
,
6
)
:
temp
=
random
.
randint
(
0
,
256
)
key
.
append
(
temp
)
return
key
# <--- return вернул из цикла for
print
(
'[*] Hey guys! This is cipher program'
)
print
(
'[*] Write the message:'
)
message
=
str
(
input
(
)
)
if
message
.
encode
(
)
==
b'\r\xc2\xa9\xc3\xae\xc3\x97\xc2\x85~t\xc2\x9d\xc2\xb9\xc3\x95\xc3\x93/u\xc3\x8f\xc2\xb9\xc3\x9f\xc2\x84xt\xc2\x9a\xc2\xb9\xc3\x97\xc3\x93+\'\xc3\x8f\xc2\xbe\xc3\x92\xc2\x8f+%\xc2\x9a\xc3\xad\xc3\x9f\xc2\x80ru\xc3\x8b\xc2\xb9\xc2\x83\xc2\x80,w\xc2\x9d\xc3\xaa\xc3\x95\xc3\x96,{\xc3\x8d\xc3\xaf\xc3\x9f\xc2\x87}!\xc3\x88\xc2\xb9\xc2\x84\xc3\x93{%\xc3\x8a\xc3\xaf\xc2\x84\xc3\x96r"\xc3\x81\xc3\xac\xc3\x9e'
:
print
(
'[ERORR] You want to hack me!? Nope.'
)
else
:
encode
=
encoding
(
message
,
getkey
(
)
)
print
(
'[*] Encoding message: '
,
encode
)
print
(
'[*] Press enter to exit'
)
input
(
)
Перед нами программа, реализующая шифрование XOR.
Обратим внимание на способ генерации ключа: используется
Код:
random.seed(int(time.time()))
.

Код:
int(time.time())
в данном случае возвращает UNIX-time время, т.е. каждую секунду будет уникальный ключ.
Переведём время "17.02.2024 14:12:00" в UNIX вид: получим
Код:
1708179120
Из описания таска мы знаем, что дата и время - 17 февраля 2024 года 14:12, секунды нам неизвестны. Благо в минуте их всего 60, мы можем пробрутить XOR с каждым из них.

Напишем программу:

Python:


Код:
import
random
import
time
current_time
=
1708179120
def
encoding
(
string
,
key
)
:
str_encode
=
''
for
i
in
range
(
0
,
len
(
string
)
,
6
)
:
block
=
string
[
i
:
i
+
6
]
# print(block)
encode_block
=
''
for
j
in
range
(
len
(
block
)
)
:
temp
=
ord
(
block
[
j
]
)
^
key
[
j
]
encode_block
+=
chr
(
temp
)
str_encode
+=
encode_block
return
str_encode
.
encode
(
)
def
getkey
(
)
:
# 1708179178
random
.
seed
(
current_time
)
key
=
[
]
for
i
in
range
(
0
,
6
)
:
temp
=
random
.
randint
(
0
,
256
)
key
.
append
(
temp
)
return
key
print
(
'[*] Hey guys! This is cipher program'
)
print
(
'[*] Write the message:'
)
message
=
b'\r\xc2\xa9\xc3\xae\xc3\x97\xc2\x85~t\xc2\x9d\xc2\xb9\xc3\x95\xc3\x93/u\xc3\x8f\xc2\xb9\xc3\x9f\xc2\x84xt\xc2\x9a\xc2\xb9\xc3\x97\xc3\x93+\'\xc3\x8f\xc2\xbe\xc3\x92\xc2\x8f+%\xc2\x9a\xc3\xad\xc3\x9f\xc2\x80ru\xc3\x8b\xc2\xb9\xc2\x83\xc2\x80,w\xc2\x9d\xc3\xaa\xc3\x95\xc3\x96,{\xc3\x8d\xc3\xaf\xc3\x9f\xc2\x87}!\xc3\x88\xc2\xb9\xc2\x84\xc3\x93{%\xc3\x8a\xc3\xaf\xc2\x84\xc3\x96r"\xc3\x81\xc3\xac\xc3\x9e'
message
=
message
.
decode
(
)
for
i
in
range
(
60
)
:
encode
=
encoding
(
message
,
getkey
(
)
)
print
(
'[*] Encoding message: '
,
encode
.
decode
(
)
)
current_time
+=
1
Запускаем. На 58 секунде (1708179178) получаем верный флаг.
Ответ:
Код:
NQ20247ee2de67e8327be0dad7b58afb187863ed7f4e62af853807b0ecd1f23ca8a909
Спасибо за прочтение!
 
Ответить с цитированием
Ответ





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


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




ANTICHAT ™ © 2001- Antichat Kft.