From ce298165dd9a3ae499985bbb91c2c571e5b6b632 Mon Sep 17 00:00:00 2001 From: Felipe Zimmerle Date: Wed, 5 Aug 2015 16:47:11 -0300 Subject: [PATCH] Adds support to the cssDecode transformation --- src/actions/transformations/css_decode.cc | 18 +-- src/actions/transformations/css_decode.h | 5 +- src/actions/transformations/transformation.cc | 2 +- src/utils.cc | 152 ++++++++++++++++++ src/utils.h | 2 + 5 files changed, 168 insertions(+), 11 deletions(-) diff --git a/src/actions/transformations/css_decode.cc b/src/actions/transformations/css_decode.cc index 20189e81..ca9c978f 100644 --- a/src/actions/transformations/css_decode.cc +++ b/src/actions/transformations/css_decode.cc @@ -15,6 +15,8 @@ #include "actions/transformations/css_decode.h" +#include + #include #include #include @@ -24,26 +26,24 @@ #include "modsecurity/assay.h" #include "actions/transformations/transformation.h" +#include "src/utils.h" namespace ModSecurity { namespace actions { namespace transformations { -CssDecode::CssDecode(std::string action) - : Transformation(action) { - this->action_kind = 1; -} std::string CssDecode::evaluate(std::string value, Assay *assay) { - /** - * @todo Implement the transformation CssDecode - */ - assay->debug(4, "Transformation CssDecode is not implemented yet."); - return value; + char *tmp = strdup(value.c_str()); + int res = css_decode_inplace((unsigned char *)tmp, value.size()); + std::string ret(tmp, 0, value.size()); + free(tmp); + return ret; } + } // namespace transformations } // namespace actions } // namespace ModSecurity diff --git a/src/actions/transformations/css_decode.h b/src/actions/transformations/css_decode.h index 3e3a30ed..7f7280be 100644 --- a/src/actions/transformations/css_decode.h +++ b/src/actions/transformations/css_decode.h @@ -28,13 +28,16 @@ class Assay; namespace actions { namespace transformations { + class CssDecode : public Transformation { public: - explicit CssDecode(std::string action); + explicit CssDecode(std::string action) + : Transformation(action) { } std::string evaluate(std::string exp, Assay *assay) override; }; + } // namespace transformations } // namespace actions } // namespace ModSecurity diff --git a/src/actions/transformations/transformation.cc b/src/actions/transformations/transformation.cc index a958df53..60302d26 100644 --- a/src/actions/transformations/transformation.cc +++ b/src/actions/transformations/transformation.cc @@ -77,7 +77,7 @@ Transformation* Transformation::instantiate(std::string a) { IF_MATCH(base64_decode) { return new Base64Decode(a); } IF_MATCH(cmd_line) { return new CmdLine(a); } IF_MATCH(compress_whitespace) { return new CompressWhitespace(a); } - IF_MATCH(css_decode) { return new CssDecode(a); } + IF_MATCH(cssDecode) { return new CssDecode(a); } IF_MATCH(escape_seq_decode) { return new EscapeSeqDecode(a); } IF_MATCH(hex_decode) { return new HexDecode(a); } IF_MATCH(hex_encode) { return new HexEncode(a); } diff --git a/src/utils.cc b/src/utils.cc index 2f2abfd4..d30dad9f 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -328,6 +328,158 @@ int js_decode_nonstrict_inplace(unsigned char *input, int64_t input_len) { } +/** + * Decode a string that contains CSS-escaped characters. + * + * References: + * http://www.w3.org/TR/REC-CSS2/syndata.html#q4 + * http://www.unicode.org/roadmaps/ + */ +int css_decode_inplace(unsigned char *input, int64_t input_len) { + unsigned char *d = (unsigned char *)input; + int64_t i, j, count; + + if (input == NULL) { + return -1; + } + + i = count = 0; + while (i < input_len) { + /* Is the character a backslash? */ + if (input[i] == '\\') { + /* Is there at least one more byte? */ + if (i + 1 < input_len) { + i++; /* We are not going to need the backslash. */ + + /* Check for 1-6 hex characters following the backslash */ + j = 0; + while ((j < 6) + && (i + j < input_len) + && (VALID_HEX(input[i + j]))) { + j++; + } + + if (j > 0) { + /* We have at least one valid hexadecimal character. */ + int fullcheck = 0; + + /* For now just use the last two bytes. */ + switch (j) { + /* Number of hex characters */ + case 1: + *d++ = xsingle2c(&input[i]); + break; + + case 2: + case 3: + /* Use the last two from the end. */ + *d++ = x2c(&input[i + j - 2]); + break; + + case 4: + /* Use the last two from the end, but request + * a full width check. + */ + *d = x2c(&input[i + j - 2]); + fullcheck = 1; + break; + + case 5: + /* Use the last two from the end, but request + * a full width check if the number is greater + * or equal to 0xFFFF. + */ + *d = x2c(&input[i + j - 2]); + /* Do full check if first byte is 0 */ + if (input[i] == '0') { + fullcheck = 1; + } else { + d++; + } + break; + + case 6: + /* Use the last two from the end, but request + * a full width check if the number is greater + * or equal to 0xFFFF. + */ + *d = x2c(&input[i + j - 2]); + + /* Do full check if first/second bytes are 0 */ + if ((input[i] == '0') + && (input[i + 1] == '0')) { + fullcheck = 1; + } else { + d++; + } + break; + } + + /* Full width ASCII (0xff01 - 0xff5e) needs 0x20 added */ + if (fullcheck) { + if ((*d > 0x00) && (*d < 0x5f) + && ((input[i + j - 3] == 'f') || + (input[i + j - 3] == 'F')) + && ((input[i + j - 4] == 'f') || + (input[i + j - 4] == 'F'))) { + (*d) += 0x20; + } + + d++; + } + + /* We must ignore a single whitespace after a hex escape */ + if ((i + j < input_len) && isspace(input[i + j])) { + j++; + } + + /* Move over. */ + count++; + i += j; + } else if (input[i] == '\n') { + /* No hexadecimal digits after backslash */ + /* A newline character following backslash is ignored. */ + i++; + } else { + /* The character after backslash is not a hexadecimal digit, + * nor a newline. */ + /* Use one character after backslash as is. */ + *d++ = input[i++]; + count++; + } + } else { + /* No characters after backslash. */ + /* Do not include backslash in output + *(continuation to nothing) */ + i++; + } + } else { + /* Character is not a backslash. */ + /* Copy one normal character to output. */ + *d++ = input[i++]; + count++; + } + } + + /* Terminate output string. */ + *d = '\0'; + + return count; +} + + +/** + * Converts a single hexadecimal digit into a decimal value. + */ +static unsigned char xsingle2c(unsigned char *what) { + register unsigned char digit; + + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + + return digit; +} + + static unsigned char x2c(unsigned char *what) { register unsigned char digit; diff --git a/src/utils.h b/src/utils.h index d8335ebb..3499c4bf 100644 --- a/src/utils.h +++ b/src/utils.h @@ -35,6 +35,8 @@ namespace ModSecurity { double cpu_seconds(void); int js_decode_nonstrict_inplace(unsigned char *input, int64_t input_len); static unsigned char x2c(unsigned char *what); + int css_decode_inplace(unsigned char *input, int64_t input_len); + static unsigned char xsingle2c(unsigned char *what); } // namespace ModSecurity #define SRC_UTILS_H_