密码学实战 - HTB YAML

992 阅读1分钟

概述

YAML是来自于HTB(hackthebox.com)的一个中级密码学挑战,完成该挑战所需要掌握的知识点在于RSA算法。

题目分析

相关的任务文件包括server.py源代码和一个在线环境。

server.py内容节选如下

class YALM:

    def __init__(self):
        self.e = 3
        self.n = N

    def get_secret(self):
        message = f'Hey! This is my secret... it is secure because RSA is extremely strong and very hard to break... Here you go: {FLAG}'
        m = int(message.encode().hex(), 16)
        c = pow(m, self.e, self.n)

        return hex(c)

    def test_encryption(self):
        plaintext = input('Plaintext: ').strip()

        m = int(plaintext, 16)
        cs = []

        while m:
            c = pow(m, self.e, self.n)
            cs.append(c)
            m //= self.n

        if len(cs) > 1:
            return 'Too many messages!'

        return 'Thanks for the message!'


def main():
    yalm = YALM()

    while True:
        print(MENU)

        option = int(input('Option: '))

        if option == 1:
            print(f'Ciphertext: {yalm.get_secret()}')
        elif option == 2:
            print(yalm.test_encryption())
        else:
            break

以上代码实现了一个基本的RSA加密算法,已知数据包括e和密文,未知的则是n。 在线环境提供两个输入选项,选项1给出密文, 选项2允许输入一个明文,其输出的结果可以用于判断明文与n的大小关系。

解题过程

首先使用选项2使用二分查找法获取n

from pwn import remote

conn = remote('134.122.103.40', 31566, level = 'info')

conn.recvuntil("Option: ")

conn.sendline("1")

output = conn.recvline()

print("output =", output)

low = 0
high = pow(2, 2048)
mid = 0

test_count = 0

while low <= high:
  mid = (high + low) // 2

  conn.recvuntil("Option: ")

  conn.sendline("2")
  
  conn.recvuntil("Plaintext: ")
  
  conn.sendline(hex(mid))
  
  line = conn.recvline()
   
  if b"Too many messages!" in line:
    high = mid - 1
    print("change high to ", hex(high))
  else:
    low = mid + 1
    print("change low to ", hex(low))
  
    
print("Search finised with low = ", hex(low))

n = low

得到n之后,进行解密,因为明文前面的一大部分已知,因此我们可以使用Stereotyped Message Attack, 由于FLAG的长度未知,因此使用一个循环进行遍历。

from Crypto.Util.number import bytes_to_long, long_to_bytes 

e = 3

c = 0x8ce7a727c4a70470aa3d6b872f82ef26c8ff5c7820d5790aa53e9dbd1d9f328c8847b268813c9c1bca54c3a892c9a848e95f37bb3c467971af3a29a2ea706dde662caa595728ff094b6c3c66bdddc6733f428c5b80ef81c0dbfa7f4419f08cc6ce7cde30df2004ff8037baf3647cf1813c577ca1303fb92f319418e3ed4f36dc49d33e7d92471a53ae2c029cdfa2b9034a6cb8f3b468f9154a6755aff13d923bd7e6d49d2e8db3b34b6135675d1a11236e7c8641716c54fe91dc26677200232baae8a9d5293109d73336f239d9a8905c7a1b81aec57d3df55f3302c0cddbcd742ea302c1157fa7b1138b36acd82cd73142b4fbf26099153616cd0d244dea2e1c

for flag_length in range(16, 64): 
  
  message = b"Hey! This is my secret... it is secure because RSA is extremely strong and very hard to break... Here you go: " + b'\x00' * flag_length
  
  m = bytes_to_long(message)
  
  P.<x> = PolynomialRing(Zmod(N), implementation='NTL')
  pol = (m + x)^e - c
  roots = pol.small_roots(epsilon=1/30)
  if (len(roots) > 0):
    print("Potential solutions:")
    for root in roots:
      print("message =", long_to_bytes(m + int(root)))