medium
Bertrand (21 solves)
附件代码
task.py
#!/usr/bin/env python3
import sys
import math
import functools
from PIL import Image
from random import randint
import string
from secret import flag, key, n
def pad(s, l):
while len(s) < l:
s += string.printable[randint(0, 61)]
return s
def sox(n, d):
x, y, t = 0, 0, d
for s in range(n - 1):
u = 1 & t // 2
v = 1 & t ^ u
x, y = spin(2**s, x, y, u, v)
x += 2**s * u
y += 2**s * v
t = t // 4
return x, y
def spin(n, x, y, u, v):
if v == 0:
if u == 1:
x = n - 1 - x
y = n - 1 - y
x, y = y, x
return x, y
def encrypt(msg, key, n):#n=16
_msg = pad(msg, n ** 2)
_msg, _key = [ord(_) for _ in _msg], [ord(_) for _ in key]
img = Image.new('RGBA', (n, n), 'darkblue')
pix = img.load()
for _ in range(len(key)):
w = len(_key)
h = n**2 // w + 1
arr = [[_msg[w*x + y] if w*x + y < n**2 else None for x in range(h)] for y in range(w)]
_conf = sorted([(_key[i], i) for i in range(w)])
_marshal = [arr[_conf[i][1]] for i in range(w)]
_msg = functools.reduce(lambda a, r: a + _marshal[r], range(w), [])
_msg = list(filter(lambda x: x is not None, _msg))
_msg = [(_msg[_] + _key[_ % w]) % 256 for _ in range(n**2)]
for d in range(n**2):
x, y = sox(n, d)
pix[x,y] = (_msg[d], _msg[d], _msg[d])
keysum = sum(_key) if w > 0 else 0
orient = keysum % 4
img = img.rotate(90*orient)
return img
assert len(key) == 3
img = encrypt(flag, key, n)
img.save('enc.png')
enc.png
我的理解
代码使用了一个叫做"像素置换加密"的算法。函数接受三个参数:msg(要加密的消息),key(加密密钥),n(图片的大小,即宽和高)。
函数首先对要加密的消息进行填充,填充的长度是n的平方。然后将消息和密钥转换为ASCII码的列表。接下来,创建一个大小为n x n的RGBA图片,并将其加载到像素数组中。
然后,对密钥的每个字符进行循环,计算宽度w和高度h。然后创建一个二维数组arr,将消息按照一定规则填充到arr中。然后按照密钥的顺序重新排列arr,并将其转换为一维数组_msg。然后过滤掉_msg中的None值,并对_msg中的每个值进行一定的计算。最后,将_msg中的值分别赋给图片中的像素。
接下来,根据密钥的ASCII码求和计算一个值keysum,并根据keysum%4对图片进行旋转。最后返回加密后的图片。
我的回答
仔细观察代码,先看第一步
for _ in range(len(key)):
w = len(_key)
h = n**2 // w + 1
arr = [[_msg[w*x + y] if w*x + y < n**2 else None for x in range(h)] for y in range(w)]
_conf = sorted([(_key[i], i) for i in range(w)])
_marshal = [arr[_conf[i][1]] for i in range(w)]
_msg = functools.reduce(lambda a, r: a + _marshal[r], range(w), [])
_msg = list(filter(lambda x: x is not None, _msg))
_msg = [(_msg[_] + _key[_ % w]) % 256 for _ in range(n**2)]
这一串,其实对加密有用的就只有第三行和第四行和最后一行,中间的是那两行算是列表整合,把二维列表转为一维列表,然后去掉None。(建议加上两个print,设置一个flag和key,跑一下测试测试,测试后容易理解)
而这个加密的过程,属于线性过程,置换操作以及模加,属于很好逆的
这个就是主要加密过程
然后是
for d in range(n**2):
x, y = sox(n, d)
pix[x,y] = (_msg[d], _msg[d], _msg[d])
keysum = sum(_key) if w > 0 else 0
orient = keysum % 4
img = img.rotate(90*orient)
这一段,就是将msg的值全部作为像素点经过置换放在图片中的各个位置,对于这个sox函数,并不用理解
他的作用就是产生一个列表,然后msg根据这个列表置换再图片的各个位置,既然n,d都是已知的,我们只需要跑一遍这个代码,获得这个列表,那么将它逆一遍就可以的。
然后图片化成后会根据key的ASCII值和来进行旋转,我们可以根据猜测的key来将其旋转回来。
key的值我们并不知道,只知道是3位,我们可以根据题上中给的范围string.printable[randint(0, 61)](这个是我猜的)进行爆破key。
但是这个题不只是这么简单,如果靠爆破进行得到key的话,中间的那些获取像素点来提取msg,以及三次循环解密的运算时间是不短的特别是
6
1
3
61^3
613,时间几乎无解,我采用的方法是,key三位,ascii大小排序一共6种情况,而图片旋转一共四种情况,我们先将这些情况列举出来,以字典或者列表的形式保存,当爆破的key满足这些条件时直接调用,而不再重复进行这些运算,节约时间。
即便如此,也要需要二十多分钟的时间来爆破出来😭,(应该有更好的方法降低时间复杂度,但是我没找到,ψ(._. )>)
师傅们有更好的方法,欢迎来讨论
我的代码
# !/usr/bin/env python3.10
# -*- coding: utf-8 -*-
# @Time : 2023/7/16 15:56
# @Author : lingfeng
# @File : test-w.py
import functools
import itertools
import string
from PIL import Image
from tqdm import *
def sox(n, d):
x, y, t = 0, 0, d
for s in range(n - 1):
u = 1 & t // 2
v = 1 & t ^ u
x, y = spin(2 ** s, x, y, u, v)
x += 2 ** s * u
y += 2 ** s * v
t = t // 4
return x, y
def spin(n, x, y, u, v):
if v == 0:
if u == 1:
x = n - 1 - x
y = n - 1 - y
x, y = y, x
return x, y
def inv(S_BOX):
return [(S_BOX.index(i)) for i in range(len(S_BOX))]
def get_inv_arr_dec(_key):
_conf = sorted([(_key[i], i) for i in range(w)])
t = tuple([_conf[i][1] for i in range(w)])
return ARR2[x[t]]
def decrypt(_msg, _key, n):
inv_arr_dec = get_inv_arr_dec(_key)
for _ in range(3):
_msg = [(_msg[_] - _key[_ % w]) % 256 for _ in range(n ** 2)]
_msg = [_msg[i] for i in inv_arr_dec]
return _msg
def get_ori_key(key):
_key = [ord(_) for _ in key]
keysum = sum(_key)
orient = keysum % 4
return orient, _key
def get_msg(orient):
return Msg[orient]
n, w, ARR2, Msg = 256, 3, [], []
h = n ** 2 // w + 1
dic = {(sox(n, d)): d for d in trange(n ** 2)}
arr = [[(w * x + y) if w * x + y < n ** 2 else None for x in range(h)] for y in range(w)]
x = {j: i for i, j in zip(range(6), list(itertools.permutations(range(3), 3)))}
ARR = [[arr[i[0]], arr[i[1]], arr[i[2]]] for i in x.keys()]
for arr_dec in tqdm(ARR):
arr_dec = functools.reduce(lambda a, r: a + arr_dec[r], range(w), [])
arr_dec = list(filter(lambda x: x is not None, arr_dec))
inv_arr_dec = inv(arr_dec)
ARR2.append(inv_arr_dec)
for orient in range(4):
img = Image.open("enc.png")
width, height = img.size
img = img.rotate(90 * (4 - orient))
pixels = img.load()
_msg = [0] * n ** 2
for y in range(height):
for z in range(width):
r, g, b, a = pixels[z, y]
_msg[dic[(z, y)]] = r
Msg.append(_msg)
KEY = list(itertools.product(string.printable[:61], repeat=3))
for i in tqdm(KEY):
key = ''.join(i)
orient, _key = get_ori_key(key)
_msg = get_msg(orient)
msg = decrypt(_msg, _key, n)
flag = bytes(msg)
if b'CCTF{' in flag[:60]:
print(flag) # CCTF{3nCrypTioN_8Y_c0lUmn4R_7rAnSp05it!On!}
break
Insights ( 88 solves)
附件代码
Insights.sage
#!/usr/bin/env sage
from Crypto.Util.number import *
from flag import flag
def getRandomNBits(n):
nb = '1' + ''.join([str(randint(0, 1)) for _ in range(n - 1)])
return nb
def getLeader(L, n):
nb = L + getRandomNBits(n)
return int(nb, 2)
def genPrime(L, nbit):
l = len(L)
assert nbit >= l
while True:
p = getLeader(L, nbit - l)
if is_prime(p):
return p
def genKey(L, nbit):
p, q = [genPrime(L, nbit) for _ in '__']
n = p * q
d = next_prime(pow(n, 0.2919))
phi = (p - 1) * (q - 1)
e = inverse(d, phi)
pubkey, privkey = (n, e), (p, q)
return pubkey, privkey
def encrypt(msg, pubkey):
n, e = pubkey
m = bytes_to_long(msg)
c = pow(m, e, n)
return c
nbit = 1024
L = bin(bytes_to_long(b'Practical'))[2:]
pubkey, privkey = genKey(L, nbit)
p, q = privkey
c = encrypt(flag, pubkey)
print('Information:')
print('-' * 85)
print(f'n = {p * q}')
print(f'e = {pubkey[1]}')
print(f'c = {c}')
print(f'p = {bin(p)[2:len(L)]}...[REDACTED]')
print(f'q = {bin(q)[2:len(L)]}...[REDACTED]')
print('-' * 85)
我的回答
没啥好说的,非预期应该是,题目应该是想考Boneh and Durfee attack的扩大范围,因为刚好卡在边界,理论是有限的,跑不出的,需要手动更改。
但是非预期就很离谱
d
=
n
e
x
t
p
r
i
m
e
(
p
o
w
(
n
,
0.2919
)
)
d = next_prime(pow(n, 0.2919))
d=nextprime(pow(n,0.2919))
直接出d,然后解就完了
我的代码
sage
# !/usr/bin/env python3.10
# -*- coding: utf-8 -*-
# @Time : 2023/7/8 10:26
# @Author : lingfeng
# @File : wp.sage
n = 12765231982257032754070342601068819788671760506321816381988340379929052646067454855779362773785313297204165444163623633335057895252608396010414744222572161530653104640020689896882490979790275711854268113058363186249545193245142912930804650114934761299016468156185416083682476142929968501395899099376750415294540156026131156551291971922076435528869024742993840057342092865203064721826362149723366381892539617642364692012936270150691803063945919154346756726869466855557344213050973081755499746750276623648407677639812809665472258655462846021403503851719008687214848550916999977775070011121527941755954255781343103086789
e = 459650454686946706615371845737527916539205656667844780634386049268800615782964920944229084502752167395446158290854047696006034750210758341744841762479191173017773034647739346927390580848998121830029134542880713409306092967282675122699586503684943407535067216738556403169403622104762516293879994387324370835718056251706150557820106296417750402984941838652433642298378976899556042987560946508887315484380807248331504458640857234708123277403252632993828101306072382329879857946191508782246793011691530554606521701055094223574951862129713872918021549814674387049788995785872980320871421550616327471735316980754238323013
c = 10992248752412909788626396175372747713079469256270100576886987393986576680666320383209810005318254336440105142571546847427454822405793626080251363454531982746373841267986148332456716023293306870382809568309620264499225135226626560298741596462262513921032733814032790312163314776421380481083058518893602887082464123177575742160690315666730642727773288362853901330620841098230284739614618790097180848133698381487679399364400048499041582830157094876815030301231505774900176910650887780842536610942820066913075027528705150102760422836458745949063992228680293226303245265232017738712226154128654682937687199768621565945171
d = next_prime(pow(n, 0.2919))
from Crypto.Util.number import *
print(long_to_bytes(int(pow(c,d,n))))#CCTF{RSA_N3w_rEc0rd5_4Nd_nEw_!nSi9h75!}
Resuction (78 solves)
附件代码
#!/usr/bin/env python3
from Crypto.Util.number import *
from flag import flag
from decimal import *
def keygen(nbit, r):
while True:
p, q = [getPrime(nbit) for _ in '__']
d, n = getPrime(64), p * q
phi = (p - 1) * (q - 1)
if GCD(d, phi) == 1:
e = inverse(d, phi)
N = bin(n)[2:-r]
E = bin(e)[2:-r]
PKEY = N + E
pkey = (n, e)
return PKEY, pkey
def encrypt(msg, pkey, r):
m = bytes_to_long(msg)
n, e = pkey
c = pow(m, e, n)
C = bin(c)[2:-r]
return C
r, nbit = 8, 1024
PKEY, pkey = keygen(nbit, r)
print(f'PKEY = {int(PKEY, 2)}')
FLAG = flag.lstrip(b'CCTF{').rstrip(b'}')
enc = encrypt(FLAG, pkey, r)
print(f'enc = {int(enc, 2)}')
我的理解
题目上已经说是前面的suction的加强版,仔细观察代码,先看不同之处
发现d是直接生成了64bit的素数,p,q的bit数远远的增大了,其他一切都和suction的一样,少给了n,c,e的低8位。
我的方法
既然n的值非常大,再使用fact分解是不现实的,根据题目给出了d的比特,分析d,n的关系,发现d十分小,貌似满足维纳攻击的条件,既然如此,不妨一试,爆破e,n通过维纳攻击来进行分解出p,q。
首先和suction一样,将n,e的爆破范围降低,但是e并不是素数,没有办法减少,而n仍是由两个大素数相乘组成,还是和suction的一样通过模小素数以及判断是不是素数来缩小范围。
得到p,q之后,就是正常的rsa解密即可。
我的代码
# !/usr/bin/env python3.10
# -*- coding: utf-8 -*-
# @Time : 2023/7/8 14:40
# @Author : lingfeng
# @File : WP.py
#脚本1
#Sage
def factor_rsa_wiener(N, e):
N = Integer(N)
e = Integer(e)
cf = (e / N).continued_fraction().convergents()
for f in cf:
k = f.numer()
d = f.denom()
if k == 0:
continue
phi_N = ((e * d) - 1) / k
b = -(N - phi_N + 1)
dis = b ^ 2 - 4 * N
if dis.sign() == 1:
dis_sqrt = sqrt(dis)
p = (-b + dis_sqrt) / 2
q = (-b - dis_sqrt) / 2
if p.is_integer() and q.is_integer() and (p * q) % N == 0:
p = p % N
q = q % N
if p > q:
return (p, q)
else:
return (q, p)
from tqdm import *
from Crypto.Util.number import *
PKEY = 14192646310719975031517528381795548241077678859071194396837281472399230674325587198691913743313024193030641258581477544395982020385534616950314446352119543012689979705497443206671093873748633162188213109347667494028713308821945628880987033909436936504594085029207071182583896573425433818693447573712242776054326253393149643818428222532313129014785625379928796322492111783102063504659053965652092334907431265629283336869752591405010801363428649651892548988084920295512198406822149854508798413366425646089176325412867633899847841343293434518769693835679828109184625256833518392375615524221729773443578746961887429674099018040291053535429314874943299587513900900515776980848746070077651676814430155460898107362207677739452859298842563030631706907437662807884529549746843462830493900480079096937402325307522965877069080418178692616875205678928420840955518031258855869218152431304423803589723140983606576549207164114711076498723237274262054605174412193097533550076687418481230734706280737017543741247718967059747548710091320650704988384742281590019869955579949961574733610565593105027342454880292474482792325237942870408329807427182014062811782475262070063065860763705715879581562335668163076088547827008755841277828137570366416095778
ENC = 93313196155732054514477836642637636744872135106456047659057794344503071105783322399713135615723000054324693644981340568454628360027598469597864407205974007198804288563284521413279406211756674451582156555173212403946908121125010635246043311803494356106191509999360722019559999844621905376368824621365540442906142224342650371557766313381899279520110833822291649001754956653102495882094754863493058001964760438040783400782352466943990226083197340594364084294954324101604417550048379969516185353765224920719355485680872367930581872987972421836853197205534334204586713387469939986387582911728909524428102693874671302382
C=ENC<<8
N_=int(bin(PKEY)[2:2042],2)<<8
E_=int(bin(PKEY)[2042:],2)<<8
x=sieve_base
n=[]
for i in trange(0,255):
N=N_+i
if N%2==0 or isPrime(N):
continue
t=[N%a for a in x]
if 0 in t:
continue
n.append(N)
# [print(i) for i in n]
#
# print(len(n))
t=[1,2,4,5,6,7,9,11,12,14,16]#这里是我根据factor网站手动排除的
#print(len(t))
n=[n[i] for i in range(len(n)) if i not in t ]
#print(len(n),n)
e=[E_+i for i in range(255)]
for N in n:
for e0 in tqdm(e):
try:
p,q=factor_rsa_wiener(N,e0)
if (p,q):
print(p,q,e0)
except:continue
c=[C+i for i in range(255)]
#174371810769321904879771281457264126835536885769795883249689917534917724369418768788724295833034211032483643000304760729751539874477879712047563093622220226046317887445404441674034749799537927102249473198079484568936482286828850749395726000380023524546597995716458255074061858150920326214596269436009922505467 165057747190263284822060070867326404037226426769214461653453077347642660157038079939800175423376926190278742477747265143003662594094338527240051087411682630746699434893569121815945138770655880775127567084882226276395011876712312835431878615781804989285062397026979540636501309686456639319755442794081237335613
p,q,e=174371810769321904879771281457264126835536885769795883249689917534917724369418768788724295833034211032483643000304760729751539874477879712047563093622220226046317887445404441674034749799537927102249473198079484568936482286828850749395726000380023524546597995716458255074061858150920326214596269436009922505467, 165057747190263284822060070867326404037226426769214461653453077347642660157038079939800175423376926190278742477747265143003662594094338527240051087411682630746699434893569121815945138770655880775127567084882226276395011876712312835431878615781804989285062397026979540636501309686456639319755442794081237335613, 19152712448778858582528734875468196678366984818842265525346340114296810907435357813959451387293270496095878944786775775749129832803842508074794234774568097809721690831345120778762600713106116293626590641716601095020202233532544196547654794913903350183891867544539554967347396716482565232986995497267273877597593761608770699282404807896050347585632153075234094034163801474316224123620090879021107631960008144066862084573910442635526649884938724881478713853362879412453150893601267748827792136092224063120914443976032390554506925020506643629449426005820918745312311984391868895996772772355715765028825561022860823765675
phi=(p-1)*(q-1)
for c0 in c:
d = inverse(e, phi)
m= pow(c0,d,p*q)
flag=long_to_bytes(int(m))
if len(str(flag))<=80:
print(flag)
Blobfish (51 solves)
附件代码
#!/usr/bin/env python3
import os
from hashlib import md5
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from PIL import Image
from PIL import ImageDraw
from flag import flag
key = get_random_bytes(8) * 2
iv = md5(key).digest()
cipher = AES.new(key, AES.MODE_CFB, iv=iv)
enc = cipher.encrypt(flag)
img = Image.new('RGB', (800, 50))
drw = ImageDraw.Draw(img)
drw.text((20, 20), enc.hex(), fill=(255, 0, 0))
img.save("flag.png")
hkey = ''.join('\\x{:02x}'.format(x) for x in key[:10])
os.system(f'/bin/zip -0 flag.zip flag.png -e -P \"$(/bin/echo -en \"{hkey}\")\"')
我的理解
感觉本次比赛最离谱的题,没有之一。
一眼能看出来这个题的密钥根本爆破不出来,8bytes的未知,这个计算量很难搞。
然后发现也没有其他方法,一直在g,后来还是看春哥的解法才知道,真烧脑啊。
是使用bkcrack工具进行压缩包的已知明文攻击。
我的方法
没有什么密码学思想,学会使用bkcarck工具,一步一步破出来即可
可以跟着项目里面的例子学习理解一下(misc题里面经常有这种题,百度一下还挺多的,比葫芦画瓢就可以的)
bkcrack工具
学习链接
我的代码
# !/usr/bin/env python3.10
# -*- coding: utf-8 -*-
# @Time : 2023/7/17 20:23
# @Author : lingfeng
# @File : wp.py
# echo 89504E470D0A1A0A0000000D49484452 | xxd -r -ps > png_header
# ../bkcrack -C flag.zip -c flag.png -p png_header
# ../bkcrack -C flag.zip -c flag.png -k 03492be6 b81a5123 24d7b146 -d advice_deciphered.deflate
# ../bkcrack -k 03492be6 b81a5123 24d7b146 -r 10 ?b
from hashlib import md5
from Crypto.Cipher import AES
key_hex = 'ad6efb792aea5aaa'
enc = '69f421d9e241933cbc62a9ffe937779c864ffa193de014aeb57046b16c40c7353910c61a2676f14f4c7737f038a6db53262c50'
enc = bytes.fromhex(enc)
key = bytes.fromhex(key_hex)
key = key * 2
iv = md5(key).digest()
cipher = AES.new(key, AES.MODE_CFB, iv=iv)
msg = cipher.decrypt(enc)
print(msg)#CCTF{d3ep-Zip_fi5h_0f_tH3_knOwN_pL4!n_7exT_ATtAcK!}
至此,medium已经完成2/3了,这四个题感觉难度不是很大,有点奇葩,剩下的四个题,其中有两道丢番图的学习,下篇的任务更大啊!加油!
愿护一枚银杏叶
努力不是为了一点的甜头,而是我们的责任担当的,我们每个人都有自己想要守护的人,为了心里的她,为了我们自己,加油吧!青春年少,热血努力更适合我们!