iscc2018線上賽開始兩週多了,學到了不少,寫幾篇文章總結一下遇到的知識點,作一個概括,方便之後查找。php
web300-----CBC字節翻轉攻擊python
cbc是AES加密的cbc模式 即密碼分組鏈模式:web
先將銘文切分紅若干小段,而後每一小段與初始塊或者上一段的密文段進行異或運算後,在於密鑰進行加密。數組
直接看圖理解一下加解密原理:cookie
明文以16個字節爲一組進行分組,給出初始化向量iv於明文進行異或而後利用密鑰key在進行加密獲得密文a,密文a就至關於下一段明文的初始化向量,以此類推。session
解密時,一樣將密文分組,每組密文先利用密鑰解密在與「iv」異或獲得明文,過程相似加密。app
咱們的攻擊發生在解密的時候,即咱們能夠控制解密時所生成的明文。dom
根據解密方式咱們能夠知道,A=ciphertext(N-1),B=plaintext(N),C爲第N塊待異或且通過解密的字符,C'爲咱們通過翻轉要獲得的明文。post
因此咱們能夠打獲得關係:學習
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" onfocus="this.value=\'\'" /> <input name="password" type="password" class="input password" value="Password" onfocus="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。
理解的時候大費周章,記錄的時候也仍是有點難寫的 有的地方不知道怎麼說, 以此記錄學習成果。
任重而道遠。