diff --git a/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/GenericWeakCredentialDetectorBootstrapModule.java b/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/GenericWeakCredentialDetectorBootstrapModule.java index db865a24a..b27c7693c 100644 --- a/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/GenericWeakCredentialDetectorBootstrapModule.java +++ b/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/GenericWeakCredentialDetectorBootstrapModule.java @@ -41,6 +41,7 @@ import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.testers.jenkins.JenkinsCredentialTester; import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.testers.mlflow.MlFlowCredentialTester; import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.testers.mysql.MysqlCredentialTester; +import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.testers.hive.HiveCredentialTester; import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.testers.ncrack.NcrackCredentialTester; import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.testers.postgres.PostgresCredentialTester; import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.testers.rabbitmq.RabbitMQCredentialTester; @@ -70,6 +71,7 @@ protected void configurePlugin() { credentialTesterBinder.addBinding().to(JenkinsCredentialTester.class); credentialTesterBinder.addBinding().to(MlFlowCredentialTester.class); credentialTesterBinder.addBinding().to(MysqlCredentialTester.class); + credentialTesterBinder.addBinding().to(HiveCredentialTester.class); credentialTesterBinder.addBinding().to(HydraCredentialTester.class); credentialTesterBinder.addBinding().to(NcrackCredentialTester.class); credentialTesterBinder.addBinding().to(PostgresCredentialTester.class); diff --git a/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/provider/Top100Passwords.java b/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/provider/Top100Passwords.java index 3a2d2afa5..c0f9e2347 100644 --- a/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/provider/Top100Passwords.java +++ b/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/provider/Top100Passwords.java @@ -38,6 +38,7 @@ public final class Top100Passwords extends CredentialProvider { private static final ImmutableList TOP_USER_NAMES = ImmutableList.of( + "", "anonymous", "root", "admin", diff --git a/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/testers/hive/HiveCredentialTester.java b/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/testers/hive/HiveCredentialTester.java new file mode 100644 index 000000000..4442c63fd --- /dev/null +++ b/google/detectors/credentials/generic_weak_credential_detector/src/main/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/testers/hive/HiveCredentialTester.java @@ -0,0 +1,123 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.testers.hive; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.tsunami.common.net.http.HttpRequest.get; + +import com.google.common.collect.ImmutableList; +import com.google.common.flogger.GoogleLogger; +import com.google.common.net.HostAndPort; +import com.google.tsunami.common.data.NetworkEndpointUtils; +import com.google.tsunami.common.data.NetworkServiceUtils; +import com.google.tsunami.common.net.db.ConnectionProviderInterface; +import com.google.tsunami.common.net.http.HttpClient; +import com.google.tsunami.common.net.http.HttpResponse; +import com.google.tsunami.common.net.http.HttpStatus; +import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.provider.TestCredential; +import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.tester.CredentialTester; +import com.google.tsunami.proto.NetworkService; +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.Optional; +import javax.inject.Inject; + +/** Credential tester specifically for hive. */ +public final class HiveCredentialTester extends CredentialTester { + private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); + private final ConnectionProviderInterface connectionProvider; + private final HttpClient httpClient; + private static final String HIVE_TITLE = "HiveServer2"; + private static final int HIVE_TCP_PORT = 10000; + + @Inject + HiveCredentialTester(ConnectionProviderInterface connectionProvider, HttpClient httpClient) { + this.connectionProvider = checkNotNull(connectionProvider); + this.httpClient = httpClient; + } + + @Override + public String name() { + return "HiveCredentialTester"; + } + + @Override + public String description() { + return "Hive credential tester."; + } + + @Override + public boolean canAccept(NetworkService networkService) { + String targetUri = NetworkServiceUtils.buildWebApplicationRootUrl(networkService); + + try { + HttpResponse response = httpClient.send(get(targetUri).withEmptyHeaders().build(), networkService); + if (response != null) { + Optional body = response.bodyString(); + if (response.status().code() == HttpStatus.OK.code() + && body.isPresent() && body.get().contains(HIVE_TITLE)) { + return true; + } + } + } catch (IOException e) { + return false; + } + return false; + } + + @Override + public boolean batched() { + return true; + } + + @Override + public ImmutableList testValidCredentials( + NetworkService networkService, List credentials) { + if (!canAccept(networkService)) { + return ImmutableList.of(); + } + + return credentials.stream() + .filter(cred -> isHiveAccessible(networkService, cred)) + .collect(toImmutableList()); + } + + private boolean isHiveAccessible(NetworkService networkService, TestCredential credential) { + HostAndPort targetPage = NetworkEndpointUtils.toHostAndPort(networkService.getNetworkEndpoint()); + try { + String url = String.format("jdbc:hive2://%s:%d/default", targetPage.getHost(), HIVE_TCP_PORT); + logger.atInfo().log( + "url: %s, username: %s, password: %s", + url, credential.username(), credential.password().orElse("")); + Connection conn = + connectionProvider.getConnection( + url, credential.username(), credential.password().orElse("")); + + if (conn != null) { + logger.atInfo().log("Connected to the Hive server successfully."); + return true; + } + } catch (SQLException e) { + logger.atSevere().log( + "HiveCredentialTester sql error: %s (%d)", e.getMessage(), e.getErrorCode()); + } + return false; + } +} diff --git a/google/detectors/credentials/generic_weak_credential_detector/src/main/proto/target_service.proto b/google/detectors/credentials/generic_weak_credential_detector/src/main/proto/target_service.proto index 02d3496ef..f7e877b7c 100644 --- a/google/detectors/credentials/generic_weak_credential_detector/src/main/proto/target_service.proto +++ b/google/detectors/credentials/generic_weak_credential_detector/src/main/proto/target_service.proto @@ -58,4 +58,5 @@ enum TargetService { OWA = 23; // Outlook Web App (Web Application) DICOM = 24; // Digital Imaging and Communications in Medicine (Healthcare // Protocol) + HIVE = 25; // Apache Hive (SQL Database) } diff --git a/google/detectors/credentials/generic_weak_credential_detector/src/test/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/testers/hive/HiveCredentialTesterTest.java b/google/detectors/credentials/generic_weak_credential_detector/src/test/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/testers/hive/HiveCredentialTesterTest.java new file mode 100644 index 000000000..d8481ff34 --- /dev/null +++ b/google/detectors/credentials/generic_weak_credential_detector/src/test/java/com/google/tsunami/plugins/detectors/credentials/genericweakcredentialdetector/testers/hive/HiveCredentialTesterTest.java @@ -0,0 +1,180 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.testers.hive; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.tsunami.common.data.NetworkEndpointUtils.forHostnameAndPort; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import com.google.common.collect.ImmutableList; +import com.google.protobuf.ByteString; +import com.google.tsunami.common.net.db.ConnectionProviderInterface; +import com.google.tsunami.common.net.http.HttpClient; +import com.google.tsunami.common.net.http.HttpHeaders; +import com.google.tsunami.common.net.http.HttpRequest; +import com.google.tsunami.common.net.http.HttpResponse; +import com.google.tsunami.common.net.http.HttpStatus; +import com.google.tsunami.plugins.detectors.credentials.genericweakcredentialdetector.provider.TestCredential; +import com.google.tsunami.proto.NetworkService; +import com.google.tsunami.proto.Software; +import java.io.IOException; +import java.sql.Connection; +import java.util.Optional; +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** Tests for {@link HiveCredentialTester}. */ +@RunWith(JUnit4.class) +public class HiveCredentialTesterTest { + @Rule public MockitoRule rule = MockitoJUnit.rule(); + @Mock private ConnectionProviderInterface mockConnectionProvider; + @Mock private Connection mockConnection; + @Mock private HttpClient httpClient; + private HiveCredentialTester tester; + private MockWebServer mockWebServer; + + private static final TestCredential WEAK_CRED_1 = + TestCredential.create("user", Optional.of("1234")); + private static final TestCredential WEAK_CRED_2 = + TestCredential.create("root", Optional.of("pass")); + + @Before + public void setup() throws IOException { + mockWebServer = new MockWebServer(); + startMockWebServer(); + tester = new HiveCredentialTester(mockConnectionProvider, httpClient); + } + + @After + public void tearDown() throws IOException { + mockWebServer.shutdown(); + } + + @Test + public void detect_weakCredExists_returnsWeakCred() throws Exception { + when(mockConnectionProvider.getConnection( + "jdbc:hive2://example.com:10000/default", "user", "1234")) + .thenReturn(mockConnection); + when(httpClient.send(any(HttpRequest.class), any(NetworkService.class))) + .thenReturn(HttpResponse.builder() + .setStatus(HttpStatus.OK) + .setBodyBytes(ByteString.copyFromUtf8("HiveServer2")) + .setHeaders(HttpHeaders.builder().addHeader("Content-Type", "text/html").build()) + .build()); + NetworkService targetNetworkService = + NetworkService.newBuilder() + .setNetworkEndpoint(forHostnameAndPort("example.com", mockWebServer.getPort())) + .setServiceName("http") + .setSoftware(Software.newBuilder().setName("hive")) + .build(); + + assertThat(tester.testValidCredentials(targetNetworkService, ImmutableList.of(WEAK_CRED_1))) + .containsExactly(WEAK_CRED_1); + } + + @Test + public void detect_weakCredsExist_returnsAllWeakCreds() throws Exception { + when(mockConnectionProvider.getConnection( + "jdbc:hive2://example.com:10000/default", "user", "1234")) + .thenReturn(mockConnection); + when(mockConnectionProvider.getConnection( + "jdbc:hive2://example.com:10000/default", "root", "pass")) + .thenReturn(mockConnection); + when(httpClient.send(any(HttpRequest.class), any(NetworkService.class))) + .thenReturn(HttpResponse.builder() + .setStatus(HttpStatus.OK) + .setBodyBytes(ByteString.copyFromUtf8("HiveServer2")) + .setHeaders(HttpHeaders.builder().addHeader("Content-Type", "text/html").build()) + .build()); + NetworkService targetNetworkService = + NetworkService.newBuilder() + .setNetworkEndpoint(forHostnameAndPort("example.com", mockWebServer.getPort())) + .setServiceName("http") + .setSoftware(Software.newBuilder().setName("hive")) + .build(); + + assertThat( + tester.testValidCredentials( + targetNetworkService, ImmutableList.of(WEAK_CRED_1, WEAK_CRED_2))) + .containsExactly(WEAK_CRED_1, WEAK_CRED_2); + } + + @Test + public void detect_noWeakCred_returnsNoCred() throws Exception { + when(mockConnectionProvider.getConnection( + "jdbc:hive2://example.com:10000/default", "hardtoguess", "hardtoguess")) + .thenReturn(mockConnection); + when(httpClient.send(any(HttpRequest.class), any(NetworkService.class))) + .thenReturn(HttpResponse.builder() + .setStatus(HttpStatus.OK) + .setBodyBytes(ByteString.copyFromUtf8("HiveServer2")) + .setHeaders(HttpHeaders.builder().addHeader("Content-Type", "text/html").build()) + .build()); + NetworkService targetNetworkService = + NetworkService.newBuilder() + .setNetworkEndpoint(forHostnameAndPort("example.com", mockWebServer.getPort())) + .setServiceName("http") + .setSoftware(Software.newBuilder().setName("hive")) + .build(); + + assertThat(tester.testValidCredentials(targetNetworkService, ImmutableList.of(WEAK_CRED_1))) + .isEmpty(); + } + + @Test + public void detect_hiveService_skips() throws Exception { + when(mockConnectionProvider.getConnection(any(), any(), any())).thenReturn(mockConnection); + NetworkService targetNetworkService = + NetworkService.newBuilder() + .setNetworkEndpoint(forHostnameAndPort("example.com", mockWebServer.getPort())) + .setServiceName("http") + .setSoftware(Software.newBuilder().setName("hive")) + .build(); + + assertThat(tester.testValidCredentials(targetNetworkService, ImmutableList.of())) + .isEmpty(); + verifyNoInteractions(mockConnectionProvider); + } + + private void startMockWebServer() throws IOException { + final Dispatcher dispatcher = + new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) { + return new MockResponse() + .setResponseCode(200) + .setBody("HiveServer2"); + } + }; + mockWebServer.setDispatcher(dispatcher); + mockWebServer.start(); + mockWebServer.url("/"); + } +}