下载附件得到一个py文件,打开后如下
可以看到脚本的主要内容就是对flag使用了encrypt函数进行加密
尝试解读一下脚本的内容,其中random.getrandbits(n) 的作用是生成小于2^n随机数
m1=flag[4:-1] 说明加密的密文是取了flag的第5位字符到倒数第2位
key.find( )的作用是查看字符是key中的第几位(从0开始),也就是返回字符的检索值,例如key.find(‘q’)=0
在第二个for循环里,根据k%2是否等于0,对tmp进行了乘a或加b的运算,由于不确定k的值,所以无法确认具体的运算过程。
这里我没有选择去假设或爆破a、b的值,可以发现,k在“for _ in range(len(m))”的循环之后,k的值会被初始化为kt,当k的初始值不变时,对tmp进行的运算过程也不变。
这里,记tmp0=key.find(m[0]),tmp1=key.find(m[1]) (m就是密文m1)
题目说明了flag是以“hsctf{”开头的(就是assert后面的语句),而m1是从flag的第5个字符开始取的,也就是m1的前2位是“f{”,即m[0]就是’f’, m[1]是’{’
所以tmp0=13,tmp1=62
同时明文的前2位是“Wh”,分别对应了key[27]和key[15],也就是说经过for _ in range(len(m))运算之后(为了区分初始状态,前面加个f):
ftmp0%len(key)=27,ftmp1%len(key)=15,这里已知len(key)=67
刚才说过,tmp到ftmp的运算过程是不变的,那么根据四则运算的法则,计算ftmp1-ftmp0的过程中,“+b”可以被约掉,“*a”可以被提取出来
也就是说ftmp1-ftmp0 =(tmp1-tmp0)*a^n = 49*a^n
又因为ftmp0%67=27,ftmp1%67=15,并且由于ftmp1>ftmp0,那么可以得到(ftmp1-ftmp0)%67 =15-27+67 = 55
即(49*a^n)%67=55,可以得到 (a^n)%67=23
有了这一个关系,就可以计算tmp2了
m[2]是’c’,对应key[21],即ftmp2%67=21,(ftmp2-ftmp1)%67=key.find(m[2])- key.find(m[1])=21-15=6
得到(tmp2-tmp1)*a^n%67=6,为了直观表示tmp2是怎么求解的,可以用67的倍数加上余数来代替取余
即(tmp2-62)*(67x+23)%67=6,等价于23*(tmp2-tmp1)%67=key.find(m[2])- key.find(m[1]),这里要判断一下正负,因为余数是正的,所以如果key.find(m[2])- key.find(m[1])<0,就加上67
得到这一个关系就是解密脚本的核心了,可以得到脚本如下:
key = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890{}_#&'
m='WhcuU0o4Vc0VUasJc08W04uJ0qd2IJpVJ02V04p'
m1='{'
for i in range(2,39):
x=key.find(m[i])
y=key.find(m[i-1])
for ftmp in range(0,67):
if x-y>=0:
if 23*(ftmp-key.find(m1[i-2])+67)%67==x-y:
m1 += key[ftmp]
else:
if 23*(ftmp-key.find(m1[i-2])+67)%67==x-y+67:
m1+= key[ftmp]
print('hsctf'+m1+'}')
最终得到输出结果:hsctf{th3_l4st_s3c5et_0f_4he_un1ve2se_1s_42}