ciscn2023 密码部分WP

密码1-基于国密SM2算法的密钥密文分发

按照附件写exp

# 自动发包
import requests
import json
import base64
import binascii
import urllib.parse as parse
from gmssl import sm2, func
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT

# curl -d "name=%E5%93%88%E5%93%88&school=%E5%AE%89%E5%BE%BD%E5%A4%A7%E5%AD%A6&phone=15958198820" http://192.168.31.153:3000/api/login
url = 'http://39.105.187.49:16435'

data = {
    'name': '1',
    'school': '1',
    'phone': '1'
}
# 对data进行url编码
data = parse.urlencode(data)  # import urllib.parse as parse
# print(data)

接口 = '/api/login'
res = requests.post(url + 接口, data=data)
json_data = json.loads(res.text)
data = json_data['data']
# print(data)
id = data['id']
print(id)
# 1,登录选手信息获取唯一id
# 把id发给 /api/allkey
接口 = '/api/allkey'

res = requests.post(url + 接口, data={'id': id})
# print(res.text)
json_data = json.loads(res.text)
data = json_data['data']

publicKey = data['publicKey']  # 选手SM2公钥
privateKey = data['privateKey']  # 选手SM2私钥
randomString = data['randomString']

# print('publicKey\n', publicKey)
# print('privateKey\n', privateKey)
# print('randomString\n', randomString)

# 将公钥传回
接口 = '/api/allkey'
# 2,发送选手SM2公钥到服务端,并获取服务端加密后的随机数密文、私钥密文和公钥明文
# 服务器使用国密SM2算法生成密钥对B(公钥B_Public_Key、私钥B_Private_Key)、使用量子随机数发生器产生16字节随机数C;服务器首先使用16字节随机数C对私钥B_Private_Key采用SM4ECB算法加密得到私钥B_Private_Key密文,然后使用A_Public_Key对16字节随机数C进行SM2加密得到随机数C密文;
res = requests.post(url + 接口, data={'id': id, 'publicKey': publicKey})
# print(res.text)
B公钥明文 = json.loads(res.text)['data']['publicKey']
随机数C对私钥B_Private_Key采用SM4ECB算法加密得到私钥B_Private_Key密文 = json.loads(res.text)[
    'data']['privateKey']
量子随机数发生器产生16字节随机数C密文 = json.loads(res.text)['data']['randomString']
# print('serverPublicKey\n', serverPublicKey)
# 用户使用私钥A_Private_Key,对随机数C密文进行SM2解密,获取16字节随机数C明文;
sm2_crypt = sm2.CryptSM2(
    public_key=publicKey, private_key=privateKey)
print('sm4密文_\n', 量子随机数发生器产生16字节随机数C密文)
# print('
# AttributeError: 'str' object has no attribute 'hex'
量子随机数发生器产生16字节随机数C密文 = binascii.a2b_hex(量子随机数发生器产生16字节随机数C密文)
量子随机数发生器产生16字节随机数C明文 = sm2_crypt.decrypt(量子随机数发生器产生16字节随机数C密文)
# 量子随机数发生器产生16字节随机数C明文 转为字节
量子随机数发生器产生16字节随机数C明文 = binascii.b2a_hex(量子随机数发生器产生16字节随机数C明文)
# 用户使用16字节随机数C明文,对私钥B_Private_Key密文,采用SM4ECB算法解密,得到私钥B_Private_Key明文;
print('量子c\n', 量子随机数发生器产生16字节随机数C明文)
sm4_crypt = CryptSM4()
sm4_crypt.set_key(量子随机数发生器产生16字节随机数C明文, SM4_DECRYPT)
私钥B_Private_Key明文 = sm4_crypt.crypt_ecb(
    随机数C对私钥B_Private_Key采用SM4ECB算法加密得到私钥B_Private_Key密文.encode('utf-8'))
私钥B_Private = binascii.b2a_hex(私钥B_Private_Key明文)
print('私钥B_Private\n', 私钥B_Private) # 这里发现解出的私钥有问题 有可能为空 猜想是否解密错误
# 用户向服务器请求密钥,服务器使用公钥B_Public_Key明文,对密钥D(16字节)采用SM2算法加密,将密钥D密文传输给用户;
check接口 = '/api/check'
# 不过在这里check的时候发现一直check错误。。。
# 尝试找漏洞 后面有的注释是第一次按文件写的时候写的

接口 = '/api/quantum'
res = requests.post(url + 接口, data={'id': id})
# print(res.text)
quantumString = json.loads(res.text)['data']['quantumString']
res = requests.post(url + check接口, data={'id': id, 'quantumString': quantumString})
# print('quantumString\n', quantumString)
# 用户使用私钥B_Private_Key明文,对密钥D密文进行解密,得到密钥D明文;
sm2_crypt2 = sm2.CryptSM2(
    public_key=B公钥明文, private_key=私钥B_Private)
quantumString = binascii.a2b_hex(quantumString)
quantumString = sm2_crypt2.decrypt(quantumString)
quantumString = binascii.b2a_hex(quantumString)
print('quantumString\n', quantumString)

# 用户将密钥D明文,上报至服务器进行验证,服务器返回参赛结果;用户可向服务器查询个人信息及参赛结果。

# 尝试非预期
quantumString = quantumString.decode('utf-8')
res = requests.post(url + 接口, data={'id': id, 'quantumString': quantumString})
print(res.text)
res = requests.post(url + 接口, data={'id': id, 'quantumString': quantumString})
print(res.text)

接口 = '/api/search'
res = requests.post(url + 接口, data={'id': id})
print(res.text)
quantumStringServer = json.loads(res.text)['data']['quantumStringServer']
print('quantumStringServer\n', quantumStringServer)
res = requests.post(url + check接口, data={'id': id, 'quantumString': quantumStringServer})
res = requests.post(url + 接口, data={'id': id})
print(res.text)

密码2-可信度量

在根目录下递归搜索包含字符串"flag"的文件,并将结果输出到标准输出,并过滤掉无权访问的行

grep -ra "flag" / 2>&1 | grep -v "Permission denied" | grep -v "denied$" --line-buffered

密码3-Sign_in_passwd

文件后缀改txt后(方便查阅) url解码第二行信息后base64换表 第一行作为密文 第二行作为表来解


密码4-badkey1

题面

from Crypto.Util.number import *
from Crypto.PublicKey import RSA
from hashlib import sha256
import random, os, signal, string

def 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 False
    return True

if 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 > 0
    assert q > 0
    assert p != q
    assert p.bit_length() == 512
    assert q.bit_length() == 512
    assert isPrime(p)
    assert isPrime(q)
    n = p * q
    e = 65537
    assert p % e != 1
    assert q % e != 1
    d = 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 flag
    print(flag)

按照 brealid 师傅的推导过程自己推了一遍

工作量证明 模板

import hashlib
import string
import itertools
from tqdm import tqdm
from pwn import *

# 遍历所有的XXXX,找到符合条件的XXXX
b = string.digits + string.ascii_letters + string.punctuation
strL = itertools.product(b, repeat=4)

ip = '47.93.187.243'
port = 39092

io = remote(ip, port, level='debug')
data = io.recvline()
# print(data) # b'sha256(XXXX+2ywKnEOsDsVMJXvP) == f8ffb979f1819dba9905c67b9b85874c681807f6e3cb2953901785764f5e8db8\n'
残缺的sha = data.split(b'+')[1].split(b')')[0]
sha256后的值 = data.split(b'== ')[1].strip()

hash_object = hashlib.sha256()
for i in strL:
    a = i[0] + i[1] + i[2] + i[3]
    sha = a + 残缺的sha.decode()
    # print(sha)
    hash_object.update(sha.encode())
    digest = hash_object.digest()
    hex_digest = digest.hex()
    if hex_digest == sha256后的值.decode():
        print(a)
        io.sendline(a.encode())
        break

io.interactive()

推导

brealid 师傅的exp

from Crypto.Util.number import *
from pwn import remote
import sys


e = 65537
while True:
    p = getPrime(512)
    m = inverse(e, p - 1)
    temp = (e * m * p - 1) // (p - 1)    # k(q - 1)
    for k in range(1, e + 1):
        if temp % k == 0:
            q = temp // k + 1
            if isPrime(q) and q.bit_length() == 512:
                print('[+] Found Answer')
                print('[+] p:', p)
                print('[+] q:', q)
                sys.exit(0)

完整exp

import hashlib
import itertools
from string import digits, ascii_letters, punctuation
from pwn import remote
from Crypto.Util.number import *
import sys
alpha_bet = digits+ascii_letters+punctuation
strlist = itertools.product(alpha_bet, repeat=4)

ip = 'node4.anna.nssctf.cn'
port = 28537

io = remote(ip, port, level='debug')
# sha256(XXXX+NxsXxwrH2UPagm3m) == 94641f7dff7a461fa93291607a6eb6eb0525f5173667150da77b95406503fc1a\n
data = io.recvline()
sha256后的值 = data.split(b'== ')[1].strip()
print('[+]sha256后的值: ', sha256后的值.decode())
残缺的sha = data.split(b'+')[1].split(b')')[0]
print('[+]残缺的sha: ', 残缺的sha.decode())

xxxx = ''
from time import sleep
from tqdm import tqdm
for i in tqdm(strlist):
    data = i[0]+i[1]+i[2]+i[3]
    data_sha = hashlib.sha256(
        (data+str(残缺的sha, encoding='utf-8')).encode('utf-8')).hexdigest()
    # print(data_sha)
    # print(data+str(tail,encoding='utf-8'))
    # sleep(10)
    if(data_sha == str(sha256后的值, encoding='utf-8')):
        xxxx = data
        break

print('[+]XXXX: ', xxxx)
io.sendline(xxxx.encode())
# io.recvall() #接收数据,直到达到EOF
# io.recv() #接收数据
# io.recvline() #接收一行数据
# io.recvuntil() # 接收到指定的内容后停止接收
# io.recvregex() # 接收到指定的正则表达式后停止接收
# io.recvrepeat() # 接收数据,直到超时

e = 65537

"""
check = False
while not check:
    p = getPrime(512)
    m = inverse(e, p - 1)
    temp = (e * m * p - 1) // (p - 1)    # k(q - 1)
    for k in range(1, e + 1):
        if temp % k == 0:
            q = temp // k + 1
            if isPrime(q) and q.bit_length() == 512:
                print('[+] Found Answer')
                print('[+] p:', p)
                print('[+] q:', q)
                check = True
                # io.recvuntil(b'p = ')
                # sleep(0.1)
                # io.sendline(str(p).encode())
                # sleep(0.1)
                # io.recvuntil(b'q = ')
                # # io.interactive()
                # sleep(0.1)
                # io.sendline(str(q).encode())
                # io.interactive() 
                # break
                sys.exit(0) # import sys
"""
# 不知道为什么用上面代码生成的pq直接传回大概率会报错(加了sleep也不行) 但是用预先生成好的pq就不会出错 有大佬知道原因的话麻烦告诉我一下
p = '8488801046431263229761805441065431376077145677248462075865125669622031730090796088901063330271972848555387039635408118509976679229257625394609078671892933'
q = '10075063365346849262583010629024489954242128039849695882991747768104067285864791682013796650024431727893076443223174478213078909720566236635993645492116989'
io.recvuntil(b'p = ')
io.sendline(p.encode())
io.recvuntil(b'q = ')
io.sendline(q.encode())


io.interactive()

PWN-login

考点是测信道(无附件 让输入pin) 比赛的时候没去看 可惜了