Skip to content

Conversation

GiuseppePorcu
Copy link
Contributor

Hi there,
This PR contains the implementation of the weak credential tester for HyperSQL.
As described within the Issue, the service has default credentials that if not changed allow an attacker to access the DBMS.

Below it is possible to find the necessary information for review:
Issue: #576
PR Testbed: google/security-testbeds#117
PR Scanner: google/tsunami-security-scanner#128
Thank you.

Copy link
Collaborator

@giacomo-doyensec giacomo-doyensec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @GiuseppePorcu, thanks for your submission!

I found multiple issues that need to be addressed before proceeding with approval. I report them as follows:

  • Missing optimization of credential testing against the service: HyperSQL returns well-known error codes on exceptions, which can be leveraged to avoid unnecessary credential attempts, saving both time and network resources. I've provided suggestions for this optimization in this review (note that all suggestions I provided must be taken as recommendations. Please feel free to improve the code or adopt an alternative solution that you believe might be more optimal).

  • Missing proper handling of non-existent DB error: The DB name, testdb, used for the connection is hardcoded, as no default database is automatically created during service creation. The code does not handle the case when this DB does not exist. Error -458: database alias does not exist is returned when the name is not associated with anything. Please add logic to either find other potential valid names or minimize attempts to that service and log the results.

  • Missing entry in target_service.proto file: To generate the correct TargetService entry, add HSQLDB = 26; // HyperSQL (SQL Database) at the end of the enum TargetService in google/detectors/credentials/generic_weak_credential_detector/src/main/proto/target_service.proto. Also, please update the "Next ID" field in the comment above it.

  • Update tests accordingly.

Let me know if anything is unclear, and I'll be happy to provide further assistance.

import com.google.tsunami.proto.NetworkService;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import the necessary libraries

Suggested change
import java.util.List;
import java.util.HashSet;
import java.util.List;

public final class HyperSQLCredentialTester extends CredentialTester {
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
private final ConnectionProviderInterface connectionProvider;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error code constant

Suggested change
private static final int HSQL_INVALID_USER_ERROR = -4001;


@Override
public boolean batched() {
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disable batching to avoid unnecessary credential attempts

Suggested change
return true;
return false;

Comment on lines +71 to +80
public ImmutableList<TestCredential> testValidCredentials(
NetworkService networkService, List<TestCredential> credentials) {
if (!canAccept(networkService)) {
return ImmutableList.of();
}

return credentials.stream()
.filter(cred -> isHsqlAccessible(networkService, cred))
.collect(toImmutableList());
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invalidUsernames variable will contain a set of all usernames that have been tried and have failed, so they should be removed from the list of valid credentials.

Suggested change
public ImmutableList<TestCredential> testValidCredentials(
NetworkService networkService, List<TestCredential> credentials) {
if (!canAccept(networkService)) {
return ImmutableList.of();
}
return credentials.stream()
.filter(cred -> isHsqlAccessible(networkService, cred))
.collect(toImmutableList());
}
public ImmutableList<TestCredential> testValidCredentials(
NetworkService networkService, List<TestCredential> credentials) {
HashSet<String> invalidUsernames = new HashSet<String>();
if (!canAccept(networkService)) {
return ImmutableList.of();
}
return credentials.stream()
.filter(cred -> !invalidUsernames.contains(cred.username()))
.filter(cred -> isHsqlAccessible(networkService, cred, invalidUsernames))
.collect(toImmutableList());
}

* connection However hsqldb does not create a default database during installation testdb is the
* database name used within the documentation
*/
private boolean isHsqlAccessible(NetworkService networkService, TestCredential credential) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method signature should be updated to accept the newly introduced variable.

Suggested change
private boolean isHsqlAccessible(NetworkService networkService, TestCredential credential) {
private boolean isHsqlAccessible(
NetworkService networkService, TestCredential credential, HashSet<String> invalidUsernames) {

Comment on lines +104 to +107
} catch (SQLException e) {
logger.atSevere().log(
"HyperSQLCredentialTester sql error: %s (%d)", e.getMessage(), e.getErrorCode());
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error code -4001 is returned when the user is invalid.
Error code -4000 is returned when the user exists but the password is invalid.

This check optimizes testing by an order of magnitude, as it tries common passwords only on valid usernames.

Suggested change
} catch (SQLException e) {
logger.atSevere().log(
"HyperSQLCredentialTester sql error: %s (%d)", e.getMessage(), e.getErrorCode());
}
} catch (SQLException e) {
if (e.getErrorCode() == HSQL_INVALID_USER_ERROR) {
invalidUsernames.add(credential.username());
}
logger.atSevere().log(
"HyperSQLCredentialTester sql error: %s (%d)", e.getMessage(), e.getErrorCode());
}

@tooryx tooryx linked an issue Jul 25, 2025 that may be closed by this pull request
@tooryx
Copy link
Member

tooryx commented Sep 5, 2025

Hi @GiuseppePorcu,

Are you still willing to contribute on that PR? If so, please apply the suggestions provided during the review so that we can proceed with merging it.

Thank you,
~tooryx

@GiuseppePorcu
Copy link
Contributor Author

Hi @tooryx ,
yes I'm still working on one of the modifications and running some tests in order to verify that everything is working correctly.
I’ll push the modification once it’s ready, which I expect to be before the end of the month.

Sorry for the delay, and thank you for your patience.

@tooryx tooryx added the java label Oct 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PRP: HyperSQL weak credential tester
3 participants