我的簡介:
渣渣一枚,萌新一個,會划水,會喊六六
今天在bugku
遇到關於CBC翻轉攻擊
的題目,總結了一下關於CBC翻轉攻擊
的原理,以及關於這道題目的解題思路
我的博客:https://www.cnblogs.com/lxz-1263030049/javascript
CBC翻轉攻擊
的主要目的:經過損壞密文字節來改變明文字節。(注:藉助CBC內部的模式
) 經過添加單引號等惡意字符來繞過過濾器,或經過將用戶ID更改成admin來提高權限,或者更改應用程序所需的明文的任何其餘後果。php
上圖CBC加密原理圖
1:Plaintext
:待加密的數據。
2:IV
:用於隨機化加密的比特塊,保證即便對相同明文屢次加密,也能夠獲得不一樣的密文。
3:Ciphertext
:加密後的數據
4:Key
:分組加密使用的密鑰
這裏重要的一點是CBC
在一個固定長度的位組上工做,稱爲塊。在本文中,咱們將使用每一個16字節的塊。
整個加密的過程簡單說來就是:css
- 首先將明文分組(常見的以16字節爲一組),位數不足的使用特殊字符填充。
- 生成一個隨機的初始化向量(IV)和一個密鑰。
- 將IV和第一組明文異或。
- 用密鑰對3中xor後產生的密文加密。
- 用4中產生的密文對第二組明文進行xor操做。
- 用密鑰對5中產生的密文加密。
- 重複4-7,到最後一組明文。
- 將IV和加密後的密文拼接在一塊兒,獲得最終的密文。
從第一塊開始,首先與一個初始向量IV異或(IV只在第一處做用),而後把異或的結果配合Key進行加密,獲得第一塊的密文,而且把加密的結果與下一塊的明文進行異或,一直這樣進行下去。所以這種模式最重要的特色就是:前一塊的密文用來產生後一塊的密文。
html
解密的過程其實只要理解了加密,反過來看解密過程就也很簡單了,一樣的,前一塊密文參與下一塊密文的還原。java
- 從密文中提取出IV,而後將密文分組。
- 使用密鑰對第一組的密文解密,而後和IV進行xor獲得明文。
- 使用密鑰對第二組密文解密,而後和2中的密文xor獲得明文。
- 重複2-3,直到最後一組密文。
下圖是爲解釋翻轉攻擊的原理圖:
這裏能夠注意到前一塊Ciphertext
用來產生下一塊明文,若是咱們改變前一塊Ciphertext
中的一個字節,而後和下一塊解密後的密文xor
,就能夠獲得一個不一樣的明文,而這個明文是咱們能夠控制的。利用這一點,咱們就欺騙服務端或者繞過過濾器
再解釋一下:
根據解密方式咱們能夠知道,A=ciphertext(N-1)
,B=plaintext(N)
,C爲第N塊待異或且通過解密的字符,C'爲咱們通過翻轉要獲得的明文。
因此咱們能夠打獲得關係:jquery
A = B ^ C C = A ^ B A ^ B ^ C = 0 A ^ B ^ C ^ C' = C'
根據關係式能夠獲得:A' = A ^ C ^ C'
因此說咱們只須要修改前一組密文所對應的本組明文相同位置的字符,便可獲得想要的明文nginx
下面就是關於Bugku的題目
http://118.89.219.210:49168/
這一題屬於常規思路,但是不容易想到,我記得之前作過相似的題目,畢竟是萌新總會有不少知識點會忘記(QAQ)
使用備份文件腳本進行掃描就會獲得:腳本代碼:django
[hide]import requests import sys url = 'http://118.89.219.210:49168/' import threading a = ['bak','zip','rar','tar.gz','txt'] b = ['swp','swo','swn'] c = ['index.php', 'flag.php', 'profile.php','login.php'] s = requests.session() proxies = { 'http':None, 'https':None } headers = {"Cookie": "PHPSESSID=sclfgjri76captre7cvq6g4170","Accept": "text/html,application/xhtml+xml,application/xml;", "Accept-Encoding": "gzip", "Accept-Language": "zh-CN,zh;q=0.8", "Referer": "http://www.example.com/", "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36"} for i in c: nurl = url +i+ '~' r = s.get(nurl,proxies=proxies) if str(r) != '<Response [404]>': print nurl sys.stdout.flush() for j in range(0,len(a)): nurl = url + i +'.'+ a[j] r = s.get(nurl, proxies=proxies) if str(r) != '<Response [404]>': print nurl sys.stdout.flush() for j in range(0, len(b)): nurl = url + '.'+ i +'.'+ b[j] r = s.get(nurl, proxies=proxies) if str(r) != '<Response [404]>': print nurl sys.stdout.flush() print 'finish'[/hide]
運行以後就會獲得:
打開連接發現是應該能夠下載的文件而且文件是以.swp
爲後綴名
關於.swp
文件:
使用vi
,常常能夠看到.swp
這個文件。那這個文件是怎麼產生的呢,當打開一個文件,vi
就會生成這麼一個.(filename)swp
文件 以備不測(好比非正常退出),若是你正常退出,那麼這個這個.swp
文件將會自動刪除
怎麼恢復.swp
:
可使用vim
vim -r:命令來查看當前目錄下的全部swp文件
vi -r {your file name} :命令恢復文件 rm .{your file name}.swp:命令刪除swp文件,否則每一次編輯時老是有這個提示。
就會獲得html文檔:
看到該題的代碼:cookie
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">; <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Login Form</title> <link href="static/css/style.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="static/js/jquery.min.js"></script> <script type="text/javascript"> $(document).ready(function() { $(".username").focus(function() { $(".user-icon").css("left","-48px"); }); $(".username").blur(function() { $(".user-icon").css("left","0px"); }); $(".password").focus(function() { $(".pass-icon").css("left","-48px"); }); $(".password").blur(function() { $(".pass-icon").css("left","0px"); }); }); </script> </head> <?php define("SECRET_KEY", file_get_contents('/root/key')); define("METHOD", "aes-128-cbc"); session_start(); 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); $_SESSION['username'] = $info['username']; setcookie("iv", base64_encode($iv)); setcookie("cipher", base64_encode($cipher)); } function check_login(){ 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>"); $_SESSION['username'] = $info['username']; }else{ die("ERROR!"); } } } function show_homepage(){ if ($_SESSION["username"]==='admin'){ echo '<p>Hello admin</p>'; echo '<p>Flag is $flag</p>'; }else{ echo '<p>hello '.$_SESSION['username'].'</p>'; echo '<p>Only admin can see flag</p>'; } echo '<p><a href="loginout.php">Log out</a></p>'; } if(isset($_POST['username']) && isset($_POST['password'])){ $username = (string)$_POST['username']; $password = (string)$_POST['password']; if($username === 'admin'){ exit('<p>admin are not allowed to login</p>'); }else{ $info = array('username'=>$username,'password'=>$password); login($info); show_homepage(); } }else{ if(isset($_SESSION["username"])){ check_login(); show_homepage(); }else{ echo '<body class="login-body"> <div id="wrapper"> <div class="user-icon"></div> <div class="pass-icon"></div> <form name="login-form" class="login-form" action="" method="post"> <div class="header"> <h1>Login Form</h1> <span>Fill out the form below to login to my super awesome imaginary control panel.</span> </div> <div class="content"> <input name="username" type="text" class="input username" value="Username" /> <input name="password" type="password" class="input password" value="Password" /> </div> <div class="footer"> <input type="submit" name="submit" value="Login" class="button" /> </div> </form> </div> </body>'; } } ?> </html>
GO
一下能夠看到cookie
中返回的iv
和cipher
將輸入序列化得s:2:{s:8:"username";s:5:"adcin";s:8:"password";s:3:"123";}
而後每16字節分組得
1: a : 2 : { s : 8 : " u s e r n a
2: m e " ; s : 5 : " a d c i n " ;
3: s : 8 : " p a s s w o r d " ; s
4: 3 : " 1 2 3 " ; }
可見,若是咱們想要將2中的c
變成m就須要對1中的s
進行改變,使用腳本:
[hide]# -*- coding: utf-8 -*- import base64,urllib def get_newCipher(): cipher = ''#輸入所得cipher cipher = base64.b64decode(urllib.unquote(cipher)) newCipher = cipher[0:x] + chr(ord(cipher[x])^ord('')^ord('')) + cipher[x+1:]#x爲須要改變值所在的字節數,第二個ord中爲輸入值,第三個ord中爲目標值 print urllib.quote(base64.b64encode(newCipher)) def get_newIV(): cipher = ''#get_newCipher提交後所得的沒法反序列化密文 iv = ''#所得iv #cipher = urllib.unquote(cipher) cipher = base64.b64decode(cipher) iv = base64.b64decode(urllib.unquote(iv)) newIv = '' right = ''#被損壞前正確的明文 for i in range(16): newIv += chr(ord(right[i])^ord(iv[i])^ord(cipher[i])) print urllib.quote(base64.b64encode(newIv)) if __name__ == '__main__': #get_newCipher() #get_newIV()[/i][/i][/i][/hide][i][i][i]
發現新密文沒法反序列化,這是由於,咱們將c修改爲m時破壞1中的結構
因而咱們將新獲得的密文複製,經過base64解密
事後的iv與新密文解密的明文與原1中數據對應異或
提交新的iv
和剛纔獲得的cipher
便可