Нечасто нам доводится изучать код, который до нас видели только считанное количество людей; код, который был важной частью разрушения системы апартеида в ЮАР; код, который использовался для защищённых коммуникаций с одноразовыми шифрами, контрабандой передававшихся в ЮАР на дискетах бортпроводником. Но мне довелось испытать это одним утром вскоре после того, как я расшифровал тридцатилетний файл PKZIP, пароль к которому давно забыли.

Недавно я заинтересовался защищёнными коммуникациями, которые использовались Африканским национальным конгрессом в рамках операции «Вула», проводившейся в конце 1980-х годов. Операция «Вула» заключалась в проникновении лидеров АНК (и передаче снаряжения) в ЮАР для подготовки тайной сети, реализующей различные элементы политической активности АНК внутри страны.

Для успеха операции требовались защищённые коммуникации, организованные на основе 8-битных компьютеров, DTMF-сигналов, акустических преобразователей и различного другого оборудования для обмена сообщений с одноразовым шифрованием, использующих программы, написанные на PowerBASIC.


Я не буду вдаваться в подробности работы всего этого, поскольку главный разработчик системы шифрования Тим Дженкин выложил исходный код в опенсорс. Статью Тима о системе шифрования можно найти здесь. Я крайне рекомендую прочитать её, если вам интересны подробности.

Код не был выложен в опенсорс раньше по одной простой причине: улетая из Великобритании в ЮАР в 1991 году, он упаковал весь исходный код в файл zip и установил пароль. В последующие годы он просто забыл пароль! Поэтому когда я написал ему письмо с вопросом, можно ли выложить его в опенсорс, он ответил:

У меня всё ещё хранится исходный код «Вула», но, к сожалению, по большей мере он недоступен, потому что возвращаясь из Великобритании в ЮАР в 1991 году, я запаковал все файлы в zip с паролем. Мне удалось расшифровать и распаковать один из файлов, но, увы, это была очень ранняя версия ПО. Остальное я не могу извлечь, потому что забыл пароль. Когда я вернулся в ЮАР, потребности в коде не было. Я думал, что никогда не забуду пароль, но когда попытался расшифровать его спустя несколько лет, не смог его вспомнить.

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

Я с готовностью согласился, и он отправил мне два файла: ALLBAS.ZIP и CODMAY93.ZIP. Они были созданы в старой версии PKZIP и защищены паролем. К счастью, существует известная атака на основе открытых текстов на схему ZipCrypto, которая использовалась в формате ZIP той эпохи. А опенсорсная реализация этой атаки называется bkcrack.

То есть достаточно было «просто» спрогнозировать 12 байтов открытого текста в известном месте внутри файла ZIP. Вот фрагмент того, что находилось внутри файла ZIP:

$ bkcrack -L ALLBAS.ZIP | head -n 20

bkcrack 1.7.0 - 2024-05-26
Archive: ALLBAS.ZIP
Index Encryption Compression CRC32    Uncompressed  Packed size Name
----- ---------- ----------- -------- ------------ ------------ ----------------
    0 ZipCrypto  Shrink      b0f86b1d          163          117 A1PSW.BAS
    1 ZipCrypto  Shrink      8fa662d4          163          118 A2PSW.BAS
    2 ZipCrypto  Shrink      0c5a7295          163          119 A3PSW.BAS
    3 ZipCrypto  Shrink      49907f86          179          125 A4PSW.BAS
    4 ZipCrypto  Shrink      3d20eb7a          163          120 A5PSW.BAS
    5 ZipCrypto  Shrink      f8b558f0          136          128 BIOS.INC
    6 ZipCrypto  Implode     799074ed          377          278 CHKERR.INC
    7 ZipCrypto  Implode     c44ea0a5        17906         5401 CODSUBS.INC
    8 ZipCrypto  Implode     7bd7e23d        27287         8297 COMAID.BAS
    9 ZipCrypto  Implode     03dc63da         2109         1001 COMKEY.BAS
   10 ZipCrypto  Store       3500d320         2372         2384 CONFIG.TIM
   11 ZipCrypto  Shrink      35a85089          147          111 CONPSW.BAS
   12 ZipCrypto  Implode     55be75ce         2094          825 DOS.INC
   13 ZipCrypto  Shrink      3387d043          134          127 DOSVER.INC
   14 ZipCrypto  Implode     28a32efa         1304          535 DOSX.INC
   15 ZipCrypto  Implode     6578a66c         3196          966 EDDY.BAS

У Тима было несколько незашифрованных файлов .BAS, но их версии отличались от того, что было в файле, и попытки атак bkcrack с их помощью (после прогона из через оригинальный PKZIP в DOSBox) оказались безуспешными; я решил, что прежде чем проводить дальнейшие атаки, нужно немного поразмыслить.

В ALLBAS.ZIP содержалось множество файлов без сжатия, потому что они и так были двоичными, а значит, не стоили сжатия. Эти файлы помечены как Store:

