先來講一講CBC模式加密原理:
加密過程:
1.首先將明文(Plaintext)分組(常見的以16或8字節為一組),位數不足的使用特殊字符填充。
2.生成一個隨機的初始化向量(IV)和一個密鑰。
3.將IV和第一組明文異或(xor運算)。
4.用密鑰對3中xor后產生的密文加密。
5.用4中產生的密文對第二組明文進行xor操作。
6.用密鑰對5中產生的密文加密。
7.重復4-7,到最后一組明文。
8.將IV和加密后的密文拼接在一起,得到最終的密文
解密過程:
1.先從密文中取出IV,然后對剩下的密文分組(16或8字節為一組)
2.使用秘鑰解密第一組密文,將解密結果與IV做異或運算,得到明文1
3.然后使用秘鑰解第二組密文,將解密的結果與上一組密文進行異或運算,得出明文2
4.重復2-3,直至所有密文解密完畢
以上就是CBC模式的加密解密過程,接下來講兩種手段:
Padding Oracle Attack
直譯為 "填充Oracle攻擊" ,這里主要關注一下解密過程:
密文cipher首先進行一系列處理,如圖中的Block Cipher Decryption
我們將處理后的值稱為 middle 中間值
然后 middle 與我們輸入的iv進行異或操作
得到的即為明文
但這里有一個規則叫做Padding填充:
因為加密是按照16位一組分組進行的
而如果不足16位,就需要進行填充
有幾個空,就要填充幾個"幾"
比如明文為admin,那么需要填充的就是
admin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b (11個\x0b)
如果我們輸入一個錯誤的IV(初始向量),依舊是可以解密的,但是 middle 和我們輸入的IV經過異或后得到的填充值可能出現錯誤
比如本來應該是 admin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b
而我們錯誤的得到 admin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x3b\x2c
這樣解密程序往往會拋出異常(Padding Error)
當這個CBC解密模式應用在web程序里的時候,往往是302或是500報錯
而正常解密的時候是200。
所以這時,我們可以根據服務器的反應來判斷我們輸入的IV
一個例子
我們假設middle中間值為:
0x39 0x73 0x23 0x22 0x07 0x6a 0x26 0x3d
正確的解密IV應該為
0x6d 0x36 0x70 0x76 0x03 0x6e 0x22 0x39
解密后正確的明文為:
T E S T 0x04 0x04 0x04 0x04
但是關鍵點在于,我們可以知道iv的值,卻不能得到中間值和解密后明文的值
而我們的目標是只根據我們輸入的iv值和服務器的狀態去判斷出解密后明文的值
這里的攻擊即叫做 Padding Oracle Attack 攻擊
是一種根據頁面回顯來爆破密文的攻擊
如果我們構造一個IV為:
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
那么 middle 的值和這個iv異或將會得到原封不動的 middle 的值
0x39 0x73 0x23 0x22 0x07 0x6a 0x26 0x3d
現在這個解密結果是不對的,web程序拋出錯誤。
正確的 padding 值只可能是:
1個字節的padding為0x01
2個字節的padding為0x02,0x02
3個字節的padding為0x03,0x03,0x03
4個字節的padding為0x04,0x04,0x04,0x04
……
因此我們希望慢慢調整IV的值,并且希望解密后最后一個值為正確的 padding 比如一個0x01,我們于是遍歷最后一位IV:
那么最后一位中間密文就是: 0x01^0x3C=0x3D (這個一定成立,看圖),原來的明文就是 0x3D^0x0F=0x32(中間密文^原來的iv)
知道了最后一位的中間密文,就可以遍歷倒數第二位iv了,這個時候應該為 0x02 而非 0x01 了。看圖就懂:
以此類推,我們可以就能推算所有中間密文,再用 中間密文^原來的iv 就能算出明文了
CBC字節翻轉攻擊
有了上面的CBC加密解密過程的基礎,這個手段其實很容易理解;
由解密算法可知:
A=B^C
由 ^ 運算的性質我們可以知道:
A=B^C、B=A^C、C=A^B
這是最關鍵的一點,我們可以推導出三者做異或運算的結果是0
C=A^B
C^C=A^B^C=0
也就是說,我們修改了B的值,就一定會影響到A
B^X^C=A^X
換句話說,我們只要給B異或了X,A的值也會改變為他之前的值異或X的結果
一 道CTF的例子
NPUCTF2020_web
源碼:
<?php
error_reporting(0);
include('config.php'); # $key,********$file1*********
define("METHOD", "aes-128-cbc"); //定義加密方式
define("SECRET_KEY", $key); //定義密鑰
define("IV","6666666666666666"); //定義初始向量 16個6
define("BR",'<br>');
if(!isset($_GET['source']))header('location:./index.php?source=1');
#var_dump($GLOBALS); //聽說你想看這個?
function aes_encrypt($iv,$data)
{
echo "--------encrypt---------".BR;
echo 'IV:'.$iv.BR;
return base64_encode(openssl_encrypt($data, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)).BR;
}
function aes_decrypt($iv,$data)
{
return openssl_decrypt(base64_decode($data),METHOD,SECRET_KEY,OPENSSL_RAW_DATA,$iv) or die('False'); #不返回密文,解密成功返回1,解密失敗返回False
}
if($_GET['method']=='encrypt')
{
$iv = IV;
$data = $file1;
echo aes_encrypt($iv,$data);
} else if($_GET['method']=="decrypt")
{
$iv = @$_POST['iv'];
$data = @$_POST['data'];
echo aes_decrypt($iv,$data);
}
echo "我攤牌了,就是懶得寫前端".BR;
if($_GET['source']==1)highlight_file(__FILE__);
?>
exp:
# coding:utf-8
import requests
import base64
# b'\x97.\xda\xb8\xa5Pt\x95\xae\x9b\xf5\xbf\xe2\x8b.<'
CYPHERTEXT = base64.b64decode("ly7auKVQCZWum/W/4osuPA==")
# initialization vector
IV = "6666666666666666"
# PKCS7 16個字節為1組
N = 16
# intermediaryValue ^ IV = plainText
inermediaryValue = ""
plainText = ""
# 爆破時不斷需要更改的iv
iv = ""
URL = "http://webdog.popscat.top/index.php?method=decrypt&source=1"
def xor(a, b):
"""
用于輸出兩個字符串對位異或的結果
"""
return "".join([chr(ord(a[i]) ^ ord(b[i])) for i in range(len(a))])
for step in range(1, N + 1):
padding = chr(step) * (step - 1)
print(step,end=",")
for i in range(0, 256):
print(i)
"""
iv由三部分組成:
待爆破位置 chr(0)*(N-step)
正在爆破位置 chr(i)
使 iv[N-step+1:] ^ inermediaryValue = padding 的 xor(padding,inermediaryValue)
"""
iv = chr(0)*(N-step)+chr(i)+xor(padding,inermediaryValue)
data = {
"data": "ly7auKVQCZWum/W/4osuPA==",
"iv": iv
}
r = requests.post(URL,data = data)
if r.text !="False":
inermediaryValue = xor(chr(i),chr(step)) + inermediaryValue
print(inermediaryValue)
break
plainText = xor(inermediaryValue,IV)
print(plainText)
得到 FlagIsHere.php,訪問之:
F7LMTk/3nKSVUoSQuOS/dA==
<?php
#error_reporting(0);
include('config.php'); //**************$file2********last step!!
define("METHOD", "aes-128-cbc");
define("SECRET_KEY", "6666666");
session_start();
function get_iv(){ //生成隨機初始向量IV
$random_iv='';
for($i=0;$i<16;$i++){
$random_iv.=chr(rand(1,255));
}
return $random_iv;
}
$lalala = 'piapiapiapia';
if(!isset($_SESSION['Identity'])){
$_SESSION['iv'] = get_iv();
$_SESSION['Identity'] = base64_encode(openssl_encrypt($lalala, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $_SESSION['iv']));
}
echo base64_encode($_SESSION['iv'])."<br>";
if(isset($_POST['iv'])){
$tmp_id = openssl_decrypt(base64_decode($_SESSION['Identity']), METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_POST['iv']));
echo $tmp_id."<br>";
if($tmp_id ==='weber')die($file2);
}
highlight_file(__FILE__);
?>
整理一下已知信息:
iv=
F7LMTk/3nKSVUoSQuOS/dA==
\x17 \xb2 \xcc \x4e \x4f \xf7 \x9c \xa4 \x95 \x52 \x84 \x90 \xb8 \xe4 \xbf \x74
加密后:
$Identity='MLvuYeH07rhiAa5NJ1p75A=='
$Identity='\x30 \xbb \xee \x61 \xe1 \xf4 \xee \xb8 \x62 \x01 \xae \x4d \x27 \x5a \x7b \xe4 '
目的就是傳入新的iv對identity進行解密,如果解密結果為'weber'那么就爽歪歪,這里考察的就是CBC字節翻轉攻擊
和Padding Oracle Attack不一樣,這里不需要推測中間密文,根據我上面說的
B^X^C=A^X
本來是 piapiapiapia\x04\x04\x04\x04 現在我們需要改為 weber\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B ,就拿第一位來說:
我們想要把 p 改為 w ,那么我就要找出 X 的值, 'p'^X='w' 很容易算出 X='p'^'w' 那么我們只需要在將B異或一個 ('p'^'w') ,就可以達到目的。
exp:
import base64
def bxor(b1, b2): # use xor for bytes
parts = []
for b1, b2 in zip(b1, b2):
parts.Append(bytes([b1 ^ b2]))
return b''.join(parts)
iv = base64.b64decode("h34HL5RbMPw8oTaQ+P58nw==")
text = b"piapiapiapia\x04\x04\x04\x04"
result = b"weber\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
middle = bxor(iv,text)
iv = bxor(middle,result)
print(base64.b64encode(iv))
跑出來POST一個iv過去得到一個網址: https://c-t.work/s/034d3b3bf3fb48||verification code:2q2hwm
有個附件,下載來是一個xxx.class文件考的JAVA反編譯,用工具 jd-gui-1.4.0 一下就跑出來。
得到數組,就是flag的ASCII碼
q = [102, 108, 97, 103, 123, 119, 101, 54, 95, 52, 111, 103,
95, 49, 115, 95, 101, 52, 115, 121, 103, 48, 105, 110, 103, 125]
for i in q:
print(chr(i), end='')
flag{we6_4og_1s_e4syg0ing}
以上就是一次對于Padding Oracle Attack和CBC字節翻轉攻擊的一次初步學習,歡迎各位指正。
文章來自我們內部小組成員:低語