Доброго времени суток!
Хочу поделиться неожиданным открытием, а точнее — рассказать о необычном поведении довольно известного архиватора Zip.
Мне нужно было сделать проект на свободную тему по курсу «Защита информации» в университете. Наша команда решила создать приложение на Python, которое, если смотреть в первом приближении, простым перебором открывало бы файл, заархивированный с паролем.
Сначала мы тестировали приложение на файлах, сжатых с помощью Zip.
- Архивируем файл:
$ zip --password 123 secure.zip README.md
- Проверяем вручную, что с этим паролем все работает:
$ unzip secure.zip
- Удаляем тот файл, который был сгенерирован в процессе проверки для чистоты эксперимента;
- Ждем несколько томительных секунд;
- Получаем ожидаемый вывод нашей программы:
Success!123
Нашему счастью не было предела, программа работает, все взламывает, можно идти дальше и добавлять новые интересные «фичи», красивый интерфейс. Но далеко мы не убежали, потому что ген заядлого тестировщика сидит в нашей крови с рождения, а значит — мы не могли поверить, что программа корректна, пока она не доказала это на всех придуманных нами сложнейших паролях.
- Архивируем файл:
$ zip --password 2048 secure.zip README.md
- И снова мучительное ожидание;
- Получаем вывод нашей программы:
Success!278
Стоп! Получили не совсем ожидаемый результат. Что делать? Анализировать, распутывать, исправлять. План был просто прекрасным, но уже на этапе «анализировать» он пошел прахом.
Первым делом мы решили исключить вероятность того, что каким-то образом наша программа действительно взломала файл с тем паролем, который вывела. И вот оно! Вот оно разочарование: этот пароль оказался валидным. Как?!
Но больше нас удивило то, что и предыдущий пароль тоже оказался подходящим.
Теперь поговорим о грустном. Пока что эта загадка не была разгадана. Мы проверили наличие этого эффекта на нескольких операционных системах (Fedora 20 (Heisenbug), Ubuntu 14.04, OS X 10.9.4); на каждой из них он воспроизводится. Вывод, который мы можем сделать, тоже неутешительный: пользоваться этим архиватором в каких-то сколь-нибудь ответственных целях не стоит.
Ниже привожу исходный код скрипта, чтобы читатель смог самостоятельно ознакомиться с эффектом:
import threading as th
import pexpect as pe
#Derive a class for incapsulating a thread
#which will be trying to open the encrypted file.
class HackerThread(th.Thread):
def __init__(self, remainder, np, event, filename):
super(HackerThread, self).__init__()
self._remainder = remainder
self._np = np
self._event = event
self._filename = filename
def run(self):
effort = self._remainder
while 1:
print(effort)
if self._event.isSet():
quit()
child = pe.spawn('unzip '+self._filename)
try:
child.expect('password:', timeout=2)
except pe.ExceptionPexpect as ex:
#print ex.message
print "Didn't get password prompt!"
self._event.set()
quit()
child.sendline(str(effort))
try:
child.expect('reenter:', timeout=2)
except pe.ExceptionPexpect as ex:
print "Success!"+str(effort)
self._event.set()
quit()
effort += self._np
if __name__ == '__main__':
np = 4
filename = "secure.zip"
event = th.Event()
threads = [ HackerThread(i, np, event, filename) for i in range(np) ]
for thread in threads:
thread.start()
for thread in threads:
thread.join(timeout=None)
Примечание: для теста просто создайте в директории, в которой лежит скрипт, запароленный файл «secure.zip» и запустите скрипт интерпретатором питона 2.7.
Автор: mshimche