diff --git a/templated/templateddetector/plugins/cve/2026/MLflow_CVE_2026_2635.textproto b/templated/templateddetector/plugins/cve/2026/MLflow_CVE_2026_2635.textproto new file mode 100755 index 000000000..a2365072f --- /dev/null +++ b/templated/templateddetector/plugins/cve/2026/MLflow_CVE_2026_2635.textproto @@ -0,0 +1,82 @@ +# proto-file: proto/templated_plugin.proto +# proto-message: TemplatedPlugin + +############### +# PLUGIN INFO # +############### + +info: { + type: VULN_DETECTION + name: "MLflow_CVE_2026_2635" + author: "sanjaymahajan14" + version: "1.0" +} + +finding: { + main_id: { + publisher: "GOOGLE" + value: "CVE-2026-2635" + } + severity: CRITICAL + title: "MLflow Hard-coded Default Credentials Authentication Bypass" + description: "A critical authentication bypass vulnerability exists in MLflow due to hard-coded default credentials in basic_auth.ini. Unauthenticated remote attackers can leverage these default credentials to gain administrative access to MLflow instances, access sensitive models/data, and potentially execute arbitrary code." + recommendation: "Update basic_auth.ini to change default credentials, disable default authentication mechanisms, or upgrade MLflow to a patched version 2.11.0 (or the latest available stable release)." + related_id: { + publisher: "CVE" + value: "CVE-2026-2635" + } +} + +config: {} + +########### +# ACTIONS # +########### + +actions: { + name: "fingerprint_mlflow" + http_request: { + method: GET + uri: "/" + response: { + http_status: 401 + expect_any: { + conditions: [ + { body: {} contains: "mlflow" } + ] + } + } + } +} + +actions: { + name: "probe_default_basic_auth" + http_request: { + method: GET + uri: "/api/2.0/mlflow/users/get?username=admin" + headers: [ + # Basic authorization header with default 'admin:password' credentials + { name: "Authorization" value: "Basic YWRtaW46cGFzc3dvcmQ=" } + ] + response: { + http_status: 200 + expect_all: { + conditions: [ + { body: {} contains: "\"is_admin\":true" }, + { body: {} contains: "\"username\":\"admin\"" } + ] + } + } + } +} + +############# +# WORKFLOWS # +############# + +workflows: { + actions: [ + "fingerprint_mlflow", + "probe_default_basic_auth" + ] +} \ No newline at end of file diff --git a/templated/templateddetector/plugins/cve/2026/MLflow_CVE_2026_2635_test.textproto b/templated/templateddetector/plugins/cve/2026/MLflow_CVE_2026_2635_test.textproto new file mode 100755 index 000000000..0fb006a0a --- /dev/null +++ b/templated/templateddetector/plugins/cve/2026/MLflow_CVE_2026_2635_test.textproto @@ -0,0 +1,75 @@ +# proto-file: proto/templated_plugin_tests.proto +# proto-message: TemplatedPluginTests + +config: { + tested_plugin: "MLflow_CVE_2026_2635" +} + +######### +# TESTS # +######### + +tests: { + name: "whenVulnerable_returnsTrue" + expect_vulnerability: true + + mock_http_server: { + mock_responses: [ + # 1. Fingerprint MLflow (401 Unauthorized with basic auth prompt) + { + uri: "/" + status: 401 + body_content: "You are not authenticated. Please see https://www.mlflow.org/docs/latest/auth/index.html#authenticating-to-mlflow on how to authenticate." + }, + # 2. Probe default admin:password credentials + { + uri: "/api/2.0/mlflow/users/get?username=admin" + status: 200 + headers: [ + { name: "Content-Type" value: "application/json" } + ] + body_content: "{\"user\":{\"experiment_permissions\":[],\"id\":1,\"is_admin\":true,\"registered_model_permissions\":[],\"username\":\"admin\"}}" + } + ] + } +} + +tests: { + name: "whenNotVulnerable_returnsFalse" + expect_vulnerability: false + + mock_http_server: { + mock_responses: [ + # 1. Fingerprint MLflow + { + uri: "/" + status: 401 + body_content: "You are not authenticated. Please see https://www.mlflow.org/docs/latest/auth/index.html#authenticating-to-mlflow on how to authenticate." + }, + # 2. Basic auth present, but default admin:password fails with 401 + { + uri: "/api/2.0/mlflow/users/get?username=admin" + status: 401 + headers: [ + { name: "Content-Type" value: "application/json" } + ] + body_content: "{\"error_code\":\"UNAUTHORIZED\",\"message\":\"You are not authorized to perform this operation.\"}" + } + ] + } +} + +tests: { + name: "whenNotMlflow_returnsFalse" + expect_vulnerability: false + + mock_http_server: { + mock_responses: [ + { + uri: "TSUNAMI_MAGIC_ANY_URI" + status: 200 + body_content: "Hello world" + } + ] + } +} \ No newline at end of file