Plaid CTF 2013 (cyrpto)

Решения

http://scoding.de/plaidctf-2013-writeup-cyrpto/ (m4tze)
http://r3dey3.com/2013/04/plaidctf-crypto100/ (Kenny)
http://eindbazen.net/2013/04/pctf-2013-cyrpto-crypto-100/ (Eindbazen)
Назад к списку заданий

Задание (crypto)

image00.jpg

Подробное описание

Условия задачи:
"Один из нас изобрел новую криптосистему! Вы сможете ее сломать? Сервис client.py работает на 54.234.245.15:13797 или 54.235.50.140:13797".
Смотрим код client.py
Ага, алгоритм шифрования находится в функции encrypt(m, N), а зашифрованные данные в переменной ciphertext. Ясно, чтобы получить флаг, нам надо расшифровать данные из ciphertext. Теперь подключаемся к серверу. Сервер предлагает три варианта действий: зашифровать, расшифровать, показать открытый ключ.

nc 54.234.245.15 13797
Send 1 to encrypt, 2 to decrypt, 3 to get pubkey:

Если попробовать самый простой способ: послать серверу данные из переменной ciphertext, то он ответит незамысловато "..Nice try.." (хорошая попытка). Но если сервису указать другие данные, то процесс шифрования/расшифрования заработает, значит, на самом сервере стоит проверка на точное совпадение с шифрованным текстом из ciphertext. Вывод, надо обмануть службу и получить расшифрованные данные.

Изучаем алгоритм шифрования. Сообщение просто XORится с гаммой (ее длина L = 375 совпадает с длиной сообщения). Сама гамма создается в цикле: берется самый младший бит из преобразования:
y = y2 mod pubkey
Кстати, можно оценить длину сообщения: L / 8 примерно 47 байт (символов, флагом всегда выступает текстовое сообщение).
Замечаем, что отсутствуют какие-либо проверки на длину сообщения. Поэтому мы можем легко продлить цикл шифрования еще на n бит (n – может быть любым, конечно, проще взять 1 или значение кратное 8 бит) и получить новое значение ciphertext (тонкость в том, что после расшифровки мы получим исходное сообщение, сдвинутое на те же n бит). Вот и сам алгоритм.

image01.jpg

Выберем n = 8. Считаем новое значение ciphertext.

def encrypt_ext():
    c = ciphertext[0]
    y = ciphertext[1]
    L = ciphertext[2]
    L_ = L + 8
    for k in range(L, L_):
        c = c << 1
        y = pow(y, 2, pubkey)
    return (c, y, L_)

Новое значение ciphertext посылаем на сервер.

nc 54.234.245.15 13797
Send 1 to encrypt, 2 to decrypt, 3 to get pubkey: 2
c =489982736160181609215355328331541630598045351088622741149501263648392
7003221022122178567850390286780503383318583552
y = 84039037314514124249128065600130405633984200404171047165036590944723
27164353508300757639572042089353301346020442631051163707602106233004363
37742874565941006115272148137553352324536326369212946349871
L = 383
Here you go!
11585251310141253930862395587794192209166387936767084414500126477960241395
989561157460977131994123827546368473595209

Расшифрованное сообщение сдвигаем вправо на n = 8 бит.

def inttostr(s):
    if s == 0:
        return ""
    c = chr(s & 0xFF)
    return inttostr(s >> 8) + c
 
m_ = 115852513101412539308623955877941922091663879367670844145001264779602413
95989561157460977131994123827546368473595209
m_ = m_ >> 8
print("[+]Flag = "+inttostr(m_))

Результат:
[+]Flag = KEY{Cyprus_keylength(cm)_STILL_BEST_KEY_FORMAT}

Флаг:
Cyprus_keylength(cm)_STILL_BEST_KEY_FORMAT

Пока не указано иное, содержимое этой страницы распространяется по лицензии Creative Commons Attribution-ShareAlike 3.0 License