-
Notifications
You must be signed in to change notification settings - Fork 7
/
Set2.java
179 lines (156 loc) · 7.42 KB
/
Set2.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package org.meri.matasano;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.CodecSupport;
import org.meri.matasano.utils.ArrayManips;
import org.meri.matasano.utils.Xor;
import org.meri.matasano.utils.cryptoanalysis.ECBAnalyzer;
import org.meri.matasano.utils.cryptoanalysis.ECBAnalyzer.BlockInfo;
import org.meri.matasano.utils.encryption.AESCBC;
import org.meri.matasano.utils.encryption.PkcsN7Padding;
import org.meri.matasano.utils.oracle.ConstantKeyConstantSuffixAESECB;
import org.meri.matasano.utils.oracle.ECBCBCOracle;
import org.meri.matasano.utils.oracle.ECBCBCOracle.OracleMode;
import org.meri.matasano.utils.oracle.EncryptingOracleCipher;
import org.meri.matasano.utils.oracle.RandomPrefixRemovingOracleCipher;
import org.meri.matasano.utils.webtools.ForumManager;
import org.meri.matasano.utils.webtools.SimulatedWebServer;
public class Set2 {
private ArrayManips arrayManips = new ArrayManips();
private Xor xor = new Xor();
/** Exercise 9 */
public String padPkcsN7(String raw, int blockLength) {
PkcsN7Padding padding = new PkcsN7Padding();
return CodecSupport.toString(padding.padPkcsN7(CodecSupport.toBytes(raw), blockLength));
}
/** Exercise 10 - AES! CBC
*
* Decrypted plaintext contains the same Vanilla Ice song as exercises 6 and 7. It
* starts with "I'm back and I'm ringin' the bell \nA rockin' on the mike ...".
* */
public String decryptMultipleTimePadInECB(String base64Ciphertext, String keyString, String ivString) {
byte[] ciphertext = Base64.decode(base64Ciphertext);
final byte[] key = CodecSupport.toBytes(keyString);
byte[] iv = CodecSupport.toBytes(ivString);
AESCBC aesCbc = new AESCBC();
byte[] plaintext = aesCbc.decrypt(ciphertext, key, iv);
return CodecSupport.toString(plaintext);
}
/** Exercise 11 - The detector constructs plaintext with repeating blocks
* and let oracle encrypt it. If ciphertext contains repeating blocks, then
* it probably is ECB.
* */
public OracleMode oracleEncryptAndGuess(ECBCBCOracle oracle) {
ECBAnalyzer detector = new ECBAnalyzer();
if (detector.isECBEncryption(oracle, oracle.getBlockSize())) {
return OracleMode.ECB;
} else {
return OracleMode.CBC;
}
}
/** Exercise 12
*
* The algorithm is implemented in {@see SuffixCrackingAlgorithm} class. The ciphertext
* decrypts into:
*
* "Rollin' in my 5.0 \nWith my rag-top down so my hair can blow \nThe girlies on standby waving just to say hi \nDid you stop? No, I just drove by \n";.
*
* */
public String decryptSuffix(ConstantKeyConstantSuffixAESECB server) {
ECBAnalyzer detector = new ECBAnalyzer();
return CodecSupport.toString(detector.decryptServerAddedSuffix(server));
}
/** Exercise 13
* This solution relies on role being the last in cookies string. It would not work with
* role being on any other place.
*
* - Works for: email=xxxx&uid=xxxx&role=xxx
* - Does not work for: role=xxx&uid=xxxx&email=xxxx
*
* If I was supposed to find another solution, let me know please.
*/
public byte[] createEncryptedAdminProfileFor(final SimulatedWebServer webServer) {
ECBAnalyzer analyzer = new ECBAnalyzer();
// validate input server - unimportant code
BlockInfo blockSizeInfo = analyzer.discoverBlockSizeInfo(new EncryptingOracleCipher() {
public byte[] encrypt(byte[] plaintext) {
return webServer.createEcodedProfileFor(CodecSupport.toString(plaintext));
}
});
if (blockSizeInfo.getBlockLength()!=16)
throw new IllegalStateException("This solution requires 16 bytes block size. It is not general enough to handle " + blockSizeInfo.getBlockLength());
// attack
byte[] adminBlockFullCipher = webServer.createEcodedProfileFor("fo@bar.comadmin\u000b\u000b\u000b\u000b\u000b\u000b\u000b\u000b\u000b\u000b\u000b");
byte[] adminBlock = arrayManips.extractBlock(adminBlockFullCipher, 16, 1);
byte[] attack = webServer.createEcodedProfileFor("foooo@bar.com");
arrayManips.replaceLastBlock(attack, adminBlock);
return attack;
}
/** Exercise 14
*
* "What's harder about doing this?"
*
* It is harder to predict position of cipher blocks we are interested in.
*
* "How would you overcome that obstacle?"
*
* I modified each encryption query by prepending it with three blocks of constant data. I call
* them "signature". Attacked server then add its own prefix, encrypts it and sends me result.
*
* - Send data: three-constant-blocks || attack || target-bytes
* - Encrypted data: random-prefix || three-constant-blocks || attack || target-bytes
*
* If the length of servers prefix is divisible by 16, then three-constant-blocks corresponds to three
* repeated blocks in ciphertext. The code removes those three blocks and everything leading to them.
* Remaining ciphertext is valid encryption of "attack || target-bytes"
*
* Random prefix divisible by 16 leads to:
* - Attacked server answer: aescbc(random-prefix) || aescbc(three-constant-blocks) || aescbc(attack || target-bytes)
* - Modified answer: aescbc(attack || target-bytes)
*
* If the length of servers prefix is NOT divisible by 16, then the ciphertex does not contain three consecutive
* repeated blocks. If that is the case, code asks the server to encrypt the same input again.
*
* This random prefix removal logic is implemented in {@see RandomPrefixRemovingOracleCipher}.
*
* Cracking algorithm uses exactly the same code as in exercise 12. Only difference is that
* target server gets hit with more requests.
*
* */
public String decryptSuffixBewareRandomPrefix(final ConstantKeyConstantSuffixAESECB server) {
ECBAnalyzer detector = new ECBAnalyzer();
EncryptingOracleCipher modifiedServer = new RandomPrefixRemovingOracleCipher(server, server.getBlockSize());
return CodecSupport.toString(detector.decryptServerAddedSuffix(modifiedServer));
}
/** Exercise 15 */
public String validateAndRemovePkcsN7(String input) {
PkcsN7Padding padding = new PkcsN7Padding();
return CodecSupport.toString(padding.validateAndremovePadding(CodecSupport.toBytes(input)));
}
/** Exercise 16
*
* Encode user data equivalent to two blocks of data. Use the cipher block corresponding to first
* block to modify plaintext of the second.
*
* */
public byte[] createEncryptedAdminProfileFor(final ForumManager forum) {
ECBAnalyzer analyzer = new ECBAnalyzer();
// validate input server - unimportant code
BlockInfo blockSizeInfo = analyzer.discoverBlockSizeInfo(new EncryptingOracleCipher() {
public byte[] encrypt(byte[] plaintext) {
return forum.createEcodedUserData(CodecSupport.toString(plaintext));
}
});
if (blockSizeInfo.getBlockLength()!=16)
throw new IllegalStateException("This solution requires 16 bytes block size. It is not general enough to handle " + blockSizeInfo.getBlockLength());
// attack
byte[] initialCiphertext = forum.createEcodedUserData("1234567890123456<admin>true<aa>b");
byte[] modifier = new byte[initialCiphertext.length];
modifier[2*blockSizeInfo.getBlockLength()] = 7;
modifier[2*blockSizeInfo.getBlockLength()+6] = 3;
modifier[2*blockSizeInfo.getBlockLength()+11] = 7;
modifier[2*blockSizeInfo.getBlockLength()+14] = 3;
byte[] attack = xor.xor(initialCiphertext, modifier);
forum.isAdminData(attack);
return attack;
}
}