Rating:
# **Write-up Invalid Machine (CTFZone 2021)**
![](https://i.imgur.com/UqqFsm1.png)
> Этот райтап спонсирован каналом [@ch4nnel1](https://t.me/ch4nnel1)
Original write-up: [https://hackmd.io/@osogi/Invalid-Machine](https://hackmd.io/@osogi/Invalid-Machine)
## Разбор + история
![](https://i.imgur.com/jKqumbe.png)
Сам таск (https://ctf.bi.zone/challenges/9)
Ну как обычно запустим бинарник
![](https://i.imgur.com/9yu4b1n.png)
Он скажет, что какая-то машина, скорее всего наш комп, невалидна. Откроем его в радаре, любым удобным способом выйдем на "главную" функцию `fcn_00001ce1` обзавем ее `Main` (так как main занят), там происходит примерно следующее.
```python=1
def Main:
start_arr=[0x8c, 0x2a ... 0xf5, 0x0f] #[rbp - 0x90]...[rbp - 0x11]
#len(start_arr)==0x80
buf_arr=[0]*0x80 #[rbp - 0x110]...[rbp - 0x91]
hashid=[0]*0x20 #[rbp-0x140]-[rbp-0x121]
leng=0x80 #[rbp - 0x144]
if(!fcn_0000179e(hashid)):
return 0
if(!fcn_00001c23(hashid)):
return 0
buf_arr=start_arr.copy()
#Какая-то магия с buf_arr и hashid
#В результате которой buf_arr меняется
start_arr=buf_arr.copy()
if(!fcn_00001a15(start_arr, leng)):
print("Invalid machine...")
return 0
print("Good job")
for c in start_arr[0x40:leng]:
print(chr(c), end='')
print()
return 1
```
Рассмотрим функцию `fcn_0000179e` (я ее сильно упростил, там были еще проверки на успешное завершение функций, но функции могли завершиться некорректно только из-за сбоя в системе)
```python=1
def fcn_0000179e(a):
integer=0
s=read_machine_id(32) #fcn_00001326
# read_machine_id(n) - выделить в памяти n+1 байт,
# прочитать в них n байт из "/etc/machine-id",
# вернуть указатель на них
hex_to_int(s, integer) #fcn_00001246
# hex_to_int(a, b) == b=int(a[:8], 16)
hashid = sha256(integer, 4) #fcn_000011e5. Определяем, что это sha256 по константам в глубине этой функции
# sha256(arg, ln) arg-то от чего считается хеш, ln-размер arg в байтах
#Тут, снова криптографическая магия, но в этот раз с hashid
a=hashid
return 0
```
Получается `fcn_0000179e` вернет в `Main` некий преобразованный хеш от 4 байт, представленных в `/etc/machine-id`, и запишет его в `hashid`
Рассмотрим `Main` дальше, в функции `fcn_00001c23` происходит просто проверка на то, что `hashid` состоит не полностью из нулей.
А вот `fcn_00001a15` будет чуть интереснее (псевдокод как и в прошлый раз приведен без 'системных' проверок)
```python=1
def fcn_00001a15(arr, leng):
first=arr[0:0x40].copy()
second=arr[0x40:leng].copy()
buf=sha256(second, leng-0x40)
hexstr=to_hexstr(buf, 32) #fcn_000018e8
#to_hexstr(arg, ln) == hex(buf) при этом sizeof(buf)==ln
return (hexstr==str(first))
```
Посмотрев на все эти функции и куски кода с криптографической магией. Мне показалось, что самым верным и быстрым способом решения будет сбрутить `integer` из `fcn_0000179e`, так как `hex_to_int` возвращает только 4 байта и прога использует `integer`, как 4 байтную переменную. Хоть 4 байта и можно сбрутить, но это будет не слишком быстро, так что нужно это максимально оптимизировать, и способ из разряда, менять `/etc/machine-id`, запускать бинарь и смотреть на его вывод, точно не будет достаточно быстрым.
В результате мной было решено запатчить бинарь, патч в псевдокоде должен был выглядеть примерно так
```python=1
def Main:
while True:
start_arr=[0x8c, 0x2a ... 0xf5, 0x0f] #[rbp - 0x90]...[rbp - 0x11]
#len(start_arr)==0x80
buf_arr=[0]*0x80 #[rbp - 0x110]...[rbp - 0x91]
hashid=[0]*0x20 #[rbp-0x140]-[rbp-0x121]
leng=0x80 #[rbp - 0x144]
if(!fcn_0000179e(hashid)):
return 0
if(!fcn_00001c23(hashid)):
return 0
buf_arr=start_arr.copy()
#Какая-то магия с buf_arr и hashid
#В результате которой buf_arr меняется
start_arr=buf_arr.copy()
if(!fcn_00001a15(start_arr, leng)):
continue #patched
print("Good job")
for c in start_arr[0x40:leng]:
print(chr(c), end='')
print()
return 1
def fcn_0000179e(a):
integer=0
glob+=1 #patched
integer=glob
hashid = sha256(integer, 4) #fcn_000011e5.
#Тут, снова криптографическая магия, но в этот раз с hashid
a=hashid
return 0
glob = 0 #patched
```
Проблемы с которыми я столкнулся:
1. Где размещать глобальную переменную? Это решилось гениальным или неочень способом, я разместил ее на месте занопленного (заполненного nop'ами) кода, но это вызвало еще другую проблему.
2. Изменить права секции и сегмента на rwx, права секции я изменил через радар, а вот разрешения сегмента пришлось менять вручную через 010editor
3. Радар/ризин часто косячут с преобразованием асм инструкций, и приходится либо исжитряться либо пользоваться другими тулзами. Я воспользовался первым способом.
![](https://i.imgur.com/e7KQ8Br.png)
В результате получил примерно такой [бинарь](https://github.com/osogi/writeups/blob/main/ctfzone2021/Invalid_Machine/invalid_machine_patched.elf).
Пока он стоял на бруте, я успел поделать другие таски, устал, сделал еще одну версию бинарника, которая бы перебирала результат с верху, посчитал что даже с двумя активными прогами я не успею перебрать весь отрезок, растроился, отчаился, разобрал куски кода с криптографической магией, понял, что в теории зная формат флага и начальное состояние массива можно востановить `hashid` из `Main`, а потом и весь флаг, решил воспользоваться angr'ом, для совершения этого плана. Но в моей голове пошло что-то не так и в результате вместо, поиска валидного значения `hashid` для получения правильного начала флага, я заставил angr искать `integer`, который я пытался сбрутить.
Вроде мой скрипт с angr'ом даже начал работать, но у меня сложилось впечатление, что он просто начал брутить, так что я остановил две свои брут-машины (и записал значение на которых они остановились) и решил дать ему шанс поработать 10 минут, но он за отведенное время ничего не выдал => был ~~казнен~~ остановлен. Я запустил свои брут-машины снова, и каково было мое удивление, когда одна из них спустя 3 минуты выдала это
![](https://i.imgur.com/VIerfw9.png)
Тип, вроде флаг, ура! Но нет, сайт говорит "флаг - инвалид", тут я уже растроился, что стратегия с брутом не сработала, но тут мне опять помогла моя тима
![](https://i.imgur.com/1ukyBzw.png)
Оказывается верным флагом был не `ctfzone{_You_are_a_hacker_but_you_are_the_good_kind_of_hacker!_}`, а `ctfzone{You_are_a_hacker_but_you_are_the_good_kind_of_hacker!}`, ну или по крайней мере мне так сообщил тимейт.
## Решение
- Разреверсить fcn_00001ce1 и fcn_0000179e
- Понять, что в fcn_0000179e есть четырехбайтная переменная, которую можно сбрутить
- Пропатчить файл, чтобы он сам себя брутил ([пример пропатченого файла](https://github.com/osogi/writeups/blob/main/ctfzone2021/Invalid_Machine/invalid_machine_patched.elf))
- Поставить брутить
- Подождать 5 часов ([версия для нетерпеливых на 3 минуты](https://github.com/osogi/writeups/blob/main/ctfzone2021/Invalid_Machine/invalid_machine_patched_flash.elf))
- Закончить брут, попытаться сдать флаг `ctfzone{_You_are_a_hacker_but_you_are_the_good_kind_of_hacker!_}`
- Понять, что в нем лишние нижние подчеркивания, сдать флаг `ctfzone{You_are_a_hacker_but_you_are_the_good_kind_of_hacker!}`
## Эпилог
Хоть я и решил этот таск, но кажется авторское, правильное решение далеко не такое. Так что пожалуйста, если Вы знаете как его решить по нормальному и читаете этот райтап. Напишите свой райтап, поделитесь своими знаниями с миром, в том числе и со мной.
За сим я откланиваюсь, свою миссию по райтапам для ctfzone2021 уже выполнил, и теперь свободен.
![](https://pa1.narvii.com/6456/4e3f00283ac6ae4b98a540aa78aaa768093e4413_hq.gif)