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}