前言:
很有幸可以参加这次的长城杯初赛,第三赛区多少少有点太难了(疯狂吐槽),密码写出来了两题,一题签到,一题rsa。
normalrsa
给了一个二进制文件,其实就是c,另一个文件是乱码,都导入到工具箱中可以直接得到n,c,e,然后尝试着分解n,结果真出来了,那么flag就出来了。
(就最后一点点)
challange
是一道很明显的e过大和类似于连分数类型的题目,可以去参考这个师傅博客里面的内容https://lazzzaro.github.io/2020/05/06/crypto-RSA/,当然也可以去nssctf里面去看,里面有讲解和配对的习题。
————————————
题目
#!/usr/bin/env python3
from Crypto.Util.number import *
from math import gcd
def keygen(nbit, dbit):
assert 2*dbit < nbit
while True:
u, v = getRandomNBitInteger(dbit), getRandomNBitInteger(nbit // 2 – dbit)
p = u * v + 1
if isPrime(p):
while True:
x, y = getRandomNBitInteger(dbit), getRandomNBitInteger(nbit // 2 – dbit)
q = u * y + 1
r = x * y + 1
if isPrime(q) and isPrime(r):
while True:
e = getRandomNBitInteger(dbit)
if gcd(e, u * v * x * y) == 1:
phi = (p – 1) * (r – 1)
d = inverse(e, phi)
k = (e * d – 1) // phi
s = k * v + 1
if isPrime(s):
n_1, n_2 = p * r, q * s
return (e, n_1, n_2)
def encrypt(msg, pubkey):
e, n = pubkey
return pow(msg, e, n)
nbit, dbit = 1024, 256
e, n_1, n_2 = keygen(nbit, dbit)
FLAG = int(FLAG.encode(“utf-8”).hex(), 16)
c_1 = encrypt(FLAG, (e, n_1))
c_2 = encrypt(FLAG, (e, n_2))
print(‘e =’, e)
print(‘n_1 =’, n_1)
print(‘n_2 =’, n_2)
print(‘enc_1 =’, c_1)
print(‘enc_2 =’, c_2)
# e = 98467641690093871695975267875795181585857989477939466388009417059131670850127
# n_1 = 64521998900946151035183250161361578590562898382852517840849876271081392030031478373013958266147661124818863160595707167944931647973532540982401464973584021447422475868642097572419813003671216480805967701734701439164191640156357221192923526468945493032975925992013636078779749848731628561181164769185524770589
# n_2 = 36054324863568917511130648763027165052886430166527588098384118258948926960785865728094271975577562345932247144759061359056537900829252352901512813929996631705102975455228713114838828313022933493627416617967097921738345054995913178996631066725890217631952233116357709092349310952226981239074912741833028864549
# enc_1 = 44409530711611323971318548112765117429254234773113032975079541538836074541902571432094967280311956244304196918788882147751568079628816573837227196431565600487906430653102753682987284091056610196645602610743599718659750856474574422459340904042648646992537529462261441494430867623023213656814387251664662129314
# enc_2 = 4839505804094209210134989537374676295205076918332316790821587122796708880477883386797213308650255056165174482638715054174457192839650288569888079975883348512160642182072963895694733760983833154108923043128161463710202982360661381008676286064498611857732239429580056063695889933346958696980759190313056536538
————————————————
分析
首先最基础的就是 p = u * v + 1,q = u * y + 1,r = x * y + 1,s = k * v + 1,那么就可以知道phi=(p-1)*(r-1)=u*v*x*y,然后这个题目一共给了两组n,那么我们只需要通过一组就可以去求出flag了。知道n2=q*s,n1=p*r,那么n2/n1=q*s/p*r=(u * y + 1)*( k * v + 1)/(u * v + 1)*(x * y + 1)≈uykv/uvxy=k/x (类似于连分数),一个1024位,一个256位,直接看出来wiener攻击的话就不会陌生的,直接去敲代码就行了。
————————————————
exp
decrypt
题目
import random
import socketserver
import codecs
import os
import hashlib
from Crypto.Cipher import AES
from flag import flag, imessage
import signal
key = os.urandom(16)
lists = “ABCDEFGHIJKLMNOPQRSTUVWXY0123456789”
class MyCBC(object):
def encrypt(self, plaintext):
cipher_bytes = codecs.decode(plaintext, “hex”)
encrypt_plain = AES.new(key, AES.MODE_CBC, key).encrypt(cipher_bytes)
return codecs.encode(encrypt_plain, “hex”).decode()
def decrypt(self, ciphertext):
cipher_bytes = codecs.decode(ciphertext, “hex”)
decrypt_cipher = AES.new(key, AES.MODE_CBC, key).decrypt(cipher_bytes)
return codecs.encode(decrypt_cipher, “hex”).decode()
class MyCFB(object):
def encrypt(self, plaintext):
cipher = AES.new(key, AES.MODE_CFB, key, segment_size=8 * 16)
ciphertext = cipher.encrypt(codecs.encode(plaintext, ‘utf-8’))
return codecs.encode(ciphertext, “hex”).decode()
def decrypt(self, ciphertext):
cipher_hex = codecs.decode(ciphertext, “hex”)
cipher = AES.new(key, AES.MODE_CFB, key, segment_size=8 * 16)
plaintext_bytes = cipher.decrypt(cipher_hex)
plaintext = codecs.decode(plaintext_bytes, ‘utf-8′, errors=’ignore’)
return plaintext
class ForkServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle_timeout(self, signum, frame):
raise TimeoutError
def MyCheck(self):
list_result = ”.join(random.choices(lists.lower(), k=16))
hash_result = hashlib.md5(list_result.encode()).hexdigest()
self.request.sendall(f”hash_result: {hash_result}\nre_list: keys + {list_result[4:]}\n”.encode())
self.request.sendall(f”input keys:”.encode())
plain_text = self.request.recv(1024).strip().decode()
if len(plain_text) != 4 or hashlib.md5((plain_text + list_result[4:]).encode()).hexdigest() != hash_result:
self.request.sendall(f”error\n”.encode())
return False
return True
def handle(self):
try:
signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(60)
if not self.MyCheck():
return
signal.alarm(120)
self.request.sendall(
“Choose an option:\n1. Decrypt\n2. Encrypt\n3. Get message\n4. Check message\n”.encode())
user_choice = self.request.recv(1024).strip().decode()
if user_choice == “1”:
self.request.sendall(“Enter text to decrypt: “.encode())
cipher_text = self.request.recv(1024).strip().decode()
decrypted_text = MyCBC().decrypt(cipher_text)
self.request.sendall(f”Decrypted text: {decrypted_text}”.encode())
elif user_choice == “2”:
self.request.sendall(“Enter text to encrypt: “.encode())
plain_text = self.request.recv(1024).strip().decode()
encrypted_text = MyCBC().encrypt(plain_text)
self.request.sendall(f”Encrypted text: {encrypted_text}”.encode())
elif user_choice == “3”:
self.request.sendall(“Get message: “.encode())
key_text = self.request.recv(1024).strip().decode()
user_key_bytes = codecs.decode(key_text, “hex”)
if user_key_bytes == key:
self.request.sendall(f”message text: {imessage}”.encode())
else:
self.request.sendall(f”error”.encode())
elif user_choice == “4”:
for i in range(24):
self.request.sendall(“imessage: “.encode())
message_hex = self.request.recv(1024).strip().decode()
if len(message_hex) > 2048:
self.request.sendall(“too long: “.encode())
break
if len(message_hex) % 2 != 0:
self.request.sendall(“Invalid imessage length.”.encode())
break
plain_text = MyCFB().decrypt(message_hex)
self.request.sendall(f”imessage text: {plain_text}\n”.encode())
if plain_text == imessage:
self.request.sendall(f”flag: {flag}”.encode())
break
else:
self.request.sendall(“Invalid choice. Closing connection.”.encode())
except TimeoutError:
self.request.sendall(“\nConnection refused”.encode())
self.request.close()
except Exception as err:
print(err)
finally:
self.request.close()
if __name__ == “__main__”:
HOST, PORT = “0.0.0.0”, 10000
print(HOST, PORT)
with ForkServer((HOST, PORT), MyTCPHandler) as server:
server.serve_forever()
————————————————
比赛的时候并没有做出来,和交互环境第一开始没有配置好有关,后面用了我们队伍pwn手的电脑才开始去写。
————————————————
分析
首先就是这玩意的nc链接端口是10000,额,怎么说呢,直觉,或者说是上面就这一个长得像端口的数,然后整体的代码采用了基于TCP服务器的加密(TCP可以直接忽略),首先咱们要通过check这个检查代码,然后才能进入到下一步的输入1,2,3,4去和他进行加密,解密,生成message和求出flag的操作。
1.通过check
首先可以先尝试往里面输入点随意的数,然后发现返回的都是flag+什么什么的,同时咱们只要满足hashlib.md5((plain_text + list_result[4:]).encode()).hexdigest() != hash_result:这个要求就可以,那么就直接取决于我们的后四位随机字符,那就只要尝试爆破这个就行了
2.然后不会了,等官方wp出来后更新吧
🐮