CBC字节翻转和Padding Oracle这两个考点在2017-2018年的CTF题目中比较常见,没想到最近的巅峰极客2022的 babyweb 中再出现,总感觉知识点比较零散,这里尝试稍微系统的梳理下,水平有限,有误望师傅们指正
CBC全称Cipher Block Chaining模式(密文分组链接模式),每一个分组大小一般为128bits(16字节),因而这里会出现两种情况:
密文:(加密后可能有不可见字符,为了方便网络传输和适应不同系统的编码方案)
需要注意的是,Padding Oracle Attack 攻击与具体的加密算法无关。
在前文有提及,如果明文的长度不是16字节的整数倍,需要对最后一个分组进行填充(padding),CBC的填充规则有PKCS5和PKCS7的区别,这里使用的是PKCS7 ,即缺少N字节,就用 N 个 \xN
填充,如缺少7位则用 7 个 \x07
填充,因此,正确的填充格式有:
每次发送一个分组,则解密时都会用到IV
爆破分组最后一个字节:
根据 New_IV[-1] ^ Intermediary_Value [-1] = 0x01 爆破
New_IV[-1] 从0x00-0xFF 进行爆破,其中只有一个值能满足与 Intermediary_Value[-1] 异或结果为 0x01,也仅有这种 padding 情况能被认为是正常解密(二值推理)
根据逻辑运算得到最后一字节的明文
将 Intermediary_Value [-1] 与 IV[-1] (第一个分组)或 前一个密文分组的最后一位(其他分组)异或可以得到 Cipher[-1]
爆破分组倒数第二个字节
构造 New_IV[-1] ^ Intermediary_Value [-1] = 0x02
上一步已经得到了Intermediary_Value [-1] ,则
根据 New_IV[-2] ^ Intermediary_Value [-2] = 0x02 爆破
与爆破最后一个字节的思路相同
分组其他字节依此类推
0x01
0x02
0x02
0x03
0x03
0x03
....
0x01
0x02
0x02
0x03
0x03
0x03
....
New_IV[
-
1
] ^ Intermediary_Value [
-
1
]
=
0x01
=
> Intermediary_Value [
-
1
]
=
New_IV[
-
1
] ^
0x01
New_IV[
-
1
] ^ Intermediary_Value [
-
1
]
=
0x01
=
> Intermediary_Value [
-
1
]
=
New_IV[
-
1
] ^
0x01
New_IV[
-
1
]
=
Intermediary_Value [
-
1
] ^
0x02
New_IV[
-
1
]
=
Intermediary_Value [
-
1
] ^
0x02
import
base64 as b64
import
requests
secret
=
''
url
=
""
data
=
{
'username'
:
"admin"
,
'password'
:
"admin"
}
cookie
=
{
"session"
: ""
}
def
padding(secret, xorValue, IV):
middle
=
[]
pt
=
''
for
x
in
xrange
(
0
,
16
):
for
y
in
xrange
(
0
,
256
):
tmp_IV
=
''.join(IV)
cookie_pwd
=
b64.b64encode(tmp_IV
+
secret)
cookie.update({
'admin_password'
:cookie_pwd
})
res
=
requests.post(url
=
url,cookies
=
cookie,data
=
data).text
if
'padding error'
in
res:
IV[
15
-
x]
=
chr
(y)
elif
'False'
in
res:
print
IV
IV[
15
-
x]
=
chr
(
ord
(IV[
15
-
x]) ^ (x
+
1
))
middle.append(
ord
(IV[
15
-
x]))
print
middle
pt
+
=
chr
(
ord
(IV[
15
-
x]) ^
ord
(xorValue[
15
-
x]))
for
z
in
xrange
(
0
,x
+
1
):
IV[
15
-
z]
=
chr
(middle[z] ^ (x
+
2
))
break
else
:
print
res
exit()
if
y
=
=
255
:
print
'[!] Something wrong'
print
x
+
1
exit()
return
pt[::
-
1
]
plainText
=
''
IV
=
[
'\x00'
]
*
16
Right_IV
=
b64.b64decode(secret)[
0
:
16
]
secret
=
b64.b64decode(secret)[
16
:]
num
=
int
(
len
(secret)
/
16
)
for
i
in
range
(num):
s
=
secret[i
*
16
:(i
+
1
)
*
16
]
if
i
=
=
0
:
xorValue
=
Right_IV
else
:
xorValue
=
secret[(i
-
1
)
*
16
:i
*
16
]
plainText
+
=
padding(s,xorValue)
print
'[+] PlaintText is : '
+
plainText
import
base64 as b64
import
requests
secret
=
''
url
=
""
data
=
{
'username'
:
"admin"
,
'password'
:
"admin"
}
cookie
=
{
"session"
: ""
}
def
padding(secret, xorValue, IV):
middle
=
[]
pt
=
''
for
x
in
xrange
(
0
,
16
):
for
y
in
xrange
(
0
,
256
):
tmp_IV
=
''.join(IV)
cookie_pwd
=
b64.b64encode(tmp_IV
+
secret)
cookie.update({
'admin_password'
:cookie_pwd
})
res
=
requests.post(url
=
url,cookies
=
cookie,data
=
data).text
if
'padding error'
in
res:
IV[
15
-
x]
=
chr
(y)
elif
'False'
in
res:
print
IV
IV[
15
-
x]
=
chr
(
ord
(IV[
15
-
x]) ^ (x
+
1
))
middle.append(
ord
(IV[
15
-
x]))
print
middle
pt
+
=
chr
(
ord
(IV[
15
-
x]) ^
ord
(xorValue[
15
-
x]))
for
z
in
xrange
(
0
,x
+
1
):
IV[
15
-
z]
=
chr
(middle[z] ^ (x
+
2
))
break
else
:
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)