[CISCN 2023 初赛]badkey
task:
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
from hashlib import sha256
import random, os, signal, stringdef proof_of_work():random.seed(os.urandom(8))proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)])_hexdigest = sha256(proof.encode()).hexdigest()print(f"sha256(XXXX+{proof[4:]}) == {_hexdigest}")print('Give me XXXX: ')x = input()if len(x) != 4 or sha256(x.encode()+proof[4:].encode()).hexdigest() != _hexdigest:print('Wrong PoW')return Falsereturn Trueif not proof_of_work():exit(1)signal.alarm(10)
print("Give me a bad RSA keypair.")try:p = int(input('p = '))q = int(input('q = '))assert p > 0assert q > 0assert p != qassert p.bit_length() == 512assert q.bit_length() == 512assert isPrime(p)assert isPrime(q)n = p * qe = 65537assert p % e != 1assert q % e != 1d = inverse(e, (p-1)*(q-1))
except:print("Invalid params")exit(2)try:key = RSA.construct([n,e,d,p,q])print("This is not a bad RSA keypair.")exit(3)
except KeyboardInterrupt:print("Hacker detected.")exit(4)
except ValueError:print("How could this happen?")from secret import flagprint(flag)
analysis:
首先我们连接靶机之后,我们需要输入四位正常可显示字符,这四位字符与剩下的16位字符的hash值已经给定,所以第一步进行爆破即可.
第二部需要我们输入一组RSA加密的p,q并且有assert的限制条件,采用key = RSA.construct([n,e,d,p,q])后需要发生ValueError错误才能获取flag.通过搜索该函数:
排除调用 RSA.construct
前已进行的检查,最有可能导致失败的检查是
if Integer(n).gcd(d) != 1:raise ValueError("RSA private exponent is not coprime to modulus")
\[\begin{flalign}
&n=pq\quad and\quad gcd(n,d)!=1\Longrightarrow gcd(n,d)=p\\
&设d=k_1p,ed=ek_1p=ek_1+ek_1(p-1)\equiv1(mod\ (p-1)*(q-1))\Longrightarrow ek_1\equiv1(mod\ p-1).\\
&k_1=e^{-1}(mod\ p-1).ed = k_2(p-1)*(q-1),k_2(q-1)={ek_1p\over p-1}\Longrightarrow q={ek_1p\over k_2(p-1)}+1.\\
&k_2是整数,且q.bit\_length()=512,则在range(1,2^{{ek_1p\over p-1}.bit\_lehnth()-512})的范围进行爆破寻找能让RSA.construct产生ValueError错误的p,q即可.&
\end{flalign}
\]
exp:
# -*- coding: utf-8 -*-
# @Author : chen_xing
# @Time : 2024/12/24 下午4:35
# @File : badkey.py
# @Software: PyCharm
from pwn import *
import temp
# getpq
p,q = temp.getpq()
# hash_pop
io = remote('node4.anna.nssctf.cn',28788)
data = io.recvline().decode().strip().split(') == ')
result = data[1]
known = data[0][-16:]
io.sendline(temp.hash_pop(result,known))
io.sendlineafter(b'p = ', str(p))
io.sendlineafter(b'q = ', str(q))
io.interactive()
"""
-------------------------------getpq-begin-------------------------------
--------------------------------getpq-end--------------------------------
getpq time is: 16.541666269302368
-------------------------------hash_pop-begin-------------------------------
--------------------------------hash_pop-end---------------------------------
hash_pop time is: 44.73846364021301
How could this happen?
NSSCTF{971a423e-8f97-4f98-b7ee-e2d5eff4c2fb}
"""
"""
temp.py
import hashlib
from Crypto.Util.number import *
from time import *
def hash_pop(result,known):print("-------------------------------hash_pop-begin-------------------------------")start_time = time()for i in range(32,127):for j in range(32,127):for p in range(32,127):for q in range(32,127):temp = str(chr(i)) + str(chr(j)) + str(chr(p)) + str(chr(q)) + knownif hashlib.sha256(temp.encode('utf-8')).hexdigest() == result:end_time = time()print("--------------------------------hash_pop-end---------------------------------")print(f"hash_pop time is: {end_time - start_time}")return temp[:4:]
def getpq():print("-------------------------------getpq-begin-------------------------------")e = 65537start_time = time()while True:p = getPrime(512)d = inverse(e,p - 1)temp = (e * d * p - 1) // (p - 1)for k in range(1,2**(temp.bit_length() - 512)):if temp % k == 0:q = temp // k + 1if isPrime(q) and q.bit_length() == 512:end_time = time()print("--------------------------------getpq-end--------------------------------")print(f"getpq time is: {end_time - start_time}")return p,q
"""