$ bkcrack -L ALLBAS.ZIP | grep Store
   10 ZipCrypto  Store       3500d320         2372         2384 CONFIG.TIM
   23 ZipCrypto  Store       14a285ac            2           14 KEYCOD.EXE
   25 ZipCrypto  Store       d6343ce1         4767         4779 KEYONE.ZIP
   26 ZipCrypto  Store       650778b7         6523         6535 KEYTHREE.ZIP
   30 ZipCrypto  Store       12a711cd        58172        58184 OLDCOD.ZIP
   41 ZipCrypto  Store       00000000            0           12 TAPCOD.EXE
   44 ZipCrypto  Store       55000714        12716        12728 TECOD5.ZIP
   45 ZipCrypto  Store       f4f4366c         9230         9242 TECOD6.ZIP

Файлы, хранящиеся как Store, перспективны для прогнозирования открытого текста, потому что их не сжимали и для получения открытого текста не нужно сжимать исходный файл. Изучение файлов ZIP, поскольку файлы ZIP начинаются с заголовка PK, показалось мне подходящим способом поиска предсказуемого открытого текста в известной позиции. Вот поля стандартного заголовка PK в самом начале файла ZIP:


Я решил, что реалистичным способом атаки будет прогнозирование имени первого файла в архиве. Если имя файла состоит как минимум из восьми символов (что достаточно вероятно, ведь как минимум четыре символа использовано для .BAS, .INC и так далее), то будет доступно не менее 12 символов открытого текста, если сложить размер имени файла (смещение 0x1A, 0x1B) и длину дополнительного поля (которое во всех отправленных Тимом ZIP оказалось равным 0x00, 0x00).

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

К счастью, у Тима обнаружилась другая версия OLDCOD.ZIP (одного из файлов ZIP внутри ALLBAS.ZIP), и он сообщил мне, что первый файл в нём называется COMKEY.BAS. Я написал небольшую программу на Perl для создания необходимого открытого текста в надежде, что OLDCOD.ZIP внутри ALLBAS.ZIP начинается с COMKEY.BAS

$ cat maken.pl
use strict;
use warnings;

my $outfile = "hexname-$$.txt";

while (<>) {
    chomp;
    my $bas = $_;
    print("$bas / $outfile\n");
    my $n = sprintf("%c\x00\x00\x00$bas",length($bas));
    open G, ">$outfile";
    print G $n;
    close G;
    system("bkcrack -C ALLBAS.ZIP -c OLDCOD.ZIP -p $outfile -o 26 -j 8");
}

Через 23 минуты bkcrack выдал ключ к файлу ALLBAS.ZIP, и мне удалось его расшифровать. Тот же самый ключ подошёл и к CODMAY93.ZIP.

$ time echo "COMKEY.BAS" | perl maken.pl

COMKEY.BAS / hexname-41227.txt

bkcrack 1.7.0 - 2024-05-26

[07:49:38] Z reduction using 6 bytes of known plaintext

100.0 % (6 / 6)

[07:49:38] Attack on 925073 Z values at index 33

Keys: 98e0f009 48a0b11a c70f8499

80.6 % (745571 / 925073) 

Found a solution. Stopping.

You may resume the attack with the option: --continue-attack 745571

[18:13:49] Keys

98e0f009 48a0b11a c70f8499


real 23m4.371s

user 162m3.520s

sys 0m37.752s

После нахождения ключа bkcrack выполняет расшифровку:

$ bkcrack -C ALLBAS.ZIP -k 98e0f009 48a0b11a c70f8499 -D ALLBAS-DECRYPTED.ZIP

bkcrack 1.7.0 - 2024-05-26

[07:52:22] Writing decrypted archive ALLBAS-DECRYPTED.ZIP

100.0 % (81 / 81)

$ bkcrack -C CODMAY93.ZIP -k 98e0f009 48a0b11a c70f8499 -D CODMAY93-DECRYPTED.ZIP

bkcrack 1.7.0 - 2024-05-26

[07:58:31] Writing decrypted archive CODMAY93-DECRYPTED.ZIP

100.0 % (40 / 40)

И так мы наконец получили давно утерянный исходный код, использовавшийся для организации защищённых коммуникаций АНК!

Если бы у меня не получилось, я бы атаковал один из остальных файлов ZIP при помощи того же способа (а потом бы просто начал перебирать имена файлов). Я бы предположил, что TECOD5.ZIP, вероятно, был ZIP всего одного файла TECOD.BAS (или может быть TECOD5.BAS), судя по сжатому размеру TECOD.BAS в ALLBAS.ZIP. Оказалось, что если бы я начал с этого, то мне не пришлось бы ждать 23 минуты:

$ time echo "TECOD5.BAS" | perl maken.pl

TECOD5.BAS / hexname-41544.txt

