巅峰(癫疯)极客赛后总结
巅峰(癫疯)极客赛后总结
1.Medicallmage
题目介绍
题目给出的源码(还有比赛时我写的注释(源码自带注释是英文)和当时想的思路):
from PIL import Image
from decimal import *
import numpy as np
import random
getcontext().prec = 20 #设定小数点精度
def f1(x):
# It is based on logistic map in chaotic systems
# The parameter r takes the lesargt legal value
assert(x>=0)
assert(x<=1)
...
def f2(x):
# same as f1
...
def f3(x):
# same as f1
...
def encryptImage(path):
im = Image.open(path)
size = im.size #这一步返回的是一个元组
pic = np.array(im)
im.close()
r1 = Decimal('0.478706063089473894123')
r2 = Decimal('0.613494245341234672318')
r3 = Decimal('0.946365754637812381837')
w,h = size #分别赋值宽、高
for i in range(200):
r1 = f1(r1)
r2 = f2(r2)
r3 = f3(r3)
const = 10**14
#上面全部是初始化
for x in range(w):
for y in range(h):
x1 = int(round(const*r1))%w
y1 = int(round(const*r2))%h
r1 = f1(r1)
r2 = f2(r2)
tmp = pic[y,x]
pic[y,x] = pic[y1,x1]
pic[y1,x1] = tmp #将选出的两个位置的数字交换
#把图片的像素打乱
p0 = random.randint(100,104)
c0 = random.randint(200,204)
config = (p0,c0)
#为下面对像素值加密做准备,并且把随机出来的密钥保存,便于输出
for x in range(w):
for y in range(h):
k = int(round(const*r3))%256
k = bin(k)[2:].ljust(8,'0')
#ljust(width[,fillchar]),其中width为新字符串的指定宽度,fillchar为填充字符,如果width小于新字符串,那么返回原字符串。
k = int(k[p0%8:]+k[:p0%8],2) #将k从随机位置分开后前后交换,并从二进制转回十进制
r3 = f3(r3)
p0 = pic[y,x]
c0 = k^((k+p0)%256)^c0 #这里的^是异或的意思,不是幂函数
pic[y,x] = c0 #在已经交换完的数组元素上进行加密
#上面是对于像素值的加密
return pic,size,config
def outputImage(path,pic,size):
im = Image.new('P', size,'white') #创建新图像,模式为p(P 8位像素,使用调色板映射到其他模式),大小和输入的那个一致,初始颜色为白色
pixels = im.load() #为文件分配内存,打开它并返回一个访问对象(类似于二维数组)
for i in range(im.size[0]):
for j in range(im.size[1]):
pixels[i,j] = (int(pic[j][i]))
im.save(path)
#输出函数
def decryptImage(pic,size,config):
.....
enc_img = 'flag.bmp'
out_im = 'flag_enc.bmp'
pic,size,_ = encryptImage(enc_img)
outputImage(out_im,pic,size)
'''解题的思路:先把图片根据函数outputImage读出pic,然后再根据加密函数写出解密函数恢复原先的图片,然后输出保存'''
解题思路
题目源码给出的f函数明显是有缺损的,而根据它自带的英文注释
# It is based on logistic map in chaotic systems
# The parameter r takes the lesargt legal value
可以得知这个函数是基于混沌系统中的logistic map(逻辑斯蒂映射)的。(看不懂可以用翻译软件)
查资料易得logistic map的函数为:\(X_n=\mu X_{n-1}(1-X_{n-1})\)
因为参数r可以取得合法的最大值(这里的r就是资料中的\(\mu\)?,也就是控制参数,控制参数的取值范围为(0,4]。该函数的另一个重要的参数是初值,即\(X_1\)?),可以将函数还原为
def f1(x):
# It is based on logistic map in chaotic systems
# The parameter r takes the lesargt legal value
assert(x>=0)
assert(x<=1)
return 4 * x * (1-x)
这个题目的解题思路是先在加密的图片上把值恢复,然后再把顺序调回去。由于不知道准确的密钥,但是密钥空间不大(就25种可能性),所以就可以进行爆破,把25种情况全列出来
第一步:异或解密
#为下面对像素值加密做准备,并且把随机出来的密钥保存,便于输出
for x in range(w):
for y in range(h):
k = int(round(const*r3))%256
k = bin(k)[2:].ljust(8,'0')
#ljust(width[,fillchar]),其中width为新字符串的指定宽度,fillchar为填充字符,如果width小于新字符串,那么返回原字符串。
k = int(k[p0%8:]+k[:p0%8],2) #将k从随机位置分开后前后交换,并从二进制转回十进制
r3 = f3(r3)
p0 = pic[y,x]
c0 = k^((k+p0)%256)^c0 #这里的^是异或的意思,不是幂函数
pic[y,x] = c0 #在已经交换完的数组元素上进行加密
#上面是对于像素值的加密
这一段就是加密的程序,我甚至还画了流程图:
至于解密的程序(函数)如下:
def devalue(dpic,p0,c0,w,h,r3,const):
for x in range(w):
for y in range(h):
k = int(round(const*r3))%256
k = bin(k)[2:].ljust(8,'0')
k = int(k[p0%8:]+k[:p0%8],2)
r3=f(r3)
#因为这个函数核心上只是把加密的式子换成解密的式子,解密像素的顺序和加密的顺序是一样的,所以需要的参数也是一样的
p0=((dpic[y,x]^c0^k)+256-k)%256
#我觉得这里也可以用pow函数写,并且这里的+256是可以不写的
c0=dpic[y,x]
dpic[y,x]=p0
return dpic
解密的流程图:
核心:加密算法为:\(c_n=k\oplus (k+p_n)\oplus c_{n-1}\)?,所以解密式为\(p_n=(c_n\oplus c_{n-1}\oplus k)-k\)?
第二步:恢复顺序(爆破)
由于置换过程中像素块[y1,x1]的选择是随机的,所以可能会出现一个像素块被改变了多次位置的情况,所以解密的顺序应该要与加密的顺序相反,所以就先把需要的所有r1、r2的值存入列表,然后从后往前调用
def deChaos(dpic,w,h,r1,r2,const):#const参数在主程序中赋值
R1,R2=[r1],[r2]
for x in range(w):
for y in range(h):
R1.append(f(R1[-1]))
R2.append(f(R2[-1]))
r1_cnt,r2_cnt = 1 , 1
for x in range(w-1,-1,-1):
for y in range(h-1,-1,-1):
r1_cnt += 1
r2_cnt += 1
x1 = int(round(const*R1[-r1_cnt]))%w
y1 = int(round(const*R2[-r2_cnt]))%h
tmp = pic[y,x]
pic[y,x] = pic[y1,x1]
pic[y1,x1] = tmp
return pic
然后就是解密函数:
def decryptImage(path):
im = Image.open(path)
size = im.size
pic = np.array(im)
im.close()
r1 = Decimal('0.478706063089473894123')
r2 = Decimal('0.613494245341234672318')
r3 = Decimal('0.946365754637812381837')
w,h = size
for i in range(200):
r1 = f1(r1)
r2 = f2(r2)
r3 = f3(r3)
const = 10**14
cnt = 0 #这个主要是后面生成图片时区别取名用
for p0 in range(100,105):
for c0 in range(200,205):
cnt+=1
dpic = devalue(pic,p0,c0,w,h,r3,const)
dpic = dechaos(dpic,w,h,r1,r2,const)
outputImage("flag.flag_dec"+str(cnt)+".bmp",dpic,size)
decrypiImage('flag.bmp')
另一个脚本:
from PIL import Image
from decimal import *
import numpy as np
import random
import itertools
getcontext().prec = 20
def f1(x):
# It is based on logistic map in chaotic systems
# The parameter r takes the largest legal value
assert(x>=0)
assert(x<=1)
k = Decimal('4')
return k * x * (1-x)
def f2(x):
# same as f1
k = Decimal('4')
return k * x * (1-x)
def f3(x):
# same as f1
k = Decimal('4')
return k * x * (1-x)
def outputImage(path,pic,size):
im = Image.new('P', size,'white')
pixels = im.load()
for i in range(im.size[0]):
for j in range(im.size[1]):
pixels[i,j] = (int(pic[j][i]))
im.save(path)
def decryptImage(pic,size,config):
pic = pic.copy()
w, h = size
p0, c0 = config
r1 = Decimal('0.478706063089473894123')
r2 = Decimal('0.613494245341234672318')
r3 = Decimal('0.946365754637812381837')
const = 10 ** 14
for i in range(200):
r1 = f1(r1)
r2 = f2(r2)
r3 = f3(r3)
for x in range(w):
for y in range(h):
k = int(round(const*r3))%256
k = bin(k)[2:].ljust(8,'0')
k = int(k[p0%8:]+k[:p0%8],2)
r3 = f3(r3)
c = pic[y,x]
p0 = ((c ^ c0 ^ k) - k) % 256
c0 = c
pic[y,x] = p0
r1_list = [r1]
r2_list = [r2]
for x in range(w):
for y in range(h):
r1_list.append(f1(r1_list[-1]))
r2_list.append(f1(r2_list[-1]))
cnt = w * h - 1
for x in range(w-1, -1, -1):
for y in range(h-1, -1, -1):
r1 = r1_list[cnt]
r2 = r2_list[cnt]
cnt -= 1
x1 = int(round(const*r1))%w
y1 = int(round(const*r2))%h
tmp = pic[y,x]
pic[y,x] = pic[y1,x1]
pic[y1,x1] = tmp
return pic
enc_img = 'flag_enc.bmp'
out_im = 'flag.bmp'
img = Image.open(enc_img)
w, h = img.size
pic = np.array(img)
img.close()
index = 0
for p0, c0 in itertools.product(range(100, 105), range(200, 205)):
pic_decrypt = decryptImage(pic, (w, h), (p0, c0))
Image.fromarray(pic_decrypt).save(f'results/{index}{out_im}')
index += 1
flag{31576107-c243-4962-a4f8-7330d4ad9a76}
2.ctfrsa
题目介绍
from secret import flagn,p,q
#p and q are two primes generated by getPrime
import random
def key_gen():
while True:
dp = random.randint(1,1<<20)
dq = random.randint(1,q-1)
if gcd(dp, p - 1) == 1 and gcd(dq, q - 1) == 1:
d = crt([dp,dq],[p-1,q-1])
phi = (p-1)*(q-1)
R = Integers(phi)
e = R(d)^-1
return p*q,e
n,e = key_gen()
print e
print n
print pow(flagn,int(e),n)
输出文件output.txt
e = 2953544268002866703872076551930953722572317122777861299293407053391808199220655289235983088986372630141821049118015752017412642148934113723174855236142887
N = 6006128121276172470274143101473619963750725942458450119252491144009018469845917986523007748831362674341219814935241703026024431390531323127620970750816983
flagn = 4082777468662493175049853412968913980472986215497247773911290709560282223053863513029985115855416847643274608394467813391117463817805000754191093158289399
解题思路
通过对于dp的一系列操作:
\[\begin{equation} \begin{aligned} d&=dp\%(p-1) \\ d&=dp+k_1(p-1) \\ e\times d &=e\times dp+k_1e(p-1) \\ \end{aligned} \end{equation} \]因为\(ed\equiv 1(mod\ \phi(n))\)?,所以:
\[\begin{equation} \begin{aligned} 1+k_2\phi(n)&=e\times dp+k_1e(p-1) \\ e\times dp-1&=(k_2(q-1)-k_1e)(p-1) \end{aligned} \end{equation} \]把\((k_2(q-1)-k_1e)\)当做一个未知数,可以设为k,有:
\[e\times dp-1=k(p-1) \]根据费马小定理,可得有一个数a,满足gcd(a,p)==1,又因为p是素数,就可得
\[\begin{equation} \begin{aligned} a^{p-1}&\equiv 1(mod\ p) \\ (a^{p-1})^{k}&\equiv 1(mod\ p) \\ a^{k(p-1)}&\equiv 1(mod\ p) \\ a^{e\times dp -1}&\equiv 1(mod\ p) \\ a^{e\times dp -1}-1&=k_3\times p \end{aligned} \end{equation} \]所以\(gcd(a^{e\times dp-1}-1,n)==p\)
又因为a需要满足gcd(a,p)==1,p是素数,所以a就选择最小的偶数2,有脚本:
import gmpy2
from Crypto.Util.number import *
e = 2953544268002866703872076551930953722572317122777861299293407053391808199220655289235983088986372630141821049118015752017412642148934113723174855236142887
n = 6006128121276172470274143101473619963750725942458450119252491144009018469845917986523007748831362674341219814935241703026024431390531323127620970750816983
c = 4082777468662493175049853412968913980472986215497247773911290709560282223053863513029985115855416847643274608394467813391117463817805000754191093158289399
for i in range(1,1<<20):
x=pow(2,i*e-1,n)-1
if(gmpy2.gcd(x,n)!=1):
p=gmpy2.gcd(x,n)
break
q=n//p
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m=pow(c,d,n)
print(long_to_bytes(m))
可能得跑上一段时间
flag{d67fde91-f6c0-484d-88a4-1778f7fa0c05}
3.learn_SM4
wp是嫖的,等回头把对称加密学完了再回来整理
题目描述
题目给出了一个SM4的加密算法和交互终端。我们可以选明文输入进交互,得到第
轮的第
位加密结果。我们需要求SM4加密算法的第一轮轮密钥
,若正确即给出flag。
我的解答
我们只需要求SM4加密算法的第一轮轮密钥
。
我们可以选明文得到第
轮的第
位加密结果。所以我们可以令
,
来得到第一轮的所有加密结果。
SM4需进行32轮的加密变换,明文为
。第一轮加密分线性变换(有限域乘法)和非线性变换(S盒)
线性的操作如下:
def _linear_map(byte4): _left = loop_left_shift return byte4 ^ _left(byte4, 2) ^ _left(byte4, 10) ^ _left(byte4, 18) ^ _left(byte4, 24)
也就是说,把一个4byte(即32位)的数,自己和自己的循环移位所异或。
考虑代数结构:有限域\(GF(2)\)的扩域对
的商,这个数可以看成是这个域里面的一个多项式
,循环移位的操作可以看成是
与
相乘,所以这个结果就对应了
和
相乘。
所以要找这个映射的逆映射,我们只需要找这个域中的逆元,并乘回去就行。这个逆元为
非线性就是一个S盒替换,直接打表求逆映射就行。
然后我们现在已知第一轮加密结果为
,以及明文
。
所以
题目不让我们把明文取为全零,但是我们上面的做法可以选任何明文都能求出第一轮密钥
。
需要注意的是,SM4和题目全程用的是PY2。我们这里也索性用PY2算了Orz。PY2=坑爹!!! 代码如下:
from sm4 import _linear_map, loop_left_shift, S_BOX, _byte_pack, _byte_unpack from pwn import * import itertools import string from hashlib import sha256 def _linear_map_inv(byte4): _left = loop_left_shift return byte4 ^ _left(byte4, 2) ^ _left(byte4, 4) ^ _left(byte4, 8) ^ _left(byte4, 12) \ ^ _left(byte4, 14) ^ _left(byte4, 16) ^ _left(byte4, 18) ^ _left(byte4, 22) \ ^ _left(byte4, 24) ^ _left(byte4, 30) S_INV_BOX = {y:x for x, y in S_BOX.items()} def _s_inv_box(byte): return S_INV_BOX.get(byte) def _non_linear_map_inv(byte_array): return (_s_inv_box(byte_array[0]), _s_inv_box(byte_array[1]), _s_inv_box(byte_array[2]), _s_inv_box(byte_array[3])) def _rep_t_inv(byte4): b_array = _byte_unpack(_linear_map_inv(byte4)) return _byte_pack(_non_linear_map_inv(b_array)) c1_list = [] conn = remote('47.104.94.208', 54740) s = conn.recvline().strip() s_last = s[12:28] ans = s[-64:] for i in itertools.product(string.ascii_letters+string.digits, repeat=4): s_first = ''.join(i) s = s_first + s_last if (sha256(s).hexdigest() == ans): conn.sendline(s_first) break for i in range(4): conn.sendlineafter('r:', '0') conn.sendlineafter('i:', str(i)) conn.sendlineafter('msg(hex):\n', '01') conn.recvline() c1 = conn.recvline() c1_list.append(int(c1)) for i in range(4): conn.sendlineafter('r:', '0') conn.sendlineafter('i:', str(i)) conn.sendlineafter('msg(hex):\n', '01') conn.recvline() c1 = conn.recvline() c1 = 0 for i in c1_list: c1 = c1 * 256 + i x0, x1, x2, x3 = 0, 0, 0, 1 rk = _rep_t_inv(c1 ^ x0) ^ x1 ^ x2 ^ x3 conn.sendlineafter('Give me answer:', str(rk)) conn.interactive()
最后的flag为:
flag{a722626d-0775-4976-bf3e-36ad965c031a}
参考:
- [巅峰极客] 2021巅峰极客网络安全挑战赛 - 初赛 个人 or 团队 writeUp