From cdfb4db54ff430f8818c814460bd857d65d2db66 Mon Sep 17 00:00:00 2001 From: Tudor TIMCU Date: Tue, 17 Dec 2024 16:16:01 +0000 Subject: [PATCH] PHP filters support for path traversal --- lib/php-extension/HandlePathAccess.cpp | 6 +++-- .../expect_detection_blocked.json | 6 ++--- .../expect_detection_blocked_php_filter.json | 27 +++++++++++++++++++ .../expect_detection_not_blocked.json | 6 ++--- tests/server/test_path_traversal/index.php | 5 ++-- tests/server/test_path_traversal/test.py | 13 +++++---- 6 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 tests/server/test_path_traversal/expect_detection_blocked_php_filter.json diff --git a/lib/php-extension/HandlePathAccess.cpp b/lib/php-extension/HandlePathAccess.cpp index aec8166f..79893d98 100644 --- a/lib/php-extension/HandlePathAccess.cpp +++ b/lib/php-extension/HandlePathAccess.cpp @@ -2,8 +2,10 @@ /* Helper for handle pre file path access */ void helper_handle_pre_file_path_access(char *filename, EVENT_ID &eventId) { - if (strncmp(filename, "php://", 6) == 0) { - // Whitelist php:// streams as they are often used by PHP frameworks a lot + if (strncmp(filename, "php://", 6) == 0 && + strncmp(filename, "php://filter", 12) != 0) { + // Whitelist all php:// streams apart from php://filter, for performance reasons (some PHP frameworks do 1000+ calls / request with these streams as param) + // php://filter can be used to open arbitrary files, so we still monitor this return; } diff --git a/tests/server/test_path_traversal/expect_detection_blocked.json b/tests/server/test_path_traversal/expect_detection_blocked.json index 7ceff617..d42f6c1e 100644 --- a/tests/server/test_path_traversal/expect_detection_blocked.json +++ b/tests/server/test_path_traversal/expect_detection_blocked.json @@ -7,7 +7,7 @@ ] }, "method": "POST", - "body": "{\"folder\": \"../../../..\"}", + "body": "{\"file\": \"../../../../file\"}", "route": "/testDetection" }, "attack": { @@ -15,8 +15,8 @@ "operation": "fopen", "blocked": true, "source": "body", - "path": ".folder", - "payload": "../../../..", + "path": ".file", + "payload": "../../../../file", "metadata": { "filename": "../../../../file" } diff --git a/tests/server/test_path_traversal/expect_detection_blocked_php_filter.json b/tests/server/test_path_traversal/expect_detection_blocked_php_filter.json new file mode 100644 index 00000000..46f6cd92 --- /dev/null +++ b/tests/server/test_path_traversal/expect_detection_blocked_php_filter.json @@ -0,0 +1,27 @@ +{ + "type": "detected_attack", + "request": { + "headers": { + "content_type": [ + "application/json" + ] + }, + "method": "POST", + "body": "{\"file\": \"php://filter/convert.base64-encode/resource=../../../../file\"}", + "route": "/testDetection" + }, + "attack": { + "kind": "path_traversal", + "operation": "fopen", + "blocked": true, + "source": "body", + "path": ".file", + "payload": "php://filter/convert.base64-encode/resource=../../../../file", + "metadata": { + "filename": "php://filter/convert.base64-encode/resource=../../../../file" + } + }, + "agent": { + "dryMode": false + } +} \ No newline at end of file diff --git a/tests/server/test_path_traversal/expect_detection_not_blocked.json b/tests/server/test_path_traversal/expect_detection_not_blocked.json index ebd7a5c1..7c95f411 100644 --- a/tests/server/test_path_traversal/expect_detection_not_blocked.json +++ b/tests/server/test_path_traversal/expect_detection_not_blocked.json @@ -7,7 +7,7 @@ ] }, "method": "POST", - "body": "{\"folder\": \"../../../..\"}", + "body": "{\"file\": \"../../../../file\"}", "route": "/testDetection" }, "attack": { @@ -15,8 +15,8 @@ "operation": "fopen", "blocked": false, "source": "body", - "path": ".folder", - "payload": "../../../..", + "path": ".file", + "payload": "../../../../file", "metadata": { "filename": "../../../../file" } diff --git a/tests/server/test_path_traversal/index.php b/tests/server/test_path_traversal/index.php index 5aa5f891..770fe198 100755 --- a/tests/server/test_path_traversal/index.php +++ b/tests/server/test_path_traversal/index.php @@ -9,9 +9,8 @@ $data = json_decode($requestBody, true); // Check if 'folder' exists and get its value -if (isset($data['folder'])) { - $f = $data['folder'] . '/file'; - fopen($f, 'r'); +if (isset($data['file'])) { + fopen($data['file'], 'r'); echo "File opened!"; } else { echo "Field 'folder' is not present in the JSON data."; diff --git a/tests/server/test_path_traversal/test.py b/tests/server/test_path_traversal/test.py index bcfc8549..cfc18b71 100755 --- a/tests/server/test_path_traversal/test.py +++ b/tests/server/test_path_traversal/test.py @@ -9,8 +9,8 @@ 3. Checks that the detection event was submitted and is valid. ''' -def check_path_traversal(response_code, response_body, event_id, expected_json): - response = php_server_post("/testDetection", {"folder": "../../../.."}) +def check_path_traversal(exploit_path, response_code, response_body, event_id, expected_json): + response = php_server_post("/testDetection", {"file": exploit_path}) assert_response_code_is(response, response_code) assert_response_body_contains(response, response_body) @@ -22,13 +22,16 @@ def check_path_traversal(response_code, response_body, event_id, expected_json): assert_event_contains_subset_file(events[event_id], expected_json) def run_test(): - check_path_traversal(500, "", 1, "expect_detection_blocked.json") + exploit_path = "../../../../file" + + check_path_traversal(exploit_path, 500, "", 1, "expect_detection_blocked.json") + check_path_traversal(f"php://filter/convert.base64-encode/resource={exploit_path}", 500, "", 2, "expect_detection_blocked_php_filter.json") apply_config("change_config_disable_blocking.json") - check_path_traversal(200, "File opened!", 2, "expect_detection_not_blocked.json") + check_path_traversal(exploit_path, 200, "File opened!", 3, "expect_detection_not_blocked.json") apply_config("start_config.json") - check_path_traversal(500, "", 3, "expect_detection_blocked.json") + check_path_traversal(exploit_path, 500, "", 4, "expect_detection_blocked.json") if __name__ == "__main__": load_test_args()