bugku-login4-CBC字节翻转攻击
CBC模式:Cipher Black Chaining mode(密码分组链接模式)
CBC模式进行加解密是都需要一个随机初始向量iv,在第一轮进行加解密是都需要与iv进行xor的。
1.加解密过程
0x01:加密过程如下图(来自《图解密码技术》一书)
0x02:解密过程(来自《图解密码技术》一书)
CBC模式的加密过程主要分为这几步:
1. 将明文分为若干组(16个字节为一组),最后一组不足则用特殊字符填充
2.生成一个初始向量iv和密钥
3.用iv与第一组明文异或(iv只影响第一组生成的密文)
4. 然后再用前n组密文与后n+1组明文异或生成第n+1组密文,以次重复
5.最后将生成的密文拼接起来,就成了最终密文
CBC模式的解密过程主要分为这几步:
1.将密文分组
2.用iv与第一组密文xor,解密得到第一组明文
3.用第n组密文与第n+1组密文xor,解密得到第n+1组明文,以此类推
4.将各组的明文拼接在一起就是最终要得到的明文了
这里注意一下:解密的时候前一组密文只影响后一组明文的结果,而不会影响其他组明文的结果,由图也可看得出,这个也是进行攻击的重要之处。
2.CBC字节翻转攻击:
我们需要改变前一组密文的一个字节,然后与下一组的密文异或,我们就可以得到一个不同的明文了,从而就能达到攻击的效果。如图:
举个例子:
我现在有个明文序列:helloworld,现在我以它2个字节为一组进行分组(一般是16个字节为一组,但是这里为了方便起见就以2个字节为一组了)
第一组:he
第二组:ll
第三组:ow
第四组:or
第五组:ld
现在我想把第三组 “ow”中的o翻转为x,那么我们就需要改变第二组的密文从而改变第三的明文
phaintext="helloworld"
enc=encrypt(phaintext)
enc1=chr(ord(enc[5])^ord("o")^ord("x")) #enc[5]即是与“o”相同比特位的密文,也就是说第三组只有"o"这个比特位受影响,而“w”不受影响
result=decrypt(enc1)
这里注意一下:任何字符与本身xor都是为0,任何字符与0xor都为本身,如A xor A=0,A xor 0=A
3.实战—bugku-login4
打开是一个登陆界面,随便输入一个账号密码就能登陆,但是admin不能登陆。
抓包发现,有个iv和cipher,猜测是CBC模式的加密
扫描一波发现有源码泄露
vim -r index.php.swp恢复后得到源代码,审计关键代码
这里对usernmae进行了检测只有admin才能看得到flag,但是admin又不允许登陆,这里看起来有点矛盾,但是我们再看看前面的代码,这个对登陆信息进行序列化然后用CBC模式的加密方式进行加密最后base64_encode,然后我们要做的是以admin的身份登陆,我们可以操作cookie中的iv和cipher进行CBC字节翻转攻击。
然后我们以账号为admia,密码为12345登陆
得到的明文是:a:2:{s:8:"username";s:5:"admia";s:8:"password";s:5:"12345"}
然后16个字节分组得到
s:2:{s:8:"userna
me";s:5:"admia";
s:8:"password";s
:3:"12345";}
所以我们要翻转的是第二组的“a”翻转为“n”,所以我们要改变第一组的密文从而达到攻击的效果
代码如下
1 import base64 2 cipher="yQQeUDxlzRvPToe631KV1vcy8DyI4e0kz7Knb9K6GIH4yP8Q32kufQvWoD7oN3hzi2EpiBxx6t/7sfIH1pCExg==" 3 plain=base64.b64decode(cipher) 4 result=plain[0:13]+chr(ord(plain[13])^ord("n")^ord("a"))+plain[14:] 5 print base64.b64encode(result) 6 #print R0dGVia+fJ24Ei3t29NC90P5kRbHXnW+D690WNWHnATH3UHvC4h+btceizE5jotDgG0QZLa9LOdwSAg9LcsCdw==
POST过去提示不能正常反序列化,因为我们修改了第一组的密文,导致第一组的密文与iv xor会出错,从而导致了第一组明文不能正常解密,所以我们还要对iv进行修改
代码如下(由于我中途输入错了iv,所以代码中的iv和cipher和图中可能会不一样,但是思路是一样的):
注意一下第二步不要修改cipher,只需要修改iv就行了,因为我两个图中的cipher是不一样的,所以我还是要说一下的,以免误人子弟
1 import base64 2 cipher="wRPT3VONV2zFV6D2PbHjIm1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9" 3 plain=base64.b64decode(cipher) 4 oldiv=base64.b64decode("uxrq4TtskqrNJh7JUZV9rg==") 5 one='a:2:{s:8:"userna' 6 iv="" 7 for i in range(0,16): 8 iv=iv+chr(ord(one[i])^ord(plain[i])^ord(oldiv[i])) 9 print base64.b64encode(iv)#iv=GzMLBhOS//4yU8tMCVbw7Q==
将新的iv POST过去成功getflag
最后:写得不太好,若有师傅发现错误,望请斧正