bkcrack 1.7.0 - 2024-05-26

[18:14:51] Z reduction using 6 bytes of known plaintext

100.0 % (6 / 6)

[18:14:51] Attack on 880113 Z values at index 33

Keys: 98e0f009 48a0b11a c70f8499

2.4 % (20737 / 880113)

Found a solution. Stopping.

You may resume the attack with the option: --continue-attack 20737

[18:15:29] Keys

98e0f009 48a0b11a c70f8499



real	0m38.152s

user	4m35.318s

sys 0m0.897s

При правильном открытом тексте атака на ZipCrypto с известным открытым текстом работает быстро. Если вам когда-нибудь придётся делать что-то подобное, то стоит потратить время на размышления об открытом тексте. В частности, для изучения полезны файлы, отмеченные как Store в файле ZIP, поскольку они не сжаты и их содержимое предсказать может быть проще (вместо того, чтобы искать исходный файл и сжимать его, чтобы он соответствовал тому, что находится в ZIP).

Запускаем код


Я скомпилировал две программы и запустил их в DOSBox. Первая (RANDOM.BAS) использовалась для создания дисков со случайными числами, которые применялись в качестве одноразового шифра, а вторая (TECOD.BAS) использовалась для шифрования и дешифровки отправляемых по электронной почте сообщений. Скомпилированный код и сгенерированные исполняемые файлы можно найти в GitHub.

Для компиляции достаточно было запустить компилятор PowerBASIC следующим образом:

C:\>EXE\PBC TECOD.BAS PowerBASIC Compiler Version 3.00b Copyright (c) 1989-1993 by Robert S. Zale Spectra Publishing, Sunnyvale, CA, USA C:\TECOD.BAS 2575 statements, 2329 lines Compile time: 00:12.0 Compilation speed: 12600 stmts/minute 45984 bytes code, 4880 bytes data, 2048 bytes stack Segments(1): 46k C:\>EXE\PBC RANDOM.BAS PowerBASIC Compiler Version 3.00b Copyright (c) 1989-1993 by Robert S. Zale Spectra Publishing, Sunnyvale, CA, USA C:\RANDOM.BAS 2194 statements, 1940 lines Compile time: 00:10.1 Compilation speed: 12600 stmts/minute 33328 bytes code, 4704 bytes data, 3072 bytes stack Segments(1): 34k C:\>

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


Шифрование и дешифровка выполняются через TECOD.EXE, который защищён паролем.


Хотя пароль встроен в программу и достаточно прост, Тим Дженкин обфусцировал его следующим образом:

DIM PW$(PL)
PW$(9)=CHR$(66):PW$(4)=CHR$(66):PW$(1)=CHR$(84):PW$(5)=CHR$(79):PW$(2) = CHR$(73)
PW$(3)=CHR$(77):PW$(6)=CHR$(66):PW$(8)=CHR$(77):PW$(10)=CHR$(79):PW$(7)=CHR$(73)

В этой конкретной версии программы переход в главное меню выполняется после ввода пароля TIMBOBIMBO. Стоит отметить, что каждая версия этих программ передавалась разным членам АНК и имела разные пароли.


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

Вот скриншоты из трёх коротких видео, демонстрирующих создание случайных данных в RANDATA.1 для ключа при помощи RANDOM.EXE с последующим шифрованием сообщения, сохранённого в PLAIN.TXT на RAM-диске (все криптографические операции должны были происходить на RAM-диске), которое преобразовывалось в PLAIN.BIN (и наоборот). Сами видео можно посмотреть в оригинале статьи.

Создание случайных данных для использования в качестве ключа шифрования




Шифрование файла


Здесь программы (TECOD.EXE/TECOD.CNF) находятся на дискете A:, диск с данными (содержащий созданный выше файл ключа) находится на B:; также есть RAM-диск на R:. Чтобы всё это работало, файл RANDATA.1, созданный на предыдущем этапе, нужно переименовать в SNUM.


Дешифровка файла


Здесь программы (TECOD.EXE/TECOD.CNF) находятся на дискете A:, диск с данными (содержащий созданный выше файл ключа) на B:; также есть RAM-диск на R:. Файл RANDATA.1 должен называться RNUM на B:.


Есть ещё много интересных деталей работы этих программ, заслуживающих отдельного длинного поста. Например, материал для ключа уничтожается после использования, а программа RANDOM.EXE может создавать случайность разными способами; также есть код для проверки распределения созданных случайных байтов. При выполнении всех криптографических операций упор делается на использовании RAM-диска.

Комментарии (3)


  1. Vad344
    17.10.2024 09:43

    Тим Дженкин

    Какой интересный персонаж. Боролся с апартеидом, а после победы свалил из ЮАР.


    1. sintech
      17.10.2024 09:43

      С чего вы взяли?


      1. speedsmart_net
        17.10.2024 09:43

        если три раза прочитать

        в ЮАР