The function fread is declared the following way
size_t fread(void * restrict ptr, size_t size, 
             size_t nmemb, 
             FILE * restrict stream);
Its first parameter has the unqualified type void * (actually the parameter type is the qualified type void * restrict) . A pointer to object of any type may be assigned to a pointer of the type void *.
The expression &header has the pointer type uint8_t ( * )[HEADER_SIZE] and yields the initial address of the extent of memory occupied by the array,
The expression header used as an argument of a call of fread is implicitly converted to a pointer of the type uint8_t * and yields the same initial address of the extent of memory occupied by the array.
Thus these calls
fread(&header, sizeof(uint8_t), HEADER_SIZE, input);
fwrite(header, sizeof(uint8_t), HEADER_SIZE, output);
are equivalent in sense that the function fread gets the same values for its parameters.
Consider the following simple demonstration program.
#include <stdio.h>
int main( void )
{
    char s[6] = "Hello";
    printf( "The value of the expression  s is %p\n", ( void * )s );
    printf( "The value of the expression &s is %p\n", ( void * )&s );
}
The program output might look like
The value of the expression  s is 0x7fffa4577a2a
The value of the expression &s is 0x7fffa4577a2a
As you can see the both outputted values are equal each other though in the first call the used argument expression has the type char * while in the second case the used argument expression has the type char ( * )[6].
However if you will write for example
#include <stdio.h>
int main( void )
{
    char s[6] = "Hello";
    printf( "%s\n", s );
    printf( "%s\n", &s );
}
then the compiler can issue a message for the second call of printf that the function expects an argument of the type char * instead of the type char ( * )[6] though the both values of the expressions are equal each other.