RSA Oracle - 使用选定的密文攻击获取标志

RSA Oracle - Getting the flag by using chosen ciphertext attack

提问人:Shark44 提问时间:8/16/2023 最后编辑:Shark44 更新时间:8/18/2023 访问量:373

问:

我正在尝试解决一个简单的 RSA CTF 挑战,但我面临的问题超出了攻击背后的理论(或者至少我猜是这样)。基本上,我有一个预言机可供使用,它将首先打印加密标志,然后加密和解密我想要的任何内容(除了标志的解密)。攻击的想法是加密 2*encrypted_flag然后解密给定的密码。通过将获得的数字除以 2,我应该得到标志。我是否遗漏了什么,在下面您可以找到预言机的代码和我的漏洞。

攻击思路是选择密文攻击,我从这个视频中“偷走”了方程式:https://www.youtube.com/watch?v=ZjYzrn8M3w4&ab_channel=BillBuchananOBE

Oracle 代码:

#!/usr/bin/env python3

import signal
from binascii import hexlify
from Crypto.PublicKey import RSA
from Crypto.Util.number import *
from random import randint
from secret import FLAG
import string

TIMEOUT = 300 # 5 minutes time-out

def menu():
    print()
    print('Choice:')
    print('  [0] Exit')
    print('  [1] Encrypt')
    print('  [2] Decrypt')
    print('')
    return input('> ')

def encrypt(m):
    return pow(m, rsa.e, rsa.n)

def decrypt(c):
    return pow(c, rsa.d, rsa.n)

rsa = RSA.generate(1024)
flag_encrypted = pow(bytes_to_long(FLAG.encode()), rsa.e, rsa.n)
used = [bytes_to_long(FLAG.encode())]

def handle():
  print("================================================================================")
  print("=                      RSA Encryption & Decryption oracle                      =")
  print("=                                Find the flag!                                =")
  print("================================================================================")
  print("")
  print("Encrypted flag:", flag_encrypted)

  while True:
    choice = menu()

    # Exit
    if choice == '0':
      print("Goodbye!")
      break

    # Encrypt
    elif choice == '1':
      m = int(input('\nPlaintext > ').strip())
      print('\nEncrypted: ' + str(encrypt(m)))

    # Decrypt
    elif choice == '2':
      c = int(input('\nCiphertext > ').strip())

      if c == flag_encrypted:
        print("Wait. That's illegal.")
      else:
        for no in used:
          if m % no == 0:
            print("Wait. That's illegal.")
            break
        else:
          print('\nDecrypted: ' + str(m))

    # Invalid
    else:
      print('bye!')
      break

if __name__ == "__main__":
    signal.alarm(TIMEOUT)
    handle()

我目前的方法:

from Crypto.Util.number import *
from math import gcd
import gmpy2
import sys
#sys.set_int_max_str_digits(0)
r = remote('oracle.challs.cyberchallenge.it', 9041)
r.recvuntil(b'Encrypted flag: ')
encrypted_flag = int(r.recvline().strip().decode())
e = 65537

# Let's first gather the ciphertext of the new num
"""
Here's another hint: suppose I encrypt 2. The oracle will give me back c2= pow(2, 65537, rsa.n). Now I can also compute 2**65537 as an integer. We know that 2**65537 - c2 is divisible by N. So we can try to factor 2**65537 - c2 using, say, the elliptic curve method (ECM). If we are incredibly lucky, 2**65537 - c2 = N * (bunch of relatively small primes), and after ECM finds all the small factors we'll be left with N. But, suppose, instead, that I also encrypt 3, so I get c3 = pow(3, 65537, rsa.n). And maybe even c5 = pow(5, 65537, rsa.n) How can I combine these to find rsa.n
"""

public_exponent = 65537
numbers = [2,3,4,5,6]
numbers_bytes = [b'\x02',b'\x03',b'\x04',b'\x05',b'\x06']
ciphers = []
diffs = []
for i in range(4):
    r.recvuntil(b'>')
    r.sendline(b'1')
    r.recvuntil(b'Plaintext > ')
    r.sendline(str(bytes_to_long(numbers_bytes[i])))
    r.recvuntil(b'Encrypted: ')
    cipher = int(r.recvline().strip().decode())
    ciphers.append(cipher)
    diffs.append(gmpy2.sub(pow(numbers[i], public_exponent),cipher))

print(diffs)
common_factor = None
for diff in diffs:
    if common_factor is None:
        common_factor = diff
    else:
        common_factor = gmpy2.gcd(common_factor, diff)
print(common_factor) 
#let's check whether the common factor is N
print(ciphers[0] == pow(bytes_to_long(b'\x02'), public_exponent, common_factor))
# We have found N if True
# To trick the decryption method just sum to the original ciphertext N once
print(common_factor)
encrypted_flag += int(common_factor)
r.recvuntil(b'>')
r.sendline(b'2')
r.recvuntil(b'Ciphertext > ')
r.sendline(str(encrypted_flag))
r.recvuntil('Decrypted: ')
flag = int(r.recvline().decode())
print(long_to_bytes(flag))
Python RSA BigInteger 数论 CTF

评论

0赞 President James K. Polk 8/16/2023
我错过了什么吗?我不知道,你还没有说明问题是什么。
0赞 Shark44 8/16/2023
我问你是否能看到我误以为我看不见的东西。我已经检查了代码,对我来说似乎很好,但我不明白为什么它不起作用。理论应该是正确的,所以我认为问题可能出在实现本身,但我无法弄清楚错误在哪里。我更新了问题,添加了有关该理论的一些细节。
1赞 President James K. Polk 8/16/2023
好吧,我认为你的方法不正确。至少,这不是我解决问题的方式,尽管可以有多种方法可以解决它。我会给你一些关于我如何解决它的提示。问题是你不知道 的值。 默认值为 65537,因此您知道这一点。您还知道加密标志的值。此外,一旦您知道并encrypted_flag,就有一种简单的方法可以规避您尝试解密加密标志的检查。因此,您有 2 个问题:找到然后规避检查。rsa.nrsa.ersa.nrsa.n
0赞 Shark44 8/17/2023
我试图弄清楚如何从方程中提取 N。我知道 C=(M**e)%N,但你怎么能从这个方程中提取 N?对于第二个问题,我可能有一个解决方案,即分解 N 并查看 2 个因子,要么 1 是 d,要么是 d,所以我们可以尝试用两者解密消息(不让预言机这样做)并检查哪一个给出标志。
0赞 President James K. Polk 8/17/2023
这是另一个提示:假设我加密了 2。神谕会把我还给我.现在,我还可以将 2**65537 计算为整数。我们知道 2**65537 - c2 可以被 N 整除。因此,我们可以尝试使用椭圆曲线法 (ECM) 来计算 2**65537 - c2 的因式分解。如果我们非常幸运,2**65537 - c2 = N *(一堆相对较小的素数),在 ECM 找到所有小因子后,我们将剩下 N。但是,假设我也加密了 3,所以我得到 c3 = .甚至可能 c5 = 我怎样才能将它们结合起来找到?c2= pow(2, 65537, rsa.n)pow(3, 65537, rsa.n)pow(5, 65537, rsa.n)rsa.n

答: 暂无答案