From ebf2c154c2786c65d32e5dfb10047f3c55205066 Mon Sep 17 00:00:00 2001 From: Stig Palmquist Date: Thu, 21 Nov 2024 18:04:31 +0100 Subject: [PATCH] Fix problems with Mojolicious "paWlpaWl" module This commit fixes issues with the 16501 module for Mojolicious cookies. - Fix parsing of message: The base64 encoded message has '=' characters replaced with '-' meaning that three such characters can be encountered at the end of the message. This commit sets a fixed size for the message and doesnt use a `sep` to parse it. - Update self-test with real-world mojolicious cookie. - Update the test module to reflect what's actually happening when Mojolicious generates a signed cookie. --- src/modules/module_16501.c | 39 ++++++++++++++++++------------------ tools/test_modules/m16501.pm | 18 +++++------------ 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/modules/module_16501.c b/src/modules/module_16501.c index 06b8f7e37..36ddd6e4c 100644 --- a/src/modules/module_16501.c +++ b/src/modules/module_16501.c @@ -30,8 +30,8 @@ static const u64 OPTS_TYPE = OPTS_TYPE_STOCK_MODULE | OPTS_TYPE_PT_GENERATE_BE | OPTS_TYPE_SELF_TEST_DISABLE; static const u32 SALT_TYPE = SALT_TYPE_EMBEDDED; -static const char *ST_PASS = "secret"; -static const char *ST_HASH = "mojolicious=eyJleHBpcmVzIjoyMDAwMDAwMDAwLCAidXNlciI6ICJzdXBlcnVzZXIifVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlo--9d32db342a1caa83910dc8db51ff387591c40144bc0dddfa437d03a7d65688da"; +static const char *ST_PASS = "foobar"; +static const char *ST_HASH = "mojolicious=eyJiYXIiOiJhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmNhYmMiLCJleHBpcmVzIjoxNzMyMTYyOTMyLCJmb29vIjoxfVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlo---113312f6f43e8f53e4e5eddb3d1603613fcd5073d947935ff7e08b2f79d921f8"; u32 module_attack_exec (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return ATTACK_EXEC; } u32 module_dgst_pos0 (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_POS0; } @@ -97,7 +97,7 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE memset (&token, 0, sizeof (hc_token_t)); - token.token_cnt = 4; + token.token_cnt = 5; // total = max 4096 characters // name=value = max 4030 characters @@ -105,17 +105,14 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE // cookie name token.sep[0] = '='; token.len_min[0] = 1; - token.len_max[0] = 3005; // what's left of max cookie length + token.len_max[0] = 128; token.attr[0] = TOKEN_ATTR_VERIFY_LENGTH; - // '=' as separator - - // cookie value - token.sep[1] = '-'; - token.len_min[1] = 1024; // if there's no padding, it's not new cookie - token.len_max[1] = 4030; - token.attr[1] = TOKEN_ATTR_VERIFY_LENGTH - | TOKEN_ATTR_VERIFY_BASE64A; + // cookie value: uses a fixed length as the value is padded to 1025 + // bytes with 'Z' characters before being base64 encoded. Note that + // '=' is replaced with '-' in the base64 output. + token.len[1] = 1368; + token.attr[1] = TOKEN_ATTR_FIXED_LENGTH; // '--' as separator token.sep[2] = '-'; @@ -123,12 +120,16 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE token.len_max[2] = 0; token.attr[2] = TOKEN_ATTR_VERIFY_LENGTH; - - // signature = 64 characters + // '--' as separator token.sep[3] = '-'; - token.len_min[3] = 64; - token.len_max[3] = 64; - token.attr[3] = 0; + token.len_min[3] = 0; + token.len_max[3] = 0; + token.attr[3] = TOKEN_ATTR_VERIFY_LENGTH; + + // signature: hmac_sha256 hex encoded + token.len_min[4] = 64; + token.len_max[4] = 64; + token.attr[4] = TOKEN_ATTR_VERIFY_LENGTH | TOKEN_ATTR_VERIFY_HEX; const int rc_tokenizer = input_tokenizer ((const u8 *) line_buf, line_len, &token); @@ -136,7 +137,7 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE const int name_len = token.len[0]; const int payload_len = token.len[1]; - mojolicious->signature_len = token.len[3]; + mojolicious->signature_len = token.len[4]; // esalt @@ -183,7 +184,7 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE salt->salt_len = 16; - const u8 *hash_pos = token.buf[3]; + const u8 *hash_pos = token.buf[4]; u32 *digest = (u32 *) digest_buf; digest[0] = hex_to_u32 (hash_pos + 0); diff --git a/tools/test_modules/m16501.pm b/tools/test_modules/m16501.pm index ee68f4565..c0fd3e1ea 100644 --- a/tools/test_modules/m16501.pm +++ b/tools/test_modules/m16501.pm @@ -9,7 +9,7 @@ use strict; use warnings; use Digest::SHA qw (hmac_sha256_hex); -use MIME::Base64 qw (encode_base64url); +use MIME::Base64 qw (encode_base64); use JSON qw (encode_json); sub module_constraints { [[0, 64], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] } @@ -19,12 +19,9 @@ sub module_generate_hash my $word = shift; my $salt = shift || get_random_mojolicious_salt (); - ## mojolicious=eyJleHBpcmVzIjoxMTEyNDcwNjIwLCJuZXdfZmxhc2giOnsibWVzc2FnZSI6IkhlbGxvIHRoZXJlLiJ9LCJ1c2VyIjoiYWxpY2UifQWlpaWlpaWlpaWlp(...)aWlpaWlpaWlpaWlpaWlpaWlpaWlp my ($name, $value) = split('=', $salt); - ## mojolicious=eyJleHBpcmVzIjoxMTEyNDcwNjIwLCJuZXdfZmxhc2giOnsibWVzc2FnZSI6IkhlbGxvIHRoZXJlLiJ9LCJ1c2VyIjoiYWxpY2UifQWlpaWlpaWlpaWlp(...)aWlpaWlpaWlpaWlpaWlpaWlpaWlp--1bf346f55562ac2a08d1b86a28e87bf5aad357d7a92e816567271f5b420b93c1 my $hash = get_signed_cookie ($name, $value, $word); - return $hash; } @@ -43,7 +40,7 @@ sub module_verify_hash my ($padded_cookie, $signature) = @data; - my $unpadded_cookie = $padded_cookie =~ s/\}\KZ*$//r; + my $unpadded_cookie = $padded_cookie =~ s/Z*$//; my ($cookie_name, $cookie_value) = split('=', $unpadded_cookie); @@ -58,11 +55,6 @@ sub module_verify_hash sub get_random_mojolicious_salt { - sub add_mojolicious_padding - { - return $_[0] . 'Z' x (1025 - length $_[0]); - } - my $random_key = random_number (1, 100000000); my $random_val = random_number (1, 100000000); @@ -72,8 +64,9 @@ sub get_random_mojolicious_salt }; my $payload_json = encode_json ($payload); - my $payload_padded = add_mojolicious_padding ($payload_json); - my $payload_base64 = encode_base64url ($payload_padded, ""); + my $payload_padded = $payload_json . 'Z' x (1025 - length $payload_json); + my $payload_base64 = encode_base64 ($payload_padded, ""); + $payload_base64 =~ y/=/-/; return "mojolicious=$payload_base64"; } @@ -82,7 +75,6 @@ sub get_signed_cookie { my ($name, $value, $secret) = @_; my $sum = Digest::SHA::hmac_sha256_hex("$name=$value", $secret); - return "$name=$value--$sum" }