Skip to content

Commit d9ff3ce

Browse files
authored
Merge pull request #410 from RUB-NDS/moreattackfixes
Padding Oracle and Bleichenbacher attack Rework
2 parents 3c7fee2 + e4ef211 commit d9ff3ce

File tree

8 files changed

+204
-18
lines changed

8 files changed

+204
-18
lines changed

Attacks/src/main/java/de/rub/nds/tlsattacker/attacks/config/HeartbleedCommandConfig.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import de.rub.nds.tlsattacker.core.config.delegate.ProtocolVersionDelegate;
1919
import de.rub.nds.tlsattacker.core.constants.HeartbeatMode;
2020
import de.rub.nds.tlsattacker.core.workflow.factory.WorkflowTraceType;
21-
import de.rub.nds.tlsattacker.transport.TransportHandlerType;
2221

2322
public class HeartbleedCommandConfig extends AttackConfig {
2423

@@ -65,7 +64,6 @@ public boolean isExecuteAttack() {
6564
public Config createConfig() {
6665
Config config = super.createConfig();
6766
config.setAddHeartbeatExtension(true);
68-
config.setWorkflowTraceType(WorkflowTraceType.FULL);
6967
config.setHeartbeatMode(HeartbeatMode.PEER_ALLOWED_TO_SEND);
7068
return config;
7169
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* TLS-Attacker - A Modular Penetration Testing Framework for TLS
3+
*
4+
* Copyright 2014-2017 Ruhr University Bochum / Hackmanit GmbH
5+
*
6+
* Licensed under Apache License 2.0
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*/
9+
package de.rub.nds.tlsattacker.attacks.exception;
10+
11+
/**
12+
*
13+
* @author Robert Merget <robert.merget@rub.de>
14+
*/
15+
public class PaddingOracleUnstableException extends RuntimeException {
16+
17+
public PaddingOracleUnstableException(String string) {
18+
super(string);
19+
}
20+
21+
}

Attacks/src/main/java/de/rub/nds/tlsattacker/attacks/impl/BleichenbacherAttacker.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import de.rub.nds.modifiablevariable.util.ArrayConverter;
1212
import de.rub.nds.tlsattacker.attacks.config.BleichenbacherCommandConfig;
1313
import de.rub.nds.tlsattacker.attacks.pkcs1.Bleichenbacher;
14+
import de.rub.nds.tlsattacker.attacks.pkcs1.BleichenbacherVulnerabilityMap;
1415
import de.rub.nds.tlsattacker.attacks.pkcs1.BleichenbacherWorkflowGenerator;
1516
import de.rub.nds.tlsattacker.attacks.pkcs1.BleichenbacherWorkflowType;
1617
import de.rub.nds.tlsattacker.attacks.pkcs1.Pkcs1Vector;
@@ -99,12 +100,45 @@ private EqualityError isVulnerable(BleichenbacherWorkflowType bbWorkflowType, Li
99100
return null;
100101
}
101102
printBleichenbacherVectormap(bleichenbacherVectorMap);
103+
EqualityError error = getEqualityError(bleichenbacherVectorMap);
104+
if (error == EqualityError.SOCKET_EXCEPTION || error == EqualityError.SOCKET_STATE) {
105+
LOGGER.debug("Found a Socket related side channel. Rescanning to confirm.");
106+
// Socket Equality Errors can be caused by problems on on the
107+
// network. In this case we do a rescan
108+
// and check if we find the exact same answer behaviour (twice)
109+
List<VectorFingerprintPair> secondBleichenbacherVectorMap = getBleichenbacherMap(bbWorkflowType,
110+
pkcs1Vectors);
111+
EqualityError error2 = getEqualityError(secondBleichenbacherVectorMap);
112+
BleichenbacherVulnerabilityMap mapOne = new BleichenbacherVulnerabilityMap(bleichenbacherVectorMap, error);
113+
BleichenbacherVulnerabilityMap mapTwo = new BleichenbacherVulnerabilityMap(secondBleichenbacherVectorMap,
114+
error2);
115+
if (mapOne.looksIdentical(mapTwo)) {
116+
List<VectorFingerprintPair> thirdBleichenbacherVectorMap = getBleichenbacherMap(bbWorkflowType,
117+
pkcs1Vectors);
118+
EqualityError error3 = getEqualityError(secondBleichenbacherVectorMap);
119+
BleichenbacherVulnerabilityMap mapThree = new BleichenbacherVulnerabilityMap(
120+
secondBleichenbacherVectorMap, error2);
121+
if (!mapTwo.looksIdentical(mapThree)) {
122+
LOGGER.debug("The third scan prove this vulnerability to be non existent");
123+
error = EqualityError.NONE;
124+
}
125+
} else {
126+
LOGGER.debug("The second scan prove this vulnerability to be non existent");
127+
error = EqualityError.NONE;
128+
}
129+
}
130+
if (error != EqualityError.NONE) {
131+
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Found a vulnerability with " + bbWorkflowType.getDescription());
132+
}
133+
return error;
134+
}
135+
136+
public EqualityError getEqualityError(List<VectorFingerprintPair> bleichenbacherVectorMap) {
102137
ResponseFingerprint fingerprint = bleichenbacherVectorMap.get(0).getFingerprint();
103138
for (VectorFingerprintPair pair : bleichenbacherVectorMap) {
104139
EqualityError error = FingerPrintChecker.checkEquality(fingerprint, pair.getFingerprint(), false);
105140
if (error != EqualityError.NONE) {
106-
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Found a difference in responses in the {}.",
107-
bbWorkflowType.getDescription());
141+
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Found an EqualityError!");
108142
LOGGER.log(LogLevel.CONSOLE_OUTPUT,
109143
EqualityErrorTranslator.translation(error, fingerprint, pair.getFingerprint()));
110144
return error;

Attacks/src/main/java/de/rub/nds/tlsattacker/attacks/impl/PaddingOracleAttacker.java

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import de.rub.nds.modifiablevariable.bytearray.ModifiableByteArray;
1414
import de.rub.nds.modifiablevariable.util.ArrayConverter;
1515
import de.rub.nds.tlsattacker.attacks.config.PaddingOracleCommandConfig;
16+
import de.rub.nds.tlsattacker.attacks.exception.PaddingOracleUnstableException;
1617
import de.rub.nds.tlsattacker.attacks.util.response.EqualityError;
1718
import de.rub.nds.tlsattacker.attacks.util.response.EqualityErrorTranslator;
1819
import de.rub.nds.tlsattacker.attacks.util.response.FingerPrintChecker;
@@ -40,6 +41,7 @@
4041
import java.util.HashMap;
4142
import java.util.LinkedList;
4243
import java.util.List;
44+
import java.util.Set;
4345

4446
/**
4547
* Executes a padding oracle attack check. It logs an error in case the tested
@@ -159,6 +161,56 @@ private byte[] createPaddingBytes(int padding) {
159161

160162
@Override
161163
public Boolean isVulnerable() {
164+
165+
LOGGER.log(LogLevel.CONSOLE_OUTPUT,
166+
"A server is considered vulnerable to this attack if it responds differently to the test vectors.");
167+
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "A server is considered secure if it always responds the same way.");
168+
HashMap<Integer, List<ResponseFingerprint>> responseMap = createResponseMap();
169+
170+
EqualityError error = getEqualityError(responseMap);
171+
if (error == EqualityError.SOCKET_EXCEPTION || error == EqualityError.SOCKET_STATE) {
172+
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Found a candidate for a Socket difference performing rescan");
173+
HashMap<Integer, List<ResponseFingerprint>> responseMapTwo = createResponseMap();
174+
EqualityError errorTwo = getEqualityError(responseMapTwo);
175+
if (error == errorTwo && lookEqual(responseMap, responseMapTwo)) {
176+
HashMap<Integer, List<ResponseFingerprint>> responseMapThree = createResponseMap();
177+
EqualityError errorThree = getEqualityError(responseMapThree);
178+
if (error == errorThree && lookEqual(responseMap, responseMapThree)) {
179+
LOGGER.log(LogLevel.CONSOLE_OUTPUT,
180+
"Found an equality Error in a SocketState, performed to rescans and it still presisted");
181+
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "The Server is very likely vulnerabble");
182+
} else {
183+
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Rescan revealed a false positive");
184+
return false;
185+
}
186+
} else {
187+
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Rescan revealed a false positive");
188+
return false;
189+
}
190+
}
191+
LOGGER.log(LogLevel.CONSOLE_OUTPUT, EqualityErrorTranslator.translation(error, null, null));
192+
return error != EqualityError.NONE;
193+
}
194+
195+
public boolean lookEqual(HashMap<Integer, List<ResponseFingerprint>> responseMapOne,
196+
HashMap<Integer, List<ResponseFingerprint>> responseMapTwo) {
197+
for (Integer key : responseMapOne.keySet()) {
198+
List<ResponseFingerprint> listOne = responseMapOne.get(key);
199+
List<ResponseFingerprint> listTwo = responseMapTwo.get(key);
200+
if (listOne.size() != listTwo.size()) {
201+
throw new PaddingOracleUnstableException(
202+
"The padding Oracle seems to be unstable - there is something going terrible wrong. We recommend manual analysis");
203+
}
204+
for (int i = 0; i < listOne.size(); i++) {
205+
if (FingerPrintChecker.checkEquality(listOne.get(i), listTwo.get(i), false) != EqualityError.NONE) {
206+
return false;
207+
}
208+
}
209+
}
210+
return true;
211+
}
212+
213+
public HashMap<Integer, List<ResponseFingerprint>> createResponseMap() {
162214
int macSize = AlgorithmResolver.getMacAlgorithm(tlsConfig.getDefaultSelectedProtocolVersion(),
163215
tlsConfig.getDefaultSelectedCipherSuite()).getSize();
164216
int blockSize = AlgorithmResolver.getCipher(tlsConfig.getDefaultSelectedCipherSuite())
@@ -173,7 +225,6 @@ public Boolean isVulnerable() {
173225
State state;
174226
try {
175227
state = executeTlsFlow(record);
176-
177228
} catch (WorkflowExecutionException | ConfigurationException E) {
178229
LOGGER.warn(E);
179230
LOGGER.warn("TLS-Attacker failed execute a Handshake. Skipping to next record");
@@ -194,29 +245,22 @@ public Boolean isVulnerable() {
194245
} else {
195246
LOGGER.warn("Could not execute Workflow. Something went wrong... Check the debug output for more information");
196247
}
197-
198248
}
199-
LOGGER.log(LogLevel.CONSOLE_OUTPUT,
200-
"A server is considered vulnerable to this attack if it responds differently to the test vectors.");
201-
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "A server is considered secure if it always responds the same way.");
249+
return responseMap;
250+
}
202251

252+
public EqualityError getEqualityError(HashMap<Integer, List<ResponseFingerprint>> responseMap) {
203253
for (List<ResponseFingerprint> list : responseMap.values()) {
204254
ResponseFingerprint fingerprint = list.get(0);
205255
for (int i = 1; i < list.size(); i++) {
206256
EqualityError error = FingerPrintChecker.checkEquality(fingerprint, list.get(i), true);
207257
if (error != EqualityError.NONE) {
208258
LOGGER.log(LogLevel.CONSOLE_OUTPUT, "Found an equality Error: " + error);
209-
LOGGER.debug("Fingerprint1: " + fingerprint.toString());
210-
LOGGER.debug("Fingerprint2: " + list.get(i).toString());
211-
LOGGER.log(LogLevel.CONSOLE_OUTPUT,
212-
EqualityErrorTranslator.translation(error, fingerprint, list.get(i)));
213-
return true;
259+
return error;
214260
}
215-
216261
}
217262
}
218-
LOGGER.log(LogLevel.CONSOLE_OUTPUT, EqualityErrorTranslator.translation(EqualityError.NONE, null, null));
219-
return false;
263+
return EqualityError.NONE;
220264
}
221265

222266
private void clearConnections(State state) {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* TLS-Attacker - A Modular Penetration Testing Framework for TLS
3+
*
4+
* Copyright 2014-2017 Ruhr University Bochum / Hackmanit GmbH
5+
*
6+
* Licensed under Apache License 2.0
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*/
9+
package de.rub.nds.tlsattacker.attacks.pkcs1;
10+
11+
import de.rub.nds.tlsattacker.attacks.util.response.EqualityError;
12+
import de.rub.nds.tlsattacker.attacks.util.response.FingerPrintChecker;
13+
import java.util.List;
14+
15+
public class BleichenbacherVulnerabilityMap {
16+
17+
private final List<VectorFingerprintPair> bleichenbacherVectorMap;
18+
19+
private final EqualityError error;
20+
21+
public BleichenbacherVulnerabilityMap(List<VectorFingerprintPair> bleichenbacherVectorMap, EqualityError error) {
22+
this.bleichenbacherVectorMap = bleichenbacherVectorMap;
23+
this.error = error;
24+
}
25+
26+
public boolean looksIdentical(BleichenbacherVulnerabilityMap otherMap) {
27+
if (otherMap.error != this.error) {
28+
return false;
29+
}
30+
for (VectorFingerprintPair otherPair : otherMap.bleichenbacherVectorMap) {
31+
for (VectorFingerprintPair ourPair : bleichenbacherVectorMap) {
32+
if (otherPair.getVector().getDescription().equals(this)) {
33+
// ok we found the right pairs
34+
if (FingerPrintChecker.checkEquality(ourPair.getFingerprint(), otherPair.getFingerprint(), true) != EqualityError.NONE) {
35+
return false;
36+
} else {
37+
break;
38+
}
39+
}
40+
}
41+
}
42+
return true;
43+
}
44+
45+
public List<VectorFingerprintPair> getBleichenbacherVectorMap() {
46+
return bleichenbacherVectorMap;
47+
}
48+
49+
public EqualityError getError() {
50+
return error;
51+
}
52+
}

Attacks/src/main/java/de/rub/nds/tlsattacker/attacks/pkcs1/VectorFingerprintPair.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package de.rub.nds.tlsattacker.attacks.pkcs1;
1010

1111
import de.rub.nds.tlsattacker.attacks.util.response.ResponseFingerprint;
12+
import java.util.Objects;
1213

1314
public class VectorFingerprintPair {
1415

@@ -41,4 +42,34 @@ public void setVector(Pkcs1Vector vector) {
4142
public String toString() {
4243
return "PKCS#1 Vector: " + vector.getDescription() + " Fingerprint=" + fingerprint.toString();
4344
}
45+
46+
@Override
47+
public int hashCode() {
48+
int hash = 3;
49+
hash = 67 * hash + Objects.hashCode(this.fingerprint);
50+
hash = 67 * hash + Objects.hashCode(this.vector);
51+
return hash;
52+
}
53+
54+
@Override
55+
public boolean equals(Object obj) {
56+
if (this == obj) {
57+
return true;
58+
}
59+
if (obj == null) {
60+
return false;
61+
}
62+
if (getClass() != obj.getClass()) {
63+
return false;
64+
}
65+
final VectorFingerprintPair other = (VectorFingerprintPair) obj;
66+
if (!Objects.equals(this.fingerprint, other.fingerprint)) {
67+
return false;
68+
}
69+
if (!Objects.equals(this.vector, other.vector)) {
70+
return false;
71+
}
72+
return true;
73+
}
74+
4475
}

Attacks/src/main/java/de/rub/nds/tlsattacker/attacks/util/response/EqualityErrorTranslator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public static String translation(EqualityError error, ResponseFingerprint finger
6161
builder.append("The server seems to ocassionally respond with a socket exception.");
6262
break;
6363
case SOCKET_STATE:
64-
builder.append("The server seems to ocassionally move the TCP socket in different states. Note that this difference is prone to false-positives if the network is unreliable.");
64+
builder.append("The server seems to ocassionally move the TCP socket in different states.");
6565
break;
6666
default:
6767
builder.append(error.toString());

TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/config/delegate/CiphersuiteDelegate.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import de.rub.nds.tlsattacker.core.config.Config;
1313
import de.rub.nds.tlsattacker.core.config.converters.CipherSuiteConverter;
1414
import de.rub.nds.tlsattacker.core.constants.CipherSuite;
15+
import java.util.ArrayList;
16+
import java.util.Arrays;
1517
import java.util.Collections;
1618
import java.util.List;
1719

@@ -35,6 +37,10 @@ public void setCipherSuites(List<CipherSuite> cipherSuites) {
3537
this.cipherSuites = cipherSuites;
3638
}
3739

40+
public void setCipherSuites(CipherSuite... cipherSuites) {
41+
this.cipherSuites = new ArrayList(Arrays.asList(cipherSuites));
42+
}
43+
3844
@Override
3945
public void applyDelegate(Config config) {
4046
if (cipherSuites != null) {

0 commit comments

Comments
 (0)