[NPUCTF2020]webdog
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <?php error_reporting (0 );include ('config.php' ); define ("METHOD" , "aes-128-cbc" ); define ("SECRET_KEY" , $key ); define ("IV" ,"6666666666666666" ); define ("BR" ,'<br>' );if (!isset ($_GET ['source' ]))header ('location:./index.php?source=1' );function aes_encrypt ($iv ,$data ) { echo "--------encrypt---------" .BR; echo 'IV:' .$iv .BR; return base64_encode (openssl_encrypt ($data , METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv )).BR; } function aes_decrypt ($iv ,$data ) { return openssl_decrypt (base64_decode ($data ),METHOD,SECRET_KEY,OPENSSL_RAW_DATA,$iv ) or die ('False' ); } if ($_GET ['method' ]=='encrypt' ){ $iv = IV; $data = $flag ; echo aes_encrypt ($iv ,$data ); } else if ($_GET ['method' ]=="decrypt" ) { $iv = @$_POST ['iv' ]; $data = @$_POST ['data' ]; echo aes_decrypt ($iv ,$data ); } echo "我摊牌了,就是懒得写前端" .BR;if ($_GET ['source' ]==1 )highlight_file (__FILE__ );?>
Padding oracle attack 与CBC翻转字节攻击 异或(XOR) 何为异或? 异或是一种运算方法,简要概括就是同假异真
代码语言:javascript
复制
1 2 3 4 5 6 7 8 9 10 11 12 true xor true = false ; true xor false = true ;在二进制中: 011 xor 110 = 101 ; 二进制数每位数分别进行xor运算上面算式的运算步骤是这样的 0 xor 1 = 1 ;1 xor 1 = 0 ;1 xor 0 =1 然后把运算结果写在一起就是101 了 在更高的进制中: 以十进制为例: 65 xor 42 = 107 ; 原理就是把十进制数字先变为二进制数进行异或,再将异或得到的二进制数结果变为十进制数 在字符中: 对字符进行异或运算其实就是对字符的ascii码进行异或计算,计算得到的结果视作新的一个ascii码再将其转换为字符。
xor还有一个性质,就是 已知
1 a xor b = c 那么 b xor c =a; a xor c = b.
即满足异或运算里只需知道任意两个数就能得到另一个数。
CBC模式
CBC是一种加密模型,采用的是分组链接模式,把明文分为一组一组的进行加密
上图为CBC加密流程,最开始一个特别别分组IV(初始向量)去和第一段密文XOR,得到的结果呗密钥加密,加密得到该组的密文,同时这个密文会充当最开始的特别分组IV的作用去参与下一组的加密,以此类推。
CBC的每一个分组的加密结果都可以影响到下一个分组的加密结果,使原本独立的分组密码加密过程形成迭代,这可以强化加密算法的“敏感性”,即实现所谓的“雪崩效应”,在香浓理论中这就是”扰乱原则“。
CBC只是一种模式,他经常把aes或des作为加密使用的算法,DES分组长度式八字节而AES分组长度式十六字节。
接下来就是CBC解密
顺序依旧是从左到右,第一组密文被KEY解密后与IV xor得到第一组明文,同时第一组密文参与下一组的解密充当IV的作用
PKCS #5 竟然CBC模式涉及到分组,那么就会出现最后一组字节没有被占满的情况。 比如原本一个分组是8字节,我们有15个字节的明文需要被加密,此时最后一个分组就不会被占满(还差一个字节占满),那么这个时候要怎么办呢?
这时候就需要对最后一个分组进行填充,将其填充满。 对于采用des算法加密的内容,填充规则是PKC #5,而AES是 PKC #7. 这两者唯一区别是 PKCS #5填充是八字节分组而PKCS #7是十六字节 ,还记得上面我们说过的 DES分组长度是八字节而AES分组长度是十六字节 吗?就是这个分组字节数影响了填充方式。
那么具体是怎么填充的呢,我们以PKC #5为例
当最后一组还剩n个字节未被填充时,就会填充n个 0xn字符上去. 上图是PKCS #5,其实PKCS #7和PKCS #5原理是一样的,不过是分组字节数大了点罢了(0~16)
就是如果是alsdawgfqa这个按照4个一组的话就留了qa两个,在qa后加上\x02\x02,若是删除a,只有q,那就是\x03\x03\x03
Padding oracle attack 攻击场景举例 首先我们假设一个场景,从而引出这个攻击。
假设我们有一个任意文件包含场景
代码语言:javascript
复制
黑客们看见了就会很轻而易举的去包含想要的文件。管理者发现了这个问题,对file参数采用了CBC加密,即当提交请求时,file参数的值是被加密的,然后服务器 用算法解密得到其想包含的文件,然后返回给客户端。
代码语言:javascript
复制
1 url?file=e28b2e3c972edab8 其中前8 位数是IV ,后八位数是密文。(这里是我瞎写的密文= =,你只需要理解到这里是一个CBC 加密后的密文就行了)
那么如何去实现我们的任意文件包含呢? padding oracle attack 出现了。
攻击原理 假设我们向刚刚那个任意文件包含的提交了一段密文。服务器就会尝试解密,就会出现三种结果。
1.密文不能正常解密,这种原因是在于最后一组的填充字节出现了错误 2.密文能正常解密但解密出来的文件路径不存在 3.密文能正常解密且能成功包含
其中第1种情况和2.3种情况网页返回的内容肯定是不同的。 比如说第一种情况可能就直接返回500了,2.3可能就是302跳转啥啥的,通过这个网页返回的信息,我们就有了可乘之机。
我们先通过一个图感受一下第一组的解密流程
好的好的,感受了这个解密流程后,我们来说说攻击的事。 如果我们得到了 Intermediary Value(中间值),并且可以手动修改IV,那么我们岂不是可以构造任意Decrypted Value(明文)了? 所以padding oracle attack 的核心就是去获得中间值。 那么我们怎么去获取呢?
还记得刚刚提到的3种情况吗?我们可以通过修改IV,通过判断网页返回内容来判断中间值,具体做法如下:
我们先把IV全部设置为0x00,然后修改IV的最后一个数,当其与中间值XOR后的值为0x01则此时解密就会成功,若不是0x01解密就会失败,网页会返回不同的内容,以此来判断何时解密成功。然后把解密成功时的IV的最后一位数与0x01进行异或计算,即可得到中间值的最后一位
然后我们把IV最后一位数设置为能和中间值最后一位数异或后值为0x02的数,穷举IV倒数第二个数看看哪个数能和中间值倒数第二个数异或运算后值为0x02,然后我们就可得到中间值倒数第二个数,以此类推可以获得第一组的中间值(有点绕) 然后破解到了中间值我们再用最开始的IV(不是我们后面构造的IV)去和中间值异或就得到明文了 当然,你也可以再次构造IV,从而构造解密出来的字符(通过中间值与IV异或)
先获取密文:
1 ly7auKVQCZWum/W/4osuPA==
然后对其进行base64解密,发现其刚好是有16个字节,那么我们可以直接穷举IV得到中间值,然后凭此与初始IV(16个6)进行异或得到明文$flag.
话不多说,爆破中间值的脚本安排上。(借鉴了一个大佬的WP)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import requestsimport base64import timeIntermediary="" url="http://de1650aa-2b24-40e0-bb51-736ff5d38269.node3.buuoj.cn//index.php?source=1&method=decrypt" iv="" hexs="" IV="6666666666666666" def xor (a,b ): return "" .join([chr (ord (a[i])^ord (b[i])) for i in range (len (a))]) for step in range (1 ,17 ): padding=chr (step)*(step-1 ) print ("第%s轮" %step) for i in range (0 ,256 ): iv=chr (0 )*(16 -step)+chr (i)+xor(Intermediary,padding) post={ "iv" :iv, "data" :"ly7auKVQCZWum/W/4osuPA==" } r=requests.post(url=url,data=post,proxies={"http" :"http://127.0.0.1:8080" }) time.sleep(0.1 ) print (r.text+"第%s轮i=%s " %(step,i)) if "False" != r.text: Intermediary=xor(chr (i),chr (step))+Intermediary print (Intermediary) break for k in range (len (Intermediary)): hexs="%" +str (ord (Intermediary[k]))+hexs print (hexs)print (xor(Intermediary,IV))
爆出了中间值以及$flag明文
CBC翻转字节攻击 在对CBC模式加密的数据进行解密时,若iv可控,则可以任意控制解密后的内容。 CBC翻转字节攻击不同于padding oracle attack,后者的核心是IV可控情况下获取中间值,从而可以获得明文或者任意控制密文解密后的数据。 而CBC翻转字节攻击的核心思想就不是获取中间值了,而是在IV可控的情况下,通过算法缺陷来直接控制密文解密后的数据。
现在假定有中间值A,明文B1,IV C1
那么就有
代码语言:javascript
复制
一点错误都没有对吧。 现在又假定有中间值A,我们想要解密出的明文B2和与之对应的IV C2 那么就有
代码语言:javascript
复制
1 2 3 4 5 6 7 8 A^B2 =C2 结合以上两个式子,有 A=B1 ^C1 =B2 ^C2 于是有 B2 =B1 ^C1 ^C2 或C2 =B1 ^B2 ^C1 若我们已知B1 ,C1 ,且C2 可控,那么B2 即可控 既满足刚刚我们说的,通过算法缺陷来直接控制密文解密后的数据。
FlagIsHere.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 ryino46QYgekMUTu0JFPMQ== <?php include ('config.php' ); define ("METHOD" , "aes-128-cbc" );define ("SECRET_KEY" , "6666666" );session_start ();function get_iv ( ) { $random_iv ='' ; for ($i =0 ;$i <16 ;$i ++){ $random_iv .=chr (rand (1 ,255 )); } return $random_iv ; } $lalala = 'piapiapiapia' ;if (!isset ($_SESSION ['Identity' ])){ $_SESSION ['iv' ] = get_iv (); $_SESSION ['Identity' ] = base64_encode (openssl_encrypt ($lalala , METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $_SESSION ['iv' ])); } echo base64_encode ($_SESSION ['iv' ])."<br>" ;if (isset ($_POST ['iv' ])){ $tmp_id = openssl_decrypt (base64_decode ($_SESSION ['Identity' ]), METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode ($_POST ['iv' ])); echo $tmp_id ."<br>" ; if ($tmp_id ==='weber' )die ($fl4g ); } highlight_file (__FILE__ );?>
此时我们已知初始IV,初始密文,且IV可控,那么CBC翻转字节攻击条件成立,可以攻击。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 这里贴一个别人写的python2 CBC字节反转攻击脚本 自己拿python3写了半天都没写出很好的效果... import base64def bxor (b1, b2 ): parts = [] for b1, b2 in zip (b1, b2): parts.append(bytes ([b1 ^ b2])) return b'' .join(parts) iv = base64.b64decode("h34HL5RbMPw8oTaQ+P58nw==" ) text = b"piapiapiapia\x04\x04\x04\x04" result = b"weber\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" middle = bxor(iv,text) iv = bxor(middle,result) print (base64.b64encode(iv))
把跑出来的结果POST过去,就会得到下一步了..
直接有了链接,这个是buu的话,直接下载附件就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class HelloWorld { public HelloWorld () { } public static void main (String[] var0) { System.out.println("众所周知,你是一名WEB选手,掌握javaweb也是一项必备技能,那么逆向个java应该不过分吧?" ); byte [] var10000 = new byte []{102 , 108 , 97 , 103 , 123 , 119 , 101 , 54 , 95 , 52 , 111 , 103 , 95 , 49 , 115 , 95 , 101 , 52 , 115 , 121 , 103 , 48 , 105 , 110 , 103 , 125 }; } }
获取了这个class文件,有一些字节东西可以查看
1 2 3 4 a = bytearray ( [102 , 108 , 97 , 103 , 123 , 119 , 101 , 54 , 95 , 52 , 111 , 103 , 95 , 49 , 115 , 95 , 101 , 52 , 115 , 121 , 103 , 48 , 105 , 110 , 103 , 125 ] ) print (a.decode())
获取flag
参考链接:https://cloud.tencent.com/developer/article/1942522
参考链接:https://blog.csdn.net/weixin_43610673/article/details/105898440