實驗吧 簡單的登入題 之[ CBC翻轉攻擊 ] 詳解
解題連結: http://ctf5.shiyanbar.com/web/jiandan/index.php
1)Burpsuite抓包
2)根據Tips,修改GET請求為test.php獲得原始碼。
3)程式碼審計
這個是實驗吧的原始碼:
define("SECRET_KEY", '***********');
define("METHOD", "aes-128-cbc");
error_reporting(0);
include('conn.php');
function sqliCheck($str){
if (preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)){
return 1;
}
return 0;
}
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);
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher));
}
function show_homepage(){
global $link;
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>");
$sql="select * from users limit ".$info['id'].",0";
$result=mysqli_query($link,$sql);
if(mysqli_num_rows($result)>0 or die(mysqli_error($link))){
$rows=mysqli_fetch_array($result);
echo '<h1><center>Hello!'.$rows['username'].'</center></h1>';
}
else{
echo '<h1><center>Hello!</center></h1>';
}
}else{
die("ERROR!");
}
}
}
if(isset($_POST['id'])){
$id = (string)$_POST['id'];
if(sqliCheck($id))
die("<h1 style='color:red'><center>sql inject detected!</center></h1>");
$info = array('id'=>$id);
login($info);
echo '<h1><center>Hello!</center></h1>';
}else{
if(isset($_COOKIE["iv"])&&isset($_COOKIE['cipher'])){
show_homepage();
}else{
echo '<body class="login-body" style="margin:0 auto">
<div id="wrapper" style="margin:0 auto;width:800px;">
<form name="login-form" class="login-form" action="" method="post">
<div class="header">
<h1>Login Form</h1>
<span>input id to login</span>
</div>
<div class="content">
<input name="id" type="text" class="input id" value="id" onfocus="this.value=\'\'" />
</div>
<div class="footer">
<p><input type="submit" name="submit" value="Login" class="button" /></p>
</div>
</form>
</div>
</body>';
}
}
4) 以下是題目:http://118.89.219.210:49168/index.php 的原始碼
因為正在學習CBC翻轉攻擊,查閱了csu_vc的文章
所以暫時先按他的文章的原始碼來分析CBC翻轉的題目。
實驗吧的題目以後更新。
<!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" 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>';
}
}
?>
5)可以簡單地畫一個流程圖:
解題思路:
1)在於考察aes-128-cbc演算法的“CBC翻轉攻擊“漏洞
2)由原始碼我們可知:
2.1)Flag只有Session = admin可以檢視
2.2)我們不可以通過POST傳參的方式Set Session = admin
2.3)我們可以通過CBC演算法的翻轉攻擊漏洞 Set Session = admin
3)CBC翻轉攻擊原理:
上圖CBC加密的原理圖
1.Plaintext: 待加密的資料。
2.IV: 用於隨機化加密的位元塊,保證即使對相同明文多次加密,也可以得到不同的密文。
3.Ciphertext: 加密後的資料。
在這裡重要的一點是,CBC工作於一個固定長度的位元組,將其稱之為塊。在本文中,我們將使用包含16位元組的塊。
整個加密的過程簡單說來就是:
1.首先將明文分組(常見的以16位元組為一組),位數不足的使用特殊字元填充。
2.生成一個隨機的初始化向量(IV)和一個金鑰。
3.將IV和第一組明文異或。
4.用金鑰對3中xor後產生的密文加密。
5.用4中產生的密文對第二組明文進行xor操作。
6.用金鑰對5中產生的密文加密。
7.重複4-7,到最後一組明文。
8.將IV和加密後的密文拼接在一起,得到最終的密文。
從第一塊開始,首先與一個初始向量iv異或(iv只在第一處作用),然後把異或的結果配合key進行加密,得到第一塊的密文,並且把加密的結果與下一塊的明文進行異或,一直這樣進行下去。因此這種模式最重要的特點就是:前一塊的密文用來產生後一塊的密文。
這是解密過程,解密的過程其實只要理解了加密,反過來看解密過程就也很簡單了,同樣的,前一塊密文參與下一塊密文的還原。
1.從密文中提取出IV,然後將密文分組。
2.使用金鑰對第一組的密文解密,然後和IV進行xor得到明文。
3.使用金鑰對第二組密文解密,然後和2中的密文xor得到明文。
4.重複2-3,直到最後一組密文。
假設我們修改了第一個密文塊的第 4 個位元組,那麼根據 CBC 模式的解密,它將影響下一個密文塊的解密結果,如圖示紅部分。
上述的影響過程中,只涉及到異或運算,這裡異或運算的特性就十足重要了!
4)異或的概念:
1)異或的運算方法是一個二進位制運算: 1^1=0 0^0=0 1^0=1 0^1=1 兩者相等為0,不等為1.
這樣我們發現交換兩個整數的值時可以不用第三個引數。 如a=11,b=9.以下是二進位制 a=a^ b=1011^1001=0010; b=b^
a=1001^0010=1011; a=a^ b=0010^1011=1001; 這樣一來a=9,b=11了。
2)異或是可逆的。
已知A=B^ C
可以得到結論B=A^ C
並且也能得到A^ C^B= B ^ B=0
上述結論拿到這裡的 CBC 翻轉攻擊假設中看
設第一塊密文中的第4個位元組設為變數A,在第二塊密文的第4個位元組設為變數B,第二塊的明文中第4個位元組設為變數C
因為A ^ B=C,根據結論有B=A^C
如果人為令A變數值改變為A^ C,那麼原本的A^B將變成A ^ C ^ B=B ^ B=0,第二個產生的明文的第4個位元組將變為0
如果認為將A改變為A ^ C^ X(這裡X是任意字元),那麼參與運算後,A ^ B將等於A ^ C ^ X ^ B=B ^ X ^ B=X,
第二個產生的明文的第4個位元組將變為X字元,這樣,第二塊密文塊解密的結果就可控了!
1、根據CBC演算法的原理,此題中:
Plaintext1:s:2:{s:8:“u serna
Plaintext2:me”;s:5:“z dmin”;
Plaintext3:s:8:“password”;s
Plaintext4::3:“12345”;}XXX
2、不妨設
①②③④代表Plaintext1、2、3、4
(1)(2)(3)(4)分別代表Ciphertext1、2、3、4
3、CBC加密前:
plaintext = ①②③④
4、CBC加密後:
Ciphertext = (1)(2)(3)(4)
CBC加密:
iv ⊕① =(1)
(1)⊕② =(2)
(2)⊕③ =(3)
(3)⊕④ =(4)
CBC解密
(1)⊕iv = ①
(1)⊕(2)=②
(2)⊕(3)=③
(3)⊕(4)=④
5)攻擊流程:
1、修改能夠得到的密文的第一塊(前16個位元組),使得第二塊密文塊結果的字元中zdmin能變成admin
cipher[9] = u
xor_cipher = cipher[0:9] + chr(ord(cipher[9]) ^ ord('z') ^ ord('a')) + cipher[10:]
2、由於操作1導致第一塊密文塊解密後得到錯誤結果,修正IV初始化向量,將第一塊的明文結果還原成a:2:{s:8:"userna
通俗點說就是:
我們需要更改(1),使得(1)⊕(2)後,得到②中的zdmin變成admin
因為(1)被更改,所以(1)⊕iv得到的①會被更改,導致無法反序列化。
所以我們需要適當更改iv使得(1)⊕iv 仍然等於①。
Realfirstblk = 'a:2:{s:8:"userna'#被損壞前正確的明文
for i in range(16):
newIv += chr(ord(iv[i])^ord(cipher[i])^ord(Realfirstblk[i])) #這一步相當於把原來iv中不匹配的部分修改過來
iv ⊕ ① =(1)
(1)⊕(2)= ②
令iv =A ,①=B , (1)=C
(1)⊕(2)=② 等價於A ^ B =C
可控點(A^C ^X) ^B = X
等價於iv ^ (1) ^ RealFirstBlock = RealFirstBlock
iv = oldiv
(1) = cipher[0:16]
所以newIv[i] = chr(ord(iv[i])^ ord(cipher[i])^ord(Realfirstblk[i]))
6)攻擊流程1的Exp
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-03-15 11:45:57
# @Author : Mr.zhang(s4ad0w.protonmail.com)
# @Link : http://blog.csdn.net/csu_vc
import base64
import requests
import urllib
iv_raw='%2F8iEm4jh%2BjbgVGwlQ31ycg%3D%3D' #這裡填寫第一次返回的iv值
cipher_raw='8WdhbPxjZy9xYAgoCeghiOUQu0ri1Y3dv7cX44MbvOfIC6zZxCbR%2FPFpeMatL5qIgT%2BYA66tIdCBpxtWsWxV9Q%3D%3D' #這裡填寫第一次返回的cipher值
print "[*]原始iv和cipher"
print "iv_raw: " + iv_raw
print "cipher_raw: " + cipher_raw
print "[*]對cipher解碼,進行反轉"
cipher = base64.b64decode(urllib.unquote(cipher_raw))
#a:2:{s:8:"username";s:5:"zdmin";s:8:"password";s:5:"12345"}
#s:2:{s:8:"userna
#me";s:5:"zdmin";
#s:8:"password";s
#:3:"12345";}
xor_cipher = cipher[0:9] + chr(ord(cipher[9]) ^ ord('z') ^ ord('a')) + cipher[10:]
xor_cipher=urllib.quote(base64.b64encode(xor_cipher))
print "反轉後的cipher:" + xor_cipher
7)攻擊流程2的Exp
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-03-15 11:56:20
# @Author : csu_vc(s4ad0w.protonmail.com)
# @Link : http://blog.csdn.net/csu_vc
import base64
import urllib
cipher = 'Bc6oENSSAEPpPdv/rbqRZG1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9'#填寫提交後所得的無法反序列化密文
iv = '%2F8iEm4jh%2BjbgVGwlQ31ycg%3D%3D'#會報”無法反序列化“錯的iv
#cipher = urllib.unquote(cipher)
cipher = base64.b64decode(cipher)
iv = base64.b64decode(urllib.unquote(iv))
newIv = ''
Realfirstblk = 'a:2:{s:8:"userna'#被損壞前正確的明文
for i in range(16):
newIv += chr(ord(iv[i])^ord(cipher[i])^ord(Realfirstblk[i])) #這一步相當於把原來iv中不匹配的部分修改過來
print "oldIv: " + iv
print "newIv:" + urllib.quote(base64.b64encode(newIv))
最終得到計算得到的 cipher 和 iv:
[email protected]:~/Desktop# python 1.py
[*]原始iv和cipher
iv_raw: %2F8iEm4jh%2BjbgVGwlQ31ycg%3D%3D
cipher_raw: 8WdhbPxjZy9xYAgoCeghiOUQu0ri1Y3dv7cX44MbvOfIC6zZxCbR%2FPFpeMatL5qIgT%2BYA66tIdCBpxtWsWxV9Q%3D%3D
[*]對cipher解碼,進行反轉
反轉後的cipher:8WdhbPxjZy9xewgoCeghiOUQu0ri1Y3dv7cX44MbvOfIC6zZxCbR/PFpeMatL5qIgT%2BYA66tIdCBpxtWsWxV9Q%3D%3D