CBC字节翻转攻击
iscc2018線上賽開始兩周多了,學到了很多,寫幾篇文章總結一下遇到的知識點,做一個歸納,方便以后查找。
web300-----CBC字節翻轉攻擊
cbc是AES加密的cbc模式 即密碼分組鏈模式:
? ? ?先將銘文切分成若干小段,然后每一小段與初始塊或者上一段的密文段進行異或運算后,在于密鑰進行加密。
直接看圖理解一下加解密原理:
明文以16個字節為一組進行分組,給出初始化向量iv于明文進行異或然后利用密鑰key在進行加密得到密文a,密文a就相當于下一段明文的初始化向量,以此類推。
解密時,同樣將密文分組,每組密文先利用密鑰解密在與“iv”異或得到明文,過程類似加密。
我們的攻擊發生在解密的時候,即我們可以控制解密時所生成的明文。
根據解密方式我們可以知道,A=ciphertext(N-1),B=plaintext(N),C為第N塊待異或且經過解密的字符,C'為我們經過翻轉要得到的明文。
所以我們可以打得到關系:
A = B ^ C
C = A ^ B
A ^ B ^ C = 0
A ^ B ^ C ^ C' = C'
根據關系式可以得到 A' = A ^ C ^ C'
所以說我們只需要修改前一組密文所對應的本組明文相同位置的字符,即可得到想要的明文。
下面貼上源代碼以便理解:
<?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 $flag;}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" οnfοcus="this.value=\'\'" /><input name="password" type="password" class="input password" value="Password" οnfοcus="this.value=\'\'" /></div><div class="footer"><input type="submit" name="submit" value="Login" class="button" /></div></form></div></body>';} } ?>閱讀源代碼,我們可以知道,只有admin用戶才能讀取flag,但是admin用戶又不允許登錄。雖然相互矛盾,由于題目用到了aes的cbc模式加密,所以我們可以利用cbc字節翻轉攻擊來得到我們想要的明文。我們來看一下流程:
以賬號 admiN 密碼 123456登錄
題目將用戶名密碼傳入數組并序列化得到a:2:{s:8:"username";s:5:"admiN";s:8:"password";s:5:"123456";
接下來進行aes加密,并將得到的cipher和iv進行base64編碼放入cookie中(cookie對于攻擊者來說可控,所以存在cbc字節翻轉攻擊)
明文加密時分組為:
a:2:{s:8:"userna me";s:5:"admiN"; s:8:"password";s :6:"123456";}因此我們只需要將"N"字節翻轉為"n"即可得到flag。
根據我們得到的關系,已知只需修改前一組密文即可。
$newcipher[13]=chr(ord(13) ^ ord('N') ^ ord('n'))
這時我們就會得到
a:2:{s:8:"username";s:5:"admin";s:8:"password";s:5:"123456";
但是由于前一組密文被修改了 所以前一組的明文會出現亂碼,因此接下來我們再生成新的iv將前一組明文改回a:2:{s:8:"userna 即可得到flag。
寫了一個腳本(代碼爛的不行? 湊活看....)
#-*- coding:utf8 -*- import base64 import urllib # a:2:{s:8:"userna # me";s:5:"admiN"; # s:8:"password";s # :6:"123456";}def Module1():ciphertext = raw_input("Please input the first round cipher:\n")cipher = base64.b64decode(urllib.unquote(ciphertext))new_cipher = cipher[:13] + chr(ord(cipher[13]) ^ ord('N') ^ ord('n')) + cipher[14:]print urllib.unquote(base64.b64encode(new_cipher))def Module2():errorcipher = base64.b64decode(urllib.unquote(raw_input('Please input errorcipher: \n')))ivtext = raw_input("Please input iv:\n")iv = base64.b64decode(urllib.unquote(ivtext))cleartext = 'a:2:{s:8:"userna'newiv = ''for i in range(16):newiv += chr(ord(iv[i]) ^ ord(errorcipher[i]) ^ ord(cleartext[i]))print urllib.unquote(base64.b64encode(newiv))option = raw_input("Please input option [1 or 2]:") if option == '1':Module1() elif option == '2':Module2() else:pass?
先登錄網站
在地址欄處回車,抓包得到cipher和iv。
?
運行腳本 選擇 1;
?復制cipher值粘貼到腳本中 得到新的cipher。
用新的cipher替換數據包中的cipher
?
?
這個時候我們的admiN其實已經變成了admin了? 接下來就是生成新的iv。(將報錯的信息解碼看一下)
?
重新抓包
將iv和cipher改為 新生成的iv和之前生成的cipher發包 得到flag。
?
?理解的時候大費周章,記錄的時候也還是有點難寫的? 有的地方不知道怎么說, 以此記錄學習成果。
任重而道遠。
?
轉載于:https://www.cnblogs.com/s1ye/p/9021202.html
總結
- 上一篇: May 18:PHP 用到的学习工具
- 下一篇: IHttpHandler的学习(0-2)