当曲高和寡不是一个结果的时候,它往往会成为一种错觉,……,若你的思考真的精妙到了只有你自己能理解,那你岂不是更应该将你精妙的思考分享给他人?

巅峰(癫疯)极客赛后总结

巅峰(癫疯)极客赛后总结

1.Medicallmage

image

题目介绍

题目给出的源码(还有比赛时我写的注释(源码自带注释是英文)和当时想的思路):

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    #在已经交换完的数组元素上进行加密
    #上面是对于像素值的加密

这一段就是加密的程序,我甚至还画了流程图:
image

至于解密的程序(函数)如下:

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
            

解密的流程图:
image

核心:加密算法为:\(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

参考:

  1. [巅峰极客] 2021巅峰极客网络安全挑战赛 - 初赛 个人 or 团队 writeUp
posted @ 2021-09-20 23:45  01am  阅读(214)  评论(0编辑  收藏  举报