Brute force password cracker in Python
You should exploit the structure of the password, if it has any. Here you have a 20 character password separated into four blocks of five characters each, joined with a -. So don't go on generating all combinations of length 23, only to throw most of them away.

You also str.join the guess, then convert it to a list, then replace the values and str.join it again. You could have saved yourself the first str.join entirely by directly converting to list.

You know the length of the password, so no need to hardcode it. Just get it from the real password (or, in a more realistic cracker, pass the length as a parameter).

With these small changes your code would become:

def guess_password(real):
    chars = string.ascii_uppercase + string.digits
    password_format = "-".join(["{}"*5] * 4)
    password_length = len(real) - 3
    for guess in itertools.product(chars, repeat=password_length):
        guess = password_format.format(*guess)
        if guess == real:
            return guess


Here I used some string formatting to get the right format.

Note also that the timing and output string are not in there. Instead make the former a decorator and the latter part of the calling code, which should be protected by a if __name__ == "__main__": guard to allow you to import from this script without running the brute force cracker:

from time import perf_counter
from functools import wraps

def timeit(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = perf_counter()
        ret = func(*args, **kwargs)
        print(f"Time elapsed: {perf_counter() - start}")
        return ret
    return wrapper

@timeit
def guess_password(real):
    ...

if __name__ == "__main__":
    real_password = 'E45E7-BYXJM-7STEY-K5H7L'
    if guess_password(real_password):
        print(f"Scan completed: {real_password}")


On my machine this takes 9.96 s ± 250 ms, whereas your code takes 12.3 s ± 2.87 s for the input string "AAAAA-AAAAA-AAAAA-FORTN".

But in the end you will always be limited by the fact that there are a lot of twenty character strings consisting of upper case letters and digits. Namely, there are \$36^{20} = 13,367,494,538,843,734,067,838,845,976,576\$ different passwords that need to be checked (well, statistically you only need to check half of them, on average, until you find your real password, but you might get unlucky). Not even writing your loop in Assembler is this going to run in less than days.