Skip to content

Add SRP (Secure Remote Password) probe #140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ public enum TlsAnalyzedProperty implements AnalyzedProperty {
SUPPORTS_DSS(TlsAnalyzedPropertyCategory.CIPHER_SUITES),
SUPPORTS_GOST(TlsAnalyzedPropertyCategory.CIPHER_SUITES),
SUPPORTS_SRP(TlsAnalyzedPropertyCategory.CIPHER_SUITES),
SUPPORTS_SRP_EXTENSION(TlsAnalyzedPropertyCategory.EXTENSIONS),
SRP_CIPHERSUITES(TlsAnalyzedPropertyCategory.CIPHER_SUITES),
MISSING_SRP_EXTENSION_BUG(TlsAnalyzedPropertyCategory.ATTACKS),
SUPPORTS_KERBEROS(TlsAnalyzedPropertyCategory.CIPHER_SUITES),
SUPPORTS_PSK_PLAIN(TlsAnalyzedPropertyCategory.CIPHER_SUITES),
SUPPORTS_PSK_RSA(TlsAnalyzedPropertyCategory.CIPHER_SUITES),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public enum TlsProbeType implements ProbeType {
RANDOMNESS("Randomness"),
TLS_FALLBACK_SCSV("TLS Fallback SCSV"),
CONNECTION_CLOSING_DELTA("Connection Closing Delta"),
SRP("SRP"),
// CLIENT SPECIFIC PROBES
FORCED_COMPRESSION("Forced Compression"),
FREAK("Freak"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ public static PrintingScheme getDefaultPrintingScheme() {
colorMap.put(
TlsAnalyzedProperty.SUPPORTS_SRP,
getDefaultColorEncoding(AnsiColor.DEFAULT_COLOR, AnsiColor.DEFAULT_COLOR));
colorMap.put(
TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION,
getDefaultColorEncoding(AnsiColor.DEFAULT_COLOR, AnsiColor.DEFAULT_COLOR));
colorMap.put(
TlsAnalyzedProperty.SRP_CIPHERSUITES,
getDefaultColorEncoding(AnsiColor.DEFAULT_COLOR, AnsiColor.DEFAULT_COLOR));
colorMap.put(
TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG,
getDefaultColorEncoding(AnsiColor.RED, AnsiColor.GREEN));
colorMap.put(
TlsAnalyzedProperty.SUPPORTS_KERBEROS,
getDefaultColorEncoding(AnsiColor.DEFAULT_COLOR, AnsiColor.DEFAULT_COLOR));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import de.rub.nds.tlsscanner.serverscanner.probe.SignatureAndHashAlgorithmProbe;
import de.rub.nds.tlsscanner.serverscanner.probe.SignatureHashAlgorithmOrderProbe;
import de.rub.nds.tlsscanner.serverscanner.probe.SniProbe;
import de.rub.nds.tlsscanner.serverscanner.probe.SrpProbe;
import de.rub.nds.tlsscanner.serverscanner.probe.TlsFallbackScsvProbe;
import de.rub.nds.tlsscanner.serverscanner.probe.TlsServerProbe;
import de.rub.nds.tlsscanner.serverscanner.probe.TokenbindingProbe;
Expand Down Expand Up @@ -216,6 +217,7 @@ protected void fillProbeLists() {
registerProbeForExecution(new CipherSuiteProbe(configSelector, parallelExecutor));
registerProbeForExecution(new DirectRaccoonProbe(configSelector, parallelExecutor));
registerProbeForExecution(new CipherSuiteOrderProbe(configSelector, parallelExecutor));
registerProbeForExecution(new SrpProbe(configSelector, parallelExecutor));
registerProbeForExecution(new ExtensionProbe(configSelector, parallelExecutor));
registerProbeForExecution(new ECPointFormatProbe(configSelector, parallelExecutor));
registerProbeForExecution(new ResumptionProbe(configSelector, parallelExecutor));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/*
* TLS-Scanner - A TLS configuration and analysis tool based on TLS-Attacker
*
* Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
*
* Licensed under Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0.txt
*/
package de.rub.nds.tlsscanner.serverscanner.probe;

import de.rub.nds.modifiablevariable.util.Modifiable;
import de.rub.nds.scanner.core.probe.requirements.Requirement;
import de.rub.nds.tlsattacker.core.config.Config;
import de.rub.nds.tlsattacker.core.constants.AlertDescription;
import de.rub.nds.tlsattacker.core.constants.AlertLevel;
import de.rub.nds.tlsattacker.core.constants.CipherSuite;
import de.rub.nds.tlsattacker.core.constants.HandshakeMessageType;
import de.rub.nds.tlsattacker.core.constants.ProtocolMessageType;
import de.rub.nds.tlsattacker.core.constants.ProtocolVersion;
import de.rub.nds.tlsattacker.core.protocol.message.AlertMessage;
import de.rub.nds.tlsattacker.core.protocol.message.ServerHelloMessage;
import de.rub.nds.tlsattacker.core.protocol.message.extension.SRPExtensionMessage;
import de.rub.nds.tlsattacker.core.state.State;
import de.rub.nds.tlsattacker.core.workflow.ParallelExecutor;
import de.rub.nds.tlsattacker.core.workflow.WorkflowTraceResultUtil;
import de.rub.nds.tlsattacker.core.workflow.factory.WorkflowTraceType;
import de.rub.nds.tlsscanner.core.constants.ProtocolType;
import de.rub.nds.tlsscanner.core.constants.TlsAnalyzedProperty;
import de.rub.nds.tlsscanner.core.constants.TlsProbeType;
import de.rub.nds.tlsscanner.core.probe.requirements.ProtocolTypeTrueRequirement;
import de.rub.nds.tlsscanner.serverscanner.probe.requirements.WorkingConfigRequirement;
import de.rub.nds.tlsscanner.serverscanner.report.ServerReport;
import de.rub.nds.tlsscanner.serverscanner.selector.ConfigSelector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class SrpProbe extends TlsServerProbe {

private static final Logger LOGGER = LogManager.getLogger();

// SRP cipher suites from RFC 5054
private static final List<CipherSuite> SRP_CIPHER_SUITES =
Arrays.asList(
CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA);

// Test SRP identity
private static final byte[] TEST_SRP_IDENTITY = "testuser".getBytes();

private Boolean supportsSrpExtension = null;
private Boolean missingSrpExtensionBug = null;
private List<CipherSuite> supportedSrpCipherSuites = null;

public SrpProbe(ConfigSelector configSelector, ParallelExecutor parallelExecutor) {
super(parallelExecutor, TlsProbeType.SRP, configSelector);
register(
TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION,
TlsAnalyzedProperty.SRP_CIPHERSUITES,
TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG);
}

@Override
public void executeTest() {
// Test basic SRP support with extension
testSrpWithExtension();

// Test for missing SRP extension bug
testMissingSrpExtensionBug();
}

private void testSrpWithExtension() {
Config config = configSelector.getBaseConfig();
config.setWorkflowTraceType(WorkflowTraceType.DYNAMIC_HELLO);
config.setDefaultClientSupportedCipherSuites(SRP_CIPHER_SUITES);
config.setHighestProtocolVersion(ProtocolVersion.TLS12);
config.setEnforceSettings(true);
config.setStopReceivingAfterFatal(true);
config.setStopActionsAfterFatal(true);
config.setAddServerNameIndicationExtension(true);

// Add SRP extension
SRPExtensionMessage srpExtension = new SRPExtensionMessage();
srpExtension.setSrpIdentifier(Modifiable.explicit(TEST_SRP_IDENTITY));
srpExtension.setSrpIdentifierLength(Modifiable.explicit(TEST_SRP_IDENTITY.length));

config.setAddSRPExtension(true);
config.setSecureRemotePasswordExtensionIdentifier(TEST_SRP_IDENTITY);

State state = new State(config);
executeState(state);

ServerHelloMessage serverHello =
(ServerHelloMessage)
WorkflowTraceResultUtil.getFirstReceivedMessage(
state.getWorkflowTrace(), HandshakeMessageType.SERVER_HELLO);

if (serverHello != null && serverHello.getSelectedCipherSuite() != null) {
CipherSuite selectedSuite =
CipherSuite.getCipherSuite(serverHello.getSelectedCipherSuite().getValue());
if (SRP_CIPHER_SUITES.contains(selectedSuite)) {
supportsSrpExtension = true;
supportedSrpCipherSuites = new ArrayList<>();
supportedSrpCipherSuites.add(selectedSuite);

// Test other SRP cipher suites
for (CipherSuite suite : SRP_CIPHER_SUITES) {
if (!suite.equals(selectedSuite) && testSingleSrpCipherSuite(suite)) {
supportedSrpCipherSuites.add(suite);
}
}
} else {
supportsSrpExtension = false;
supportedSrpCipherSuites = new ArrayList<>();
}
} else {
supportsSrpExtension = false;
supportedSrpCipherSuites = new ArrayList<>();
}
}

private boolean testSingleSrpCipherSuite(CipherSuite cipherSuite) {
Config config = configSelector.getBaseConfig();
config.setWorkflowTraceType(WorkflowTraceType.DYNAMIC_HELLO);
config.setDefaultClientSupportedCipherSuites(Arrays.asList(cipherSuite));
config.setHighestProtocolVersion(ProtocolVersion.TLS12);
config.setEnforceSettings(true);
config.setStopReceivingAfterFatal(true);
config.setStopActionsAfterFatal(true);
config.setAddSRPExtension(true);
config.setSecureRemotePasswordExtensionIdentifier(TEST_SRP_IDENTITY);

State state = new State(config);
executeState(state);

ServerHelloMessage serverHello =
(ServerHelloMessage)
WorkflowTraceResultUtil.getFirstReceivedMessage(
state.getWorkflowTrace(), HandshakeMessageType.SERVER_HELLO);

if (serverHello != null && serverHello.getSelectedCipherSuite() != null) {
CipherSuite selectedSuite =
CipherSuite.getCipherSuite(serverHello.getSelectedCipherSuite().getValue());
return selectedSuite.equals(cipherSuite);
}
return false;
}

private void testMissingSrpExtensionBug() {
// Test if server properly rejects SRP cipher suites without SRP extension
Config config = configSelector.getBaseConfig();
config.setWorkflowTraceType(WorkflowTraceType.DYNAMIC_HELLO);
config.setDefaultClientSupportedCipherSuites(SRP_CIPHER_SUITES);
config.setHighestProtocolVersion(ProtocolVersion.TLS12);
config.setEnforceSettings(true);
config.setStopReceivingAfterFatal(true);
config.setStopActionsAfterFatal(true);
config.setAddSRPExtension(false); // Explicitly don't add SRP extension

State state = new State(config);
executeState(state);

// Check if we received an alert
AlertMessage alert = null;
if (WorkflowTraceResultUtil.didReceiveMessage(
state.getWorkflowTrace(), ProtocolMessageType.ALERT)) {
alert =
(AlertMessage)
WorkflowTraceResultUtil.getFirstReceivedMessage(
state.getWorkflowTrace(), ProtocolMessageType.ALERT);
}

ServerHelloMessage serverHello =
(ServerHelloMessage)
WorkflowTraceResultUtil.getFirstReceivedMessage(
state.getWorkflowTrace(), HandshakeMessageType.SERVER_HELLO);

if (serverHello != null && serverHello.getSelectedCipherSuite() != null) {
CipherSuite selectedSuite =
CipherSuite.getCipherSuite(serverHello.getSelectedCipherSuite().getValue());
if (SRP_CIPHER_SUITES.contains(selectedSuite)) {
// Server selected SRP cipher suite without SRP extension - this is a bug
missingSrpExtensionBug = true;
} else {
missingSrpExtensionBug = false;
}
} else if (alert != null && alert.getLevel() != null && alert.getDescription() != null) {
// Check if we got the expected alert
if (alert.getLevel().getValue() == AlertLevel.FATAL.getValue()
&& alert.getDescription().getValue()
== AlertDescription.UNKNOWN_PSK_IDENTITY.getValue()) {
// Correct behavior - server sent unknown_psk_identity alert
missingSrpExtensionBug = false;
} else {
// Server sent an alert but not the expected one
missingSrpExtensionBug = false;
}
} else {
// No clear response
missingSrpExtensionBug = false;
}
}

@Override
public Requirement<ServerReport> getRequirements() {
return new ProtocolTypeTrueRequirement<ServerReport>(ProtocolType.TLS)
.and(new WorkingConfigRequirement(configSelector));
}

@Override
public void adjustConfig(ServerReport report) {}

@Override
protected void mergeData(ServerReport report) {
if (supportsSrpExtension != null) {
report.putResult(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION, supportsSrpExtension);
} else {
report.putResult(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION, false);
}

if (supportedSrpCipherSuites != null && !supportedSrpCipherSuites.isEmpty()) {
report.putResult(TlsAnalyzedProperty.SRP_CIPHERSUITES, supportedSrpCipherSuites);
} else {
report.putResult(TlsAnalyzedProperty.SRP_CIPHERSUITES, new ArrayList<CipherSuite>());
}

if (missingSrpExtensionBug != null) {
report.putResult(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG, missingSrpExtensionBug);
} else {
report.putResult(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG, false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* TLS-Scanner - A TLS configuration and analysis tool based on TLS-Attacker
*
* Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
*
* Licensed under Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0.txt
*/
package de.rub.nds.tlsscanner.serverscanner.probe;

import de.rub.nds.scanner.core.probe.result.TestResults;
import de.rub.nds.tls.subject.TlsImplementationType;
import de.rub.nds.tlsattacker.util.tests.TestCategories;
import de.rub.nds.tlsscanner.core.constants.TlsAnalyzedProperty;
import org.junit.jupiter.api.Tag;

@Tag(TestCategories.INTEGRATION_TEST)
public class SrpProbeIT extends AbstractProbeIT {

public SrpProbeIT() {
super(TlsImplementationType.OPENSSL, "1.1.1f", "");
}

@Override
protected TlsServerProbe getProbe() {
return new SrpProbe(configSelector, parallelExecutor);
}

@Override
protected void prepareReport() {}

@Override
protected boolean executedAsPlanned() {
// Most servers don't support SRP, so we expect false
return verifyProperty(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION, TestResults.FALSE)
&& verifyProperty(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG, TestResults.FALSE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* TLS-Scanner - A TLS configuration and analysis tool based on TLS-Attacker
*
* Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
*
* Licensed under Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0.txt
*/
package de.rub.nds.tlsscanner.serverscanner.probe;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import de.rub.nds.scanner.core.constants.TestResults;
import de.rub.nds.tlsscanner.core.constants.TlsAnalyzedProperty;
import de.rub.nds.tlsscanner.core.constants.TlsProbeType;
import de.rub.nds.tlsscanner.serverscanner.report.ServerReport;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class SrpProbeTest {

private ServerReport report;

@BeforeEach
public void setUp() {
report = new ServerReport();
}

@Test
public void testSrpProbeRegistersCorrectProperties() {
SrpProbe probe = new SrpProbe(null, null);

assertEquals(TlsProbeType.SRP, probe.getType());
assertTrue(
probe.getAnnouncedResults().contains(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION));
assertTrue(probe.getAnnouncedResults().contains(TlsAnalyzedProperty.SRP_CIPHERSUITES));
assertTrue(
probe.getAnnouncedResults()
.contains(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG));
}

@Test
public void testMergeDataWithNoSrpSupport() {
// Create a test probe instance with test data
SrpProbe probe =
new SrpProbe(null, null) {
@Override
protected void mergeData(ServerReport report) {
super.mergeData(report);
}
};

// Merge data with no SRP support
probe.mergeData(report);

// Verify the results
assertEquals(
TestResults.FALSE, report.getResult(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION));
assertNotNull(report.getResult(TlsAnalyzedProperty.SRP_CIPHERSUITES));
assertEquals(
TestResults.FALSE, report.getResult(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG));
}
}