Crypto

6-1 MatrixRsa

矩阵版的rsa

给出了p的高位,coppr先求出p

p = 9707529668721508094878754383636813058761407528950189013789315732447048631740849315894253576415843631107370002912949379757275
n = 132298777672085547096511087266255066285502135020124093900452138262993155381766816424955849796168059204379325075568094431259877923353664926875986223020472585645919414821322880213299188157427622804140996898685564075484754918339670099806186873974594139182324884620018780943630196754736972805036038798946726414009
PR.<x> = Zmod(n)[]
f = p*2**100 + x
res = f.small_roots(2**100, beta=0.5)
# [386875690473242360543194849663]

https://www.researchtrend.net/ijet/pdf/13%20%20Matrix%20Modification%20of%20RSA%20Public%20Key%20Cryptosystem%20and%20its%20Variant%20Manju%20Sanghi%203513.pdf

根据论文矩阵rsa的phi变成了
$$
g = (p^h-1)(p^{h}-p)...(p^h-p^{h-1})*(q^h-1)(q^{h}-q)...(q^h-q^{h-1})
$$
其中$h$为矩阵的维度,此题$h=3$

from Crypto.Util.number import *
import gmpy2
p = 
q = 
n = 
C = 
e = 65537
gp = (p**3-1)*(p**3-p)*(p**3-p**2)
gq = (q**3-1)*(q**3-q)*(q**3-q**2)
g = gp*gq
d = int(gmpy2.invert(e,g))
mm = C^d
long_to_bytes(int(mm[0][0]))
# b'DASCTF{48ccbfd88061d7ff'
long_to_bytes(int(mm[0][1]))
# b'3d5325148ec55d11}=!/f{a'

6-3 已悟

分为两部分

先看$choice=2$的部分,

输入$licensekey$和$license$

如果固定$licensekey$,$RicKV$生成的$subkeys$和$roundkey$是固定的,也就说$RicKV$的$flip$相同的数据后的结果是一致的。

构造$license$:当license_key = "00"时,假设第一组license=b"\x00"*8

l0=RicKV.flip(b"\x00"*8)是固定。此时经过一轮的下述操作l = l0

l = bytes(d^j for d,j in zip(l,liscense[i:i+8]))
l = self.flip(l)
l = bytes(d^j for d,j in zip(l,liscense[i:i+8]))

如果第二组license=l0, 在经过这一轮操作就会变成b"\x00"*8

l = bytes(d^j for d,j in zip(l,liscense[i:i+8])) # l = l0^l0
l = self.flip(l)                                 # l = flip(b"\x00"*8) = l0
l = bytes(d^j for d,j in zip(l,liscense[i:i+8])) # l = l0 ^ l0 = b"\x00"*8

这样就返回了True

再看$choice=1$部分,对$login$函数进行性能分析,x = profiler.get_stats().timings里面的元素返回的是$(函数行数,执行次数,开销)$,如果$pro=False$,则会隐藏掉执行次数。

$login$函数中会先判断长度是否一致,我们可以通过多次输入不同长度的数据,如果$hackerpro$的长度不为2(因为只会执行两行)则说明输入的长度正确

$login$函数中循环判断$password$的正确性,我们爆破不同的可见字符并填充到正确的长度,如果$hackerpro$

第四或五个元素的执行次数增加了,说明该字符正确

from pwn import *

re = remote("139.155.126.78", 23521)
re.recvuntil(b"> ")
re.sendline(b"2")
re.recvuntil(b"> ")
re.sendline(b"00")
re.recvuntil(b"> ")
re.sendline(base64.b64encode(b'\x00' * 8 + b'\xbe\x86}\xe2ba\x93\xb5'))
flag_length = 0
while True:
    re.recvuntil(b"> ")
    re.sendline(b"1")
    re.recvuntil(b"> ")
    re.sendline(b"0" * flag_length)
    hacker_pro = eval(re.recvline().strip().decode()[4:-2])
    if len(hacker_pro) == 2:
        flag_length += 1
    else:
        break
print(f"{flag_length = }")
flag = ""
for i in range(flag_length - 1):
    for s in string.printable:
        temp = flag + s
        re.recvuntil(b"> ")
        re.sendline(b"1")
        re.recvuntil(b"> ")
        re.sendline((temp.strip() + "0" * (flag_length - i - 1)).encode())
        try:
            rec = re.recvline().strip().decode()
            hacker_pro = eval(rec[4:-2])
            if hacker_pro[2][1] == i + 2:
                flag = temp
                print(flag)
                break
        except:
            # print(rec)
            break
re.interactive()

预制菜

[SNOW3G 与ZUC 流密码的猜测决定攻击

4287 (jos.org.cn)

DS

8-2 DSASignatureData

先从流量中含有userid的数据流,发生包含用户信息的数据到服务器,服务器成功接收会返回您发送的数据已接收!

给chat帮忙写出DSA验证签名的函数。

按顺序处理流量中的数据流,服务器成功接收到的保存并更新到字典中,再与签名的数据进行验签,不成功则输入到文件中。

100%正确率

import base64
import csv
import json
import os.path

from Crypto.Hash import SHA256
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from natsort import ns, natsorted


# 加载公钥
def load_public_key(pem_data):
    key = DSA.import_key(pem_data)
    return key


# 验证签名
def verify_signature(public_key, data, signature):
    # 将签名从 base64 解码为字节串
    signature = bytes.fromhex(signature)

    # 使用 SHA256 哈希算法生成数据的哈希
    h = SHA256.new(data.encode('utf-8'))

    # 创建 DSS 验证对象
    verifier = DSS.new(public_key, 'fips-186-3')

    # 验证签名
    try:
        verifier.verify(h, signature)
        return True  # 签名有效
    except ValueError:
        return False  # 签名无效


# 获取目录下文件
user_data = os.path.join('user_data')
user_data = os.listdir(user_data)
user_data = natsorted(user_data, alg=ns.PATH)
user_dict = {}
for idz in range(0, len(user_data), 2):
    file_path_req = os.path.join("user_data", user_data[idz])
    file_path_res = os.path.join("user_data", user_data[idz + 1])
    user_id = file_path_req.split("=")[-1].split("(")[0]
    user = json.loads(open(file_path_req, 'r').read())
    res = json.loads(open(file_path_res, 'r').read())
    if res["message"] == "您发送的数据已接收!":
        user_dict[user_id] = user
input_file = 'data-sign.csv'

with open(input_file, newline='', encoding='utf-8') as infile:
    reader = csv.DictReader(infile)
    # print(list(reader)[0])
    list_reader = list(reader)
    invalid_data = []
    for user_id, user in user_dict.items():
        # print(user_id, int(user_id)-1, user)
        row = list_reader[int(user_id) - 1]
        name = user["name"]
        name_signature = row['name_signature']
        idcard_signature = row['idcard_signature']
        phone_signature = row['phone_signature']
        pem_file = 'public/public-' + user_id.zfill(4) + '.pem'
        pem_key = load_public_key(open(pem_file, 'rb').read())
        if not verify_signature(pem_key, name, base64.b64decode(name_signature).hex()) or \
                not verify_signature(pem_key, user["idcard"], base64.b64decode(idcard_signature).hex()) or \
                not verify_signature(pem_key, user["phone"], base64.b64decode(phone_signature).hex()):
            user["userid"] = user_id
            invalid_data.append(user)
            # print(user_id, user)
with open("invalid_data.csv", mode='w', newline='', encoding='utf-8') as outfile:
    fieldnames = ['userid', 'name', 'idcard', 'phone']
    writer = csv.DictWriter(outfile, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(invalid_data)