aes加密简介
AES算法全称Advanced Encryption Standard,是DES算法的替代者,旨在取代DES成为广泛利用的标准,于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高等加密标准已然成为对称密钥加密中最盛行的算法之一。
AES是范例的对称加密算法,对称加密不同于md5 sha的哈希择要算法,对称加密是可逆的,常日是明文+密钥,再利用算法来加密成密文,如果要还原也很大略,只要根据密钥+密文+天生算法的逆运算,即可解出,对称加密特点为可逆,并且加密解密都是利用同一个密钥,而非对称加密则是公钥私钥加解密模式这里不做谈论。
aes加密五种模式
aes加密的办法有五种事情系统编制。
1.电码本模式(Electronic Codebook Book (ECB))
这种模式紧张是将明文划分为几个明文段,分块加密,但是加密密钥是相同的。
2.密码分组链接模式(Cipher Block Chaining (CBC))
这种模式是先将明文切分成多少小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。
3.打算器模式(Counter (CTR))
4.密码反馈模式(Cipher FeedBack (CFB))
5.输出反馈模式(Output FeedBack (OFB))
个中分组如,aes-128-ecb即为16字节为一组,16字节即为128位。
其他三种模式较为繁芜,本文仅谈论前两种加密的安全性。
aes-ecb加密
aes-ecb加密是将一段明文,按照固定长度分组,然后对每一个分组,按照算法利用固定的密钥进行加密。假设123456加密。那么123为一组加密,456为一组加密,然后两段明文加密后的密文拼在一起,就算完全的密文。
把稳:这里每一组的加密都是利用相同的密钥,相同的算法,以是在这种机制下,很可能涌现安全问题。
比如:在身份认证中,查询用户是否是管理员还是普通用户,如果is_root=1则为管理员,如果不为1则为普通用户,如果采取aes-ecb加密,对原文进行分组加密。
明文:user_id:1.000000 is_root:0(个中is_root来判断是否为管理员。) 然后用一段密钥加算法进行加密。
这种提交的加密数据是在cookie中提交,明文不可控,但是密文是可控的,但由于是进行分组进行,以是我们可以推算出每一分组明文对应的密文,假设明文八个一组来进行加密,分组后变为 (提示:仅仅是假设空想情形八位,实际并不是)
第一组:is_user 第二组:1.000000 第三组: is_root: 第四组: 0(不足的八位自动添补)个中user_id 常日情形下我们前端可以修正,进行修正为1.000000,此时原文被加密之后为四组 每组为八个数字的密文假设加密后密文为 c4ca4238a0b923820dcc509a6f75849b 在cookie中被提交,将密文分为四组c4ca4238a0b923820dcc509a6f75849b
此时密文我们是可控的,如果正常提交,做事器解密之后为user_id:1.000000 is_root:0,很显然我们不是管理员,但是如果将第二组密文和第四组密文更换呢,那么user_id便是0,is_root便是1.000000。做事器就解析为user_id:0xxxxxxx(xx为添补字符) is_root:1.000000,显然我们不须要知道密钥,同样可以进行绕过。
还有一则在转账中,如果采取aes-128-ecb加密,在cookie中利用ecb分组加密,比如
付款人账户:
XXX //假设密文abc
收款人账户:
XXX //假设密文efg
试想一下,一旦这个分组是刚好分为四组,我们仅仅将abc与efg交流,那不就造成了支付收款反转,险些不须要什么技能就可以造成严重的攻击。
ctf-案例
接下来以真实题目来进行详解。
ctf address:https://mixer-f3834380.challenges.bsidessf.net/(国外的一道ctf)
首先考试测验输入admin admin 上岸。
返回内容重点为赤色框内的东西,须要使得第三个参数 is_admin=1即可得到flag,但是session cookie并不是这个题关注的点,接下来便是抓包剖析参数。修正参数。
经测试修正url,get cookie post传参都不能改变is_admin的值,以是只有一种可能,是在cookie里的user参数里加密了,然后通报给做事器,我们get参数传入的账号密码被做事器端加密,然后做事器返回来加密后的user信息。
接下来测试是何种加密,测试为aes-ecb加密,那么是如何确定的呢,由于ecb是分组加密,以是一旦一组的密文我们修正了,其他组的密文解密之后是正常的,而被我们修正了的密文解密会是乱码,以是我们随便修正下user参数。
可以看到报错,并且第一组的密文解密后是乱码,而其他组的加密解密后为正常,以是预测这一定是aes-ecb的分组加密的办法,此时,我们该当先确定分组,几个为一组,先毁坏第一组加密然后毁坏第二组加密,然后确定解密后json数据为,
{\公众first_name\公众:\"大众admin\"大众,\"大众last_name\"大众:\"大众admin\"大众,\"大众is_admin\公众:0}
统共为55个字符,
做事器密文为:d37c125ab4eae2ed02428d6d619016b06500bafffbeebe0c011977ad06c6946a45ba82569e93332195a36e61ae1fe26b325f7afd1eaa5ee8bb11efe6eebc5b54
为128个字符,五十五个字符补位为64个字符,分组测试毁坏每一组,测试到一组明文16个字符,加密密文为32个字符。
明文分为四组,一组16个字符,密文分为四组,一组32个字符。
d37c125ab4eae2ed02428d6d619016b06500bafffbeebe0c011977ad06c6946a45ba82569e93332195a36e61ae1fe26b325f7afd1eaa5ee8bb11efe6eebc5b54
可控的范围是我们输入的账号密码 admin admin。
{“first_name”:” 为十五个字符,我们首先布局账号为 a1.0000000000000}
个中a是为了添补第一组,这样第一组便是{“first_name”:”a,这样剩下的1.0000000000000}便是十六个字符为一组,第二组便是1.0000000000000},这样做事器加密后返回的第33-64位加密便是1.0000000000000},我们让做事器帮我们加密,这样我们就不须要知道密钥和算法,让做事器帮我们加密任何我们想要的东西,提交数据。
可以看到做事器返回了加密后的内容。我们截取第33位-64位字符。即为1.0000000000000}的密文。
3af6e4a9e05c702b02f9f4288c1c605c
接下来便是须要添补位数。我们让做事器解密的json数据末了的0}为第65 66位,由于如果这样的话,前64位刚好是四组,65 66为一组,恰好将它32位的密文更换成我们布局的密文。
{“first_name”:”admin”,”last_name”:”admin”,”is_admin”:0}
五十五位的字符串,我们让好账号变为admin12345678900,那么字符串便是66位,恰好符合多余出来的两位是0},末了这两位被添补之后的密文同样是32位,这样就可以更换我们布局的32位密文。
可以看到做事器布局成功得到flag。
总结一下上面思路,我们根据每一组的加密密文长度固定明文长度固定,以是添补位数,然后让我们想要的数据成为单独的一组,让做事器进行加密,这样我们就可掌握任意明文加密,然后修正cookie里提交的密文,添补字节,让我们须要的密文位置成为单独的一组,然后更换我们之前布局的一组数据,这样就可以绕过。
此题值得一题的是双引号单引号反斜线等被过滤了,以是师傅们其他须要引入双引号等的不用考试测验了。
aes-cbc加密
这种模式是先将明文切分成多少小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。aes-
IV:用于随机化加密的比特块,担保纵然对相同明文多次加密,也可以得到不同的密文。
秘钥:用于加密。
密文块0:第一组密文被加密后的内容。(同样也是第二组明文加密过程中的IV)
cbc加密办法不难明得,将一串明文进行分组,举例 123456789
123为第一组,456为第二组,789为第三组,将123与IV异或加密(加密中IV只在第一次异或有用),得到的异或后的密文与密钥加密,假设此时第一组加密的终极密文为abc,那么456先于第一组的密文abc异或加密,得到的异或密文在与密钥加密,假设第二组终极密文为def,往来来往循环,def与第三组明文异或,然后和密钥加密,假设密文ghi,那么终极密文便是
abcdefghi并且将iv发送。
个中值得一提的是初始始化向量IV每次随即初始化,以是纵然相同的字符串也不会有相同的密文。
cbc字节反转攻击
那么这种在这种加密的办法下,并不屈安,问题出在异或加密这里,在讲解字节反转攻击前先理解下异或加密。
异或 xor 符号表示为 ^ ,打算机中 两个数字异或,相同为0,不同为1。 1^1=0 0^1=1
如果是字母异或加密,a^b,那么首先转化为ascii编码,然后二进制,对每一位进行异或得到的结果转为十进制,在ascii编码出来。
异或有一个特性,任意值与自己本身做异或运算的结果都是0,任意值与0做异或运算的结果都是自己。本身a^b=乱七八糟,a^a则为空,但是a^a^任意字母=任意字母。
在CBC解密中,如图A是第一组的密文,B是第二组被解密的密文(未异或),C是明文。C=A^B。那么B=C^A,且A^B^C=0。如果我们变动A,A为我们可控的密文,C=A^B,如果我们使A=B^X,B=C^A,以是A=C^A^X,C=C^A^X^B=B^X^B=X。这里X是我们须要的任意字符,这便是CBC字节反转攻击的核心,这样一来C的明文就完备可控了。
大略的登录-cbc字节反转
事理说了很多,那么接下来实战一下。
实验吧题目:http://ctf5.shiyanbar.com/web/jiandan/index.php
首先,输入框随便输入,然后发送要求抓包,看到返回包的头要求有tips,test.php。访问test.php即可看到源码。
<?phpdefine(\"大众SECRET_KEY\"大众, '');define(\"大众METHOD\"大众, \"大众aes-128-cbc\"大众);error_reporting(0);include('conn.php');function sqliCheck($str){ if(preg_match(\"大众/\|,|-|#|=|~|union|like|procedure/i\"大众,$str)){ return 1; } return 0;}function get_random_iv(){ $random_iv=''; for($i=0;$i<16;$i++){ $random_iv.=chr(rand(1,255)); } return $random_iv;}function login($info){ $iv = get_random_iv(); $plain = serialize($info); $cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);//$plain为要加密的明文,METHOD加密方法,SECRET_KEY是秘钥,OPENSSL_RAW_DATA为数据格式,$iv随机天生的初始化向量。 setcookie(\公众iv\公众, base64_encode($iv)); setcookie(\"大众cipher\"大众, base64_encode($cipher));}function show_homepage(){ global $link; if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])) { $cipher = base64_decode($_COOKIE['cipher']); $iv = base64_decode($_COOKIE[\"大众iv\公众]); if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)) { $info = unserialize($plain) or die(\"大众<p>base64_decode('\"大众.base64_encode($plain).\公众') can't unserialize</p>\"大众); $sql=\公众select from users limit \公众.$info['id'].\公众,0\"大众; $result=mysqli_query($link,$sql); if(mysqli_num_rows($result)>0 or die(mysqli_error($link))){ $rows=mysqli_fetch_array($result); echo '<h1><center>Hello!'.$rows['username'].'</center></h1>'; } else{ echo '<h1><center>Hello!</center></h1>'; } } else { die(\"大众ERROR!\公众); } }}if(isset($_POST['id'])){ $id = (string)$_POST['id']; if(sqliCheck($id)) die(\"大众<h1 style='color:red'><center>sql inject detected!</center></h1>\公众); $info = array('id'=>$id); login($info); echo '<h1><center>Hello!</center></h1>';}else{ if(isset($_COOKIE[\"大众iv\"大众])&&isset($_COOKIE['cipher'])){ show_homepage(); }else{ echo '<body class=\"大众login-body\公众 style=\公众margin:0 auto\"大众> <div id=\公众wrapper\公众 style=\"大众margin:0 auto;width:800px;\"大众> <form name=\公众login-form\公众 class=\"大众login-form\"大众 action=\公众\公众 method=\公众post\公众> <div class=\"大众header\公众> <h1>Login Form</h1> <span>input id to login</span> </div> <div class=\"大众content\"大众> <input name=\公众id\"大众 type=\公众text\"大众 class=\"大众input id\公众 value=\"大众id\公众 onfocus=\"大众this.value=''\公众 /> </div> <div class=\"大众footer\"大众> <p><input type=\"大众submit\"大众 name=\公众submit\"大众 value=\"大众Login\"大众 class=\"大众button\"大众 /></p> </div> </form> </div> </body>'; }}?>
条件:这一关不是纯挚注入饶过的,肯定要利用cbc字节反转攻击。
1.首先直接看在哪里可以得到flag,没传入ID参数的时候,如果cookie建立了iv 和 cipher参数,那么就可以调用show_homepage,实行sql查询,flag在数据库里查询。
2.但是肯定要传参id,师长西席成iv 和 cipher,将id=X该数组进行序列化之后,以序列化结果和一个bs64编码随机数iv进行cbc加密天生密文cipher,加密算法为aes-128-cbc,此时就要考虑cbc字节反转了,128位,按十六字节分组。天生iv和cipher之后url编码返回要求头,天生细节参考自定义login函数。
3.sql查询语句拼接了一个0,以是我们只要注释掉0便可进行我们的查询。以是可以利用cbc字节翻转攻击变动密文,变动解密后的id,从而绕过进行sqlwaf,cookie传入参数 cipher和iv,base64解码然后aes解密,php反序列化,如果不能反序列化,则输出base64编码,否则就sql语句拼接查询。如果有结果回显,否则输出hello。
综上,只要我们能够CBC进行字节反转就可以实行sql查询,就可以进行查询flag。
接下来第一步首先要cbc字节反转,修正密文中的id。不妨先测试下位数,如果传入id=12(由于我们要修正为1#),则序列化后内容为
a:1:{s:2:\"大众id\"大众;s:2:\"大众12\"大众;}
由于我们须要分组,aes-128-cbc,128位16字节分组
第一组: a:1:{s:2:\"大众id\公众;s:
第二组: 2:\"大众12\"大众;}
10中的0是第二组的第五个字符,以是须要变动第5个字符,右偏移四个字符,第一组也要向右偏移四个字符。接下来便是cbc字节反转脚本。
# -- coding:utf8 --
from base64 import
import urllib
cipher='fn060OBP%2FyLIGYrD9bi%2FlWWAS9RIWvEtALaV26kuB%2F8%3D'#加密后的密文
cipher_raw=b64decode(urllib.unquote(cipher))#首先urldecode解码,然后base64解码
cipher_raw_list=list(cipher_raw)#将解码的密文分组
py=4#偏移量为4
A=cipher_raw_list[py]#要异或第二组密文的位置
C='2'#第二组被更换的明文
X='#'#将第二组更换掉的明文
cipher_raw_list[py]=chr(ord(A)^ord(C)^ord(X))#将偏移量为4的更换。
cipher_new=''.join(cipher_raw_list)#利用''将每一个字符连接起来,
cipher_new=urllib.quote(b64encode(cipher_new))#将更换完的密文base64编码,urlencode编码。
print cipher_new#打印出终极密文
个中特意将ACX等变量对应上文所讲的参数。可参考上面cbc字节反转合营图来理解。然后天生反转后的密文:
fn060PFP/yLIGYrD9bi/lWWAS9RIWvEtALaV26kuB/8%3D
此时提交密文发送做事器会返回base64编码字符串无法反序列化。
缘故原由为下面这句。
接下来我们须要修正IV,事理很大略,我们分为两组来进行加解密,第一组密文只参与第二组的异或,第一组修正完成后,第二组的解密是完备没有问题的,但是第一组被我们修正了一个字符,但是异或的IV还是原来的IV,必须要修正IV才能使第一组正常异或,得到结果。还是上述事理,三次异或,掌握想要的结果。
这里在看图,
A:这里特殊要解释把稳,A是我们第一次字节反转之后的明文(序列化状态)
B:原来的IV
C:字节反转后解密后的第一组(未被异或)
D: 正常的序列化字符串 ‘a:1:{s:2:”id”;s:’
E:新的IV
A=B^C,由于我们A是字节反转这里我们可以看到,IV是原来的IV,但是A和C都是字节反转后的,以是A一定是个无法反序列化的明文,我们修正B也便是IV,使得异或得到正常的序列化字符串。
B=A^C,我们须要得到的结果是D=E^C,而C=B^A,以是D=E^B^A,那么E=B^A^D。 //建议初学者自己多剖析下逻辑,多写写,干想很头疼。
接下来是IV修正的脚本。
# -- coding:utf8 --
__author__='pcat@chamd5.org'
from base64 import
import urllib
iv='erUDGVSvM4Kab3ztg8vT8Q%3D%3D'
B=b64decode(urllib.unquote(iv))
D='a:1:{s:2:\"大众id\公众;s:'
A=b64decode('eFoXA0j/x2Em/bhfgeLzXjI6IjEjIjt9')
iv_new=''
for i in range(16):
iv_new+=chr(ord(A[i])^ord(D[i])^ord(B[i]))
iv_new=urllib.quote(b64encode(iv_new))
print iv_new
更换掉原来的IV,即可正常sql查询。
至此,此题的cbc反转我们已经完成了,剩下的注入事理一样,注入不是本题的目的,也就不再发剩下的脚本了。CBC还是要自己写一下用图理解一下。
别的加密问题,后续我会补充到本文。