Задание: zippy.zip

Задание требует передать ключ через аргумент и выводит некую строку, которая при передаче верного ключа и будет флагом. Задание написано на Swift это видно с функции main.

...
.text:00000000004035B9                 call    swift_once
...

На данный момент только Hopper умеет красиво работать со всем, что породила Apple. Hopper сразу выдал список размангленых функций, а также дописал их комментарием в дизасм. Интересных функций немного.

В функции main соответственно вызывается zippy.main в которой формируется массив cipherText и передается вместе с keyArray и iv в zippy.process. Результат которой и выводится как флаг.

В этой же функции ничего особенного нет. Сперва получаем sbox с помощью соответствующей функции, а дальше идет посимвольный xor.

Алгоритм расшифровки можно представить как

def decrypt(data, key, iv):
    flag = []
    for i in range(len(data)):
        k = key[i]
        flag.append(data[i] ^ sbox[k] ^ iv ^ k)
        iv = data[i]
    return ''.join(map(chr, flag))

Из этого вытекает одна проблема, т.к. размер ключа равен размеру криптотекста, то полным перебором его искать это мягко говоря идиотизм. Но алфавит для перебора можно значительно сократить. Т.к. каждый элемент в криптотексте и sbox у нас 16 битное значение, а дальше еще и iv им станет, а после всех операций мы должны получить печатное значение в диапозоне 0x20-0x7f. Следовательно проверим все возможные значения ключа, которые дадут в результате печатное значение.

def make_alphabet(data, iv):
    key = []
    for i in range(len(data)):
        key.append([])
        for k in range(0x20, 0x7f):
            c = data[i] ^ sbox[k] ^ iv ^ k
            if c < 0x7f and c > 0x1f:
                key[i].append(k)
        iv = data[i]
    return key

В результате видим, что практически для всех значений имеется только одно верное значение ключа

[[67],
 [65, 123],
 [100],
 [83],
 [88],
 [110],
 [80],
 [77],
 [57],
 [90],
 [76, 89],
 [49],
 [108],
 [70, 73],
 [112],
 [124],
 [53],
 [55],
 [74],
 [70],
 [101],
 [76, 89],
 [68, 99],
 [70, 73],
 [124],
 [95],
 [126],
 [97],
 [52],
 [80],
 [63],
 [54],
 [70],
 [81],
 [80],
 [109],
 [63],
 [111],
 [54],
 [97]]

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

def brute(key, n, alphabet):
    if n > 0:
        for a in alphabet[n - 1]:
            brute([a] + key, n - 1, alphabet)
    else:
        flag = decrypt(data, key, iv)
        print(flag)

Весь перебор занимает пару секунд и на выходе получается 64 значения, среди которых с легкостью можно найти верное значение

VolgaCTF{__get_some_beer_from_my_bear__}