From b4ba9be65668ca9be6d3c8151a6323e10eab15a4 Mon Sep 17 00:00:00 2001 From: elsapet Date: Wed, 31 Jan 2024 10:57:29 +0200 Subject: [PATCH] feat(java): HTTP Param Pollution --- rules/java/lang/http_parameter_pollution.yml | 86 +++++++++++++++++++ .../lang/http_parameter_pollution/test.js | 18 ++++ .../testdata/main.java | 40 +++++++++ 3 files changed, 144 insertions(+) create mode 100644 rules/java/lang/http_parameter_pollution.yml create mode 100644 tests/java/lang/http_parameter_pollution/test.js create mode 100644 tests/java/lang/http_parameter_pollution/testdata/main.java diff --git a/rules/java/lang/http_parameter_pollution.yml b/rules/java/lang/http_parameter_pollution.yml new file mode 100644 index 000000000..0ff17c858 --- /dev/null +++ b/rules/java/lang/http_parameter_pollution.yml @@ -0,0 +1,86 @@ +imports: + - java_shared_lang_instance + - java_shared_lang_servlet_request +patterns: + # Version 4.x and greater + - pattern: new $($); + filters: + - variable: HTTP_GET + regex: \A(org\.apache\.http\.client\.methods\.)?HttpGet\z + - variable: USER_INPUT + detection: java_lang_http_parameter_pollution_user_input + scope: result + # Legacy GetMethod + - pattern: new $($); + filters: + - variable: GET_METHOD + regex: \A(org\.apache\.commons\.httpclient\.methods\.)?GetMethod\z + - variable: USER_INPUT + detection: java_lang_http_parameter_pollution_user_input + scope: result + - pattern: $.setQueryString($); + filters: + - variable: GET_METHOD + detection: java_shared_lang_instance + scope: cursor + filters: + - variable: JAVA_SHARED_LANG_INSTANCE_TYPE + regex: \A(org\.apache\.commons\.httpclient\.methods\.)?GetMethod\z + - variable: USER_INPUT + detection: java_lang_http_parameter_pollution_user_input + scope: result +auxiliary: + - id: java_lang_http_parameter_pollution_user_input + sanitizer: java_lang_http_parameter_pollution_sanitizer + patterns: + - pattern: $.getParameter(); + filters: + - variable: JAVA_SHARED_LANG_USER_INPUT_REQUEST + detection: java_shared_lang_servlet_request + scope: cursor + - id: java_lang_http_parameter_pollution_sanitizer + patterns: + - pattern: $.encode($$<_>); + filters: + - variable: CLASS + regex: \A(java\.net\.)?URLEncoder\z + - pattern: $.escape($$<_>); + filters: + - variable: PATH_SEGMENT_ESCAPER + regex: \A((com\.google\.common\.net\.)?UrlEscapers\.)?urlPathSegmentEscaper\(\)\z +languages: + - java +metadata: + description: "Possible HTTP Parameter Pollution detected." + remediation_message: | + ## Description + + Unsanitized user input is being used to construct a URL. + This can lead to HTTP Parameter Pollution (HPP) attacks, where an attacker overrides the + value of a URL or request parameter, to manipulate requests or retrieve otherwise hidden information. + + ## Remediations + + ❌ Never use direct or unsanitized user input when constructing URLs or URL parameters. Seek alternatives instead. + + For example, use a map to convert user input to an appropriate parameter. + + ```java + HashMap lookupTable = new HashMap<>(); + // ... populate hash map + String rawUserInput = request.getParameter("someParam") + + String value = lookupTable.getOrDefault(rawUserInput, "someDefault") ; + HttpGet httpget = new HttpGet("https://example.com/?param="+value); + ``` + + ✅ Always sanitize user input + + ```java + String rawUserInput = request.getParameter("someParam"); + String encoded = java.net.URLEncoder.encode(rawUserInput, StandardCharsets.UTF_8); + ``` + cwe_id: + - 88 + id: java_lang_http_parameter_pollution + documentation_url: https://docs.bearer.com/reference/rules/java_lang_http_parameter_pollution diff --git a/tests/java/lang/http_parameter_pollution/test.js b/tests/java/lang/http_parameter_pollution/test.js new file mode 100644 index 000000000..5c157f62b --- /dev/null +++ b/tests/java/lang/http_parameter_pollution/test.js @@ -0,0 +1,18 @@ +const { + createNewInvoker, + getEnvironment, +} = require("../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createNewInvoker(ruleId, ruleFile, testBase) + + test("http_parameter_pollution", () => { + const testCase = "main.java" + + const results = invoke(testCase) + + expect(results.Missing).toEqual([]) + expect(results.Extra).toEqual([]) + }) +}) \ No newline at end of file diff --git a/tests/java/lang/http_parameter_pollution/testdata/main.java b/tests/java/lang/http_parameter_pollution/testdata/main.java new file mode 100644 index 000000000..0fdb647f3 --- /dev/null +++ b/tests/java/lang/http_parameter_pollution/testdata/main.java @@ -0,0 +1,40 @@ +import org.apache.http.client.methods.HttpGet; +// legacy +import org.apache.commons.httpclient.methods.GetMethod; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.net.URLEncoder; +import static com.google.common.net.UrlEscapers.urlPathSegmentEscaper; + +public class Foo extends HttpServlet { + public void bad(HttpServletRequest req, HttpServletResponse res) { + String externalParam = req.getParameter("foo"); + + // bearer:expected java_lang_http_parameter_pollution + HttpGet httpget2 = new HttpGet("http://example.com?param=" + externalParam); + } + + public void legacyBad(HttpServletRequest req, HttpServletResponse res) { + String externalParam = req.getParameter("bar"); + + // bearer:expected java_lang_http_parameter_pollution + GetMethod get = new GetMethod("http://example.com?param=" + externalParam); + // bearer:expected java_lang_http_parameter_pollution + get.setQueryString("param=" + externalParam); + } + + public void ok(HttpServletRequest req, HttpServletResponse response) { + String externalParam = req.getParameter("baz"); + String escapedParam = urlPathSegmentEscaper().escape(externalParam); + + HttpGet escaped = new HttpGet("http://example.com?param=" + escapedParam); + + GetMethod get = new GetMethod("http://example.com?param=" + URLEncoder.encode(externalParam)); + + String escapedParam2 = UrlEscapers.urlPathSegmentEscaper().escape(externalParam); + GetMethod get = new GetMethod("http://example.com?param=" + escapedParam2); + get.setQueryString("param=" + escapedParam2); + } +}