护网杯一道密码学的感想
2019-11-28

       护网杯比赛,一道不算难的密码学却思路绕了好久才和出题人相符合,这里记录一下做题的过程及感想

       题目的源码如下:

import osdef xor(a,b): assert len(a)==len(b) c="" for i in range(len(a)): c+=chr(ord(a[i])^ord(b[i])) return cdef f(x,k): return xor(xor(x,k),7)def round(M,K): L=M[0:27] R=M[27:54] new_l=R new_r=xor(xor(R,L),K) return new_l+new_rdef fez(m,K): for i in K: m=round(m,i) return mK=[]for i in range(7): K.append(os.urandom(27))m=open("flag","rb").read()assert len(m)<54m+=os.urandom(54-len(m))test=os.urandom(54)print test.encode("hex")print fez(test,K).encode("hex")print fez(m,K).encode("hex")

除了源码,还给了三行16进制的数,看到这道题目时,首先分析一下题目,给了一个K盒子,用于加密过程使用,K是一个由7个随机字符串产生的。其中m变量的前面一部分包含着flag,test变量也是一串随机的字符串

加密函数最外层是fez,然后fez中循环调用round函数进行加密,每一次循环都是使用K盒子中的一个随机字符串,其中round是真正实现加密的过程,是一个异或的过程,异或是将传入的明文的两部分还有一个K作为变量。

       第一种思路:刚开始的时候,一看到urandom,便想到是不是伪随机数,但是google了一段时间发现urandom算是一个强伪随机数,还是比较安全的,而且就算我知道随机数种子,对于我前面解flag也没有帮助,还得靠暴力破解,应该不是出题人想要考的,所以这条路是行不通的,主要是以往在比赛中做的一些题目很多用到了暴力破解,所以看到密码学形成了一种思维定式,就想看看能不能结合暴力破解来解题。。。。唉

       第二种思路:因为test是已知的,第一次fez的调用中用到了test和K,并且密文也是已知的,那么能不能解出来K,然后去解flag?于是回到round函数,可以看到最后一次加密结束时,用到的是K7,那么最后一次的E_L和E_R我们是知道的,我们能不能推出前一次的L和R,也就是没有经过轮加密的L和R,我们已经知道下一轮的密文L等于上一轮的R,下一轮的R等于上一轮的R异或L再异或当前轮所使用的K,那么很容易得出我们能获得上一轮所使用的明文的右半部分,有了上一轮的右半部分,加密后的新的右半部分,如果我们求出上一轮的左边的部分,就能求出当前轮所使用的K,但是根绝目前已知的所有条件,上一轮的明文的左半部分是无法求出的,卡在这里了很长时间,结果无奈放弃了

      第三种思路:正向来模拟加密的过程,来发现是否存在漏洞,在round函数中,新一轮的左半部分等于上一轮的右半部分,下一轮的右半部分等于上一轮的左右两部分异或之后再和当前轮的K异或,产生的新一轮的左右两部分拼接成为新的密文。

      即假设初始明文为M,分为L和R两部分

  

1 第一轮:R+R^L^K12 第二轮:R^L^K1+R^R^L^K1=>R^L^K1+L^K1^K23 第三轮:L^K1^K2+R^K2^K34 第四轮:R^K2^K3+L^R^K1^K3^K45 第五轮:L^R^K1^K3^K4+L^K1^K2^K4^K56 第六轮:L^K1^K2^K4^K5+R^K2^K3^K5^k67 第七轮:R^K2^K3^K5^k6+R^L^K1^K3^K4^K6^K7

由最终的形式可以看到,结果由K盒子和初始的明文的两部分组成  

又因为在包含flag的m变量中进行了相同的fez函数加密,所以和test调用fez函数以后的结果形式是相同的,只是初始的明文不同,又因为根据异或运算的计算逻辑,相同的数异或为0,0异或任何数还是其本身,所i有即使我们不知道K盒子,因为两次运算结果的K盒子在结果中的形式相同,所以可以抵消掉。

因此解为两部分:

1.第七轮结束后的密文的左半部分异或后实际就是test变量的右半部分和flag的右半部分进行了一次异或,再和test本身的右半部分异或一次,就能得到m变量的右半部分。

2.第七轮结束后的密文的左右两部分异或得到只剩下原明文左半部分和K盒组成的结果,即两次fez加密的结果的各自的左右两部分先做异或运算,然后再把两组异或结果再一起异或一次,即为test变量的左半部分和m变量的左半部分异或的结果,又因为test变量已知,所以把结果再和test变量的左半部分异或一次就能得到m的左半部分。

经过以上两步,就能得到明文m,又因为m中包含了flag,所以我们就解出了答案。

还是自己不懂套路,K盒属于中间变量,并且加密为异或加密,更何况这是已知密文的攻击,调用了两次相同的加密方式,使用的K盒也是相同的,所以才产生了这样的漏洞,下次分析题目2条路:

1.先正向分析加密流程,分析加密后的结果组成,能不用暴力解决就不要用

2.逆向分析,从后往前推