【CTF WriteUp】2020全国工业互联网安全技术技能大赛(原护网杯)Crypto题解

Crypto

signsystem

题目中的加密算法很奇怪,所以测试一下加密算法的结果,得到如下结论:

在不模n的情况下,该函数加密结果满足以下数列:

f(1) = m, f(2) = m^2-2, f(n+2) = m*f(n+1) - f(n)

列出几项观察:

m, m^2-2, m^3-3m, m^4-4m^2+2, m^5-5m^3+5m, ...

暂时没有得到有用结论。再看一看e和d的关系,随便找了100个输入,确认e和d在该算法可逆,原理不明。

继续看代码。代码允许输入一个明文m计算enc(m, d, n),但m不能是secret。注意到允许输入负数,因此输入负secret时,也能得到一个签名sign。根据数列的特性,当m变为-m时,其奇数项变负,偶数项保持不变,因此尝试直接用sign或者-sign当作sig进行签名。结果发现当输入-sign时签名成功,即第e(奇数)项取负值。完整解题代码如下:

#!/usr/bin/env python
# coding:utf-8
from pwn import *
import hashlib
import string
p = remote("39.107.252.238", 10093)

def myhash(text):
    mysha = hashlib.sha256()
    mysha.update(text)
    return mysha.hexdigest()

def passPoW(plain, cipher):
    dic = string.digits+string.ascii_letters
    for a0 in dic:
        for a1 in dic:
            for a2 in dic:
                for a3 in dic:
                    tmp = a0+a1+a2+a3+plain
                    if(myhash(tmp)==cipher):
                        return a0+a1+a2+a3

def encrypt(m,e,N):
    if e == 0:
        return 2
    t1 = 2
    t2 = m
    e = bin(e)[3:]
    for i in e:
        tk  = (t2*t1 - m)%N
        sk = (t2*t2 - 2)%N
        rk = (m*t2*t2-t2*t1-m)%N
        if i == '0' :
            t2 = sk
            t1 = tk
        else:
            t2 = rk
            t1 = sk
    return t2

# pass the PoW
recv = p.recvline()
print recv.strip()
plain = recv[12:28]
cipher = recv[33:97]
res = passPoW(plain, cipher)
print p.recvuntil("Give me XXXX:")
p.sendline(res)

recv = p.recvline()
print recv.strip()
recv = p.recvline()
print recv.strip()
n = int(recv.strip()[22:])
recv = p.recvline()
print recv.strip()
secret = int(recv.strip()[14:])
e = 65537

print p.recvuntil("Tell me the plaintext:")
p.sendline(str(-secret))
recv = p.recvline()
print recv.strip()
result = recv.strip()[17:]
print p.recvuntil("Tell me the plaintext:")
p.sendline("0")
print p.recvuntil("Tell me the secret's signature and I will give you the flag.")
p.sendline("-"+result)
p.interactive()

在这里插入图片描述
flag{1890a3a7-33fe-4370-abe5-3511fdf6b0a0}

dislogAgain

(折腾了半天,最后才想到查一查dislog什么意思,嗯,离散对数- -。再一看g已知,c已知,p可求,真的是离散对数。于是又掉进了离散对数坑。。。)

求p、q
因为q=gmpy2.next_prime(p ^ 3),所以直接将n开5次方,往回找就能找到p

#!/usr/bin/env python
# coding:utf-8
import gmpy2

g = ...
n = ...
c = ...

p, q = 0, 0
s = int(gmpy2.iroot(n,5)[0])
while True:
    while not gmpy2.is_prime(s):
        s -= 1
    if(n%s==0):
        p = s
        q = n //(p**2)
        break

print p
print q

得到p,q后,网上查到该加密算法为Okamoto-Uchiyama 密码系统,利用维基百科上的解密代码求解。

#!/usr/bin/env python
# coding:utf-8
import gmpy2
from libnum import n2s

g = ...
n = ...
c = ...

p, q = 0, 0
s = int(gmpy2.iroot(n,5)[0])
while True:
    while not gmpy2.is_prime(s):
        s -= 1
    if(n%s==0):
        p = s
        q = n //(p**2)
        break

