# **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://ctf.bi.zone/challenges/9)
Ну как обычно запустим бинарник
![](https://i.imgur.com/9yu4b1n.png)
Он скажет, что какая-то машина, скорее всего наш комп, невалидна. Откроем его в радаре, любым удобным способом выйдем на "главную" функцию `fcn_00001ce1` обзавем ее `Main` (так как main занят), там происходит примерно следующее.```python=1def 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=1def 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=1def 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=1def 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, права секции я изменил через радар, а вот разрешения сегмента пришлось менять вручную через 010editor3. Радар/ризин часто косячут с преобразованием асм инструкций, и приходится либо исжитряться либо пользоваться другими тулзами. Я воспользовался первым способом.
![](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)