roger 发表于 2020-2-26 17:43:41

TetCTF 2020-yaecc

题目描述如下:Who knows the backdoor wins!
nc 207.148.119.58 7777审计代码可知,题目模拟了一版ECDSA(NIST-p256 曲线)的实现,我们的任务是根据若干消息/签名对来求私钥,为了便于描述我们先描述一下本题用到的符号系统:(R,S):签名
a:私钥
k:nonce
H:msg的哈希值
N:曲线的点群阶在ECDSA中,有如下表达式成立:R*a − S*k + H ≡ 0 (mod N)根据ECDSA算法,当对消息进行签名时,nonce应当随机均匀的从区间CVP问题。假设我们已经收集了D对消息/签名对,此时有:Ri*a − Si*ki + Hi ≡ 0 (mod N) i=1,2,3,...,D等式两边乘上Si的逆,得:(Si^(-1))*Ri*a − ki + (Si^(-1))*Hi ≡ 0 (mod N)设Ai = (Si^(-1))*Ri,Bi = -(Si^(-1))*Hi,整理得:Ai*a ≡ Bi + ki (mod N)即:Ai*a - li*N = Bi + ki (li为整数)将方程用向量形式表示如下:其中(1/2**16)是根据256(a的比特数)-240(k的比特数)= 16计算而来。对于每一个表达式Ri*a − Si*ki + Hi ≡ 0 (mod N)来讲,一对消息/签名对就可以提供私钥的16比特的信息,因此理论上来讲D(即消息/签名对的对数)= 256/16 = 16即可,在写脚本时我们收集的数量比这一理论值略多一些即可。将上述推导写成SageMath代码形式如下:from sage.all import *
from pwn import *
from hashlib import sha256
import os
from Crypto.Util.number import bytes_to_long

EC = EllipticCurve(
    GF(0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff),
    [-3, 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b]
)
n = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
Zn = Zmod(n)
G = EC((0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,
      0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5))
P = G
Q = EC((0xc97445f45cdef9f0d3e05e1e585fc297235b82b5be8ff3efca67c59852018192,
      0xb28ef557ba31dfcbdd21ac46e2a91e3c304f44cb87058ada2cb815151e610046))
pubkey = EC((50590195252452518804028762265927043036734617153869060607666882619539230027822,
      36611353725619757431858072740028832533174535444901899686884685372270344860185))

class DualEcDrbg(object):
    def __init__(self, seed):
      self.s = ZZ(bytes_to_long(seed))

    def next_bits(self):
      self.s = ZZ((self.s * P))
      return ZZ((self.s * Q)) & (2 ** 240 - 1)

def sign(private_key, message, rand):
    z = Zn(ZZ(sha256(message).hexdigest(), 16))
    k = Zn(rand.next_bits())
    assert k != 0
    K = ZZ(k) * G
    r = Zn(K)
    assert r != 0
    s = (z + r * private_key) / k
    assert s != 0
    return r, s

data = []
for _ in range(50):
    s = remote("207.148.119.58", 7777)
    m = eval(s.recvline())
    sig = eval(s.recvline())
    data.append((m, sig))
    s.close()
print len(data)
print data
with open("data.txt", "w") as f:
    f.write(str(data))

size = 20
m = []
Ai = [-1]
Bi =
r0, s0 = map(Zn, data)
z0 = Zn(ZZ(sha256(str(data)).hexdigest(), 16))
for i in range(size):
    message, sig = data
    ri, si = map(Zn, sig)
    zi = z = Zn(ZZ(sha256(str(message)).hexdigest(), 16))
    A = - (s0 * ri) / (r0 * si)
    B = (z0 * ri) / (si * r0) - zi / si
    Ai.append(A)
    Bi.append(B)

Ai.append(0)
Bi.append(n//2^16)
m.append(Ai)
for i in range(size):
    m.append(*(i+1) + + *(size-i))

m.append(Bi)
m = Matrix(ZZ, m)

mLLL = m.LLL()

for irow, row in enumerate(mLLL):
    k0 = Zn(row)
    d = (s0*k0-z0)/r0
    if pubkey == ZZ(d)*G:
      print d
      break
    k0 = Zn(-row)
    d = (s0*k0-z0)/r0
    if pubkey == ZZ(d)*G:
      print d
      break

msg2 = b"I am admin"
rand = DualEcDrbg(os.urandom(16))
sig = sign(ZZ(d), msg2, rand)
s = remote("207.148.119.58", 7777)
s.sendline(str(sig))
ret = s.recvline()
print ret执行脚本即可得到flag:TetCTF{_0oops____Sm4ll_k_ru1n3d_th3_p4rty_}

monkeyman 发表于 2020-3-1 20:20:08

支持学逆向论坛,资源不错!
页: [1]
查看完整版本: TetCTF 2020-yaecc