Перейти к содержанию

Внедряем бэкдор в RSA ключ

Здарова и с внеплановой пятницей!

Сегодня я покажу тебе как захуячить бэкдор в RSA ключ.

С чего все началось. Да блядь хуль мусолить, даж в официальных алгоритмах возможны бэкдоры.

Короче был такой алгоритм Dual_EC_DRBG стандартизованный национальным институтом стандартов и технологий пиндостана (NIST).

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

Алгоритм использует эллиптические кривые, и изначально был принят как безопасный криптографический генератор случайных чисел. Однако... что-то пошло по пизде.

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

Теоретически, если ты знаешь определённые параметры (точку Q, связанную с другой точкой P), можно предсказать выход генератора — то есть знать, какие случайные числа он генерирует.

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

А RSA Security использовало Dual_EC_DRBG по умолчанию. Причем АНБ забашляло 10кк баксов этой самой RSA Security за такую закладочку.

По итогу:

  • NIST отозвал рекомендацию по Dual_EC_DRBG
  • RSA все прокляли и доверие к ним пропало

Ну дак вот. Как бы тебе не ссали в уши — никому не верь!

Таких историй было несколько, но не суть.

Если интересно можешь загуглить сам. Там чет еще про роутеры помню было, где можно было восстановить приватный ключ из публичного и расшифровывать траффик. Чето про тайваньские чипы в 2013 году. Похуй!

Внедряем бэкдор в RSA

Но вы тут не байки пришли слушать, а позырить как это на практике провернуть. Поехали.

Ниже я накидал код на python:

import hashlib
from Crypto.Util import number
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.backends import default_backend
import base64

# Хешируем строку в SHA256, интерпретируем как BigInteger
def hash_to_bigint(data: str) -> int:
    if not isinstance(data, str):
        raise ValueError("data must be a string")
    hash_hex = hashlib.sha256(data.encode()).hexdigest()
    return int('1' + hash_hex, 16)

# Следующее простое
def next_prime(n: int) -> int:
    if n % 2 == 0:
        n += 1
    while not number.isPrime(n):
        n += 2
    return n

# Генерация RSA ключей с "бэкдором"
def generate_backdoored_key(secret: str):
    # Увеличиваем размер числа перед поиском простого
    p = next_prime(hash_to_bigint(secret + 'A') << 1024)
    q = next_prime(hash_to_bigint(secret + 'B') << 1024)

    n = p * q
    phi = (p - 1) * (q - 1)
    e = 65537
    d = pow(e, -1, phi)

    private_numbers = rsa.RSAPrivateNumbers(
        p=p,
        q=q,
        d=d,
        dmp1=d % (p - 1),
        dmq1=d % (q - 1),
        iqmp=pow(q, -1, p),
        public_numbers=rsa.RSAPublicNumbers(e=e, n=n)
    )
    private_key = private_numbers.private_key(backend=default_backend())
    public_key = private_key.public_key()

    pem_private = private_key.private_bytes(
        serialization.Encoding.PEM,
        serialization.PrivateFormat.TraditionalOpenSSL,
        serialization.NoEncryption()
    )
    pem_public = public_key.public_bytes(
        serialization.Encoding.PEM,
        serialization.PublicFormat.SubjectPublicKeyInfo
    )
    return pem_public.decode(), pem_private.decode()

# Восстановление приватного ключа по секрету и открытому ключу
def restore_private_key(secret: str, pem_public: str):
    public_key = serialization.load_pem_public_key(
        pem_public.encode(), backend=default_backend()
    )
    numbers = public_key.public_numbers()
    n = numbers.n
    e = numbers.e

    p = next_prime(hash_to_bigint(secret + 'A') << 1024)
    q = next_prime(hash_to_bigint(secret + 'B') << 1024)
    phi = (p - 1) * (q - 1)
    d = pow(e, -1, phi)

    private_numbers = rsa.RSAPrivateNumbers(
        p=p,
        q=q,
        d=d,
        dmp1=d % (p - 1),
        dmq1=d % (q - 1),
        iqmp=pow(q, -1, p),
        public_numbers=rsa.RSAPublicNumbers(e=e, n=n)
    )
    private_key = private_numbers.private_key(backend=default_backend())

    return private_key.private_bytes(
        serialization.Encoding.PEM,
        serialization.PrivateFormat.TraditionalOpenSSL,
        serialization.NoEncryption()
    ).decode()

# Демонстрация
secret = 'my-bashdays-secret'
pem_public, pem_private = generate_backdoored_key(secret)
print('--- PUBLIC KEY PEM ---')
print(pem_public)

# Шифрование
message = b'Hello, Bashdays world!'
public_key = serialization.load_pem_public_key(pem_public.encode(), backend=default_backend())
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
)
cipher_b64 = base64.b64encode(ciphertext).decode()
print('Encrypted (base64):', cipher_b64)

# Восстановление приватного ключа
pem_restored_private = restore_private_key(secret, pem_public)
print('--- RESTORED PRIVATE KEY PEM ---')
print(pem_restored_private)

# Расшифровка
private_key = serialization.load_pem_private_key(pem_restored_private.encode(), password=None, backend=default_backend())
decrypted = private_key.decrypt(
    base64.b64decode(cipher_b64),
    padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
)
print('Decrypted message:', decrypted.decode())

Если его запустить, он наглядно продемонстрирует всю подноготную.

Что делает этот скрипт

  • Создаёт RSA-ключи, которые можно восстановить из секрета (это и есть бэкдор).
  • Шифрует сообщение открытым ключом.
  • Восстанавливает закрытый ключ без сохранения — только из секрета и публичного ключа.
  • Расшифровывает сообщение этим восстановленным ключом.

Почему можно восстановить закрытый ключ, если знаешь секрет:

Если ты можешь восстановить те же p и q, то:

  1. Ты можешь пересчитать n = p * q — он должен совпасть с n в публичном ключе.
  2. Посчитать φ(n) = (p - 1)(q - 1)
  3. Получить e из публичного ключа (он в нём явно хранится)
  4. Вычислить d = e⁻¹ mod φ(n)
  5. Собрать весь приватный ключ

Смысл бэкдора

Секрет — это не просто строка, это способ воспроизводимо получить p и q. Ты как будто заранее знаешь, из чего они были сгенерированы.

Вот и вся наука! Изучай!

ㅤЧитать первым в Телеграм

Комментарии