# print p
# print q
a = (pow(c, p-1, p*p) - 1) // p
b = gmpy2.invert((pow(g, p-1, p*p) - 1) // p, p)
print n2s((a * b) % p)

flag{8bc0de2f-0995-40e2-9c33-83aa96d618e4}

2EM

注意到,题目代码中的所有运算都是异或与换位,也就是说,如果我们将明文m的二进制表示视作32个不同的变量的话,可以列出一个多元一次方程组。例如以下参数:

pbox1 = [22, 28, 2, 21, 3, 26, 6, 14, 7, 16, 15, 9, 17, 19, 8, 11, 10, 1, 13, 31, 23, 12, 0, 27, 4, 18, 30, 29, 24, 20, 5, 25]
pbox2 = [17, 6, 7, 27, 4, 20, 11, 22, 2, 19, 9, 24, 23, 31, 15, 10, 18, 28, 5, 0, 16, 29, 25, 8, 3, 21, 30, 12, 14, 13, 1, 26]

设明文m为m[0]-m[31]
密钥k为k[0]-k[31]

第一次异或key后的32位:
m[0] ^ k[0], m[1] ^ k[1], …

按照pbox1变换:
m[22] ^ k[22], m[28] ^ k[28], …

第二次异或key:
m[22] ^ k[22] ^ k[0], m[28] ^ k[28] ^ k[1], …

按照pbox2变换:
m[1] ^ k[1] ^ k[17], m[6] ^ k[6] ^ k[6], …

第三次异或key:
m[1] ^ k[1] ^ k[17] ^ k[0], m[6] ^ k[6] ^ k[6] ^ k[1], …

由于我们有后边的明密文对照,所以可以根据明密文对照情况,求出形如k[1] ^ k[17] ^ k[0]的32个值,然后根据里边的参数构造矩阵,用sage求解k[i],即求出了key。再根据key直接写逆算法求出明文

计算k[a] ^ k[b] ^ k[c]

def calckey(plain, cipher):
    newbox = [1,6,14,29,3,23,9,0,2,31,16,4,27,25,11,15,13,24,26,22,10,20,18,7,21,12,5,17,8,19,28,30]
    m = list(bin(plain)[2:].rjust(32,'0'))
    c = list(bin(cipher)[2:].rjust(32,'0'))
    mm = []
    for i in newbox:
        mm.append(m[i])
    keys = ""
    for i in range(32):
        keys += str(ord(mm[i])^ord(c[i]))
    return keys

print calckey(【plain】, 【cipher】)

import random

得到一个序列01001001111000111101111011001100。剩下的部分类似一个32元一次方程做,用sage的矩阵来解题

from sage import *

A = Matrix(GF(2),[
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0],
[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1],
[0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0],
[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0],
[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0],
[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0],
[0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0],
[0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1]])
w = vector(GF(2),[0,1,0,0,1,0,0,1,1,1,1,0,0,0,1,1,1,1,0,1,1,1,1,0,1,1,0,0,1,1,0,0])
print A.solve_right(w)

在这里插入图片描述
这个转为整数即为key,为1492446066,于是可以写出解密代码

(其实不需要求解key这一步。上一步已经知道了k[0] ^ k[1] ^ k[17],直接用来解明文就好)

逆算法

from libnum import n2s

pbox1 = [22, 28, 2, 21, 3, 26, 6, 14, 7, 16, 15, 9, 17, 19, 8, 11, 10, 1, 13, 31, 23, 12, 0, 27, 4, 18, 30, 29, 24, 20, 5, 25]
pbox2 = [17, 6, 7, 27, 4, 20, 11, 22, 2, 19, 9, 24, 23, 31, 15, 10, 18, 28, 5, 0, 16, 29, 25, 8, 3, 21, 30, 12, 14, 13, 1, 26]

def p(data,pbox):
    tmp = bin(data)[2:].rjust(32,'0') # 左边补0到总长度32位
    out = [ tmp[x] for x in pbox ] # 按照pbox重新调整位置
    return int(''.join(out),2)

def rep(data,pbox):
    tmp = bin(data)[2:].rjust(32,'0')
    out = [tmp[pbox.index(x)] for x in range(32)]
    return int(''.join(out),2)

def encrypt(key,msg):
    tmp1 = p(msg^key,pbox1)
    tmp2 = p(tmp1^key,pbox2)
    return tmp2^key

def decrypt(key,msg):
    msg = msg^key
    msg = rep(msg, pbox2)
    msg = msg^key
    msg = rep(msg, pbox1)
    msg = msg^key
    return msg

key = 1492446066
cipher = [2670163133,2168059145,2640667901,1361473960,4285198444,1462920522,1669035357,1836344829,292090312,1735062728,2338346668]
plain = ""
for i in cipher:
    plain += n2s(decrypt(key, i))
print plain

flag{843f4cf5-8edc-49e7-9fd2-7cb31840c10f}

猜你喜欢

转载自blog.csdn.net/cccchhhh6819/article/details/109262974