-1

Long story short, wanted to transfer one of the App OTP to do some automation.

Problem: No Secret to get the OTP code

By using onetimepass, understood how OTP is generated. (https://pypi.org/project/onetimepass/). I have to pass my secret and a time interval to get OTP.

So, first I signed up with Google 2FA. Get one valid TOTP with the secret. Generate a list of OTP while keeping track of time interval as key, generated OTP as values. Since I have the actual secret, wanted to write a script to see how long I will eventually get the actual secret.

tested_otp = {  
   55199987: 562980, 
   55199989: 027715, 
   55199990: 268528,
   55199991: 108919,
   55199996: 851300
}

To generate random secret, I have two different methods. First method is to just generate a random secret every time.

# Method 1 - Just keep generating
def get_random_ secret():
    chars = string.ascii_lowercase + '234567' # Base32 Characters
    char_len = 32
    secret = ''.join(random.choice(chars) for i in range(char_len))
    return secret

Initially I used method 2, but realised, I will run out of memory first before I can get any result. Also found out the rate of "brute-forcing" is slower this way.

# Method 2 - Generating, while checking if the secret has been used before
used_secret = [] # Have a array to keep track of generated secrets
def get_random_secret_with_tracking():
    chars = "abcdefghijkmnpqrstuvwxyz234567"
    char_len = 32
    while True:
        secret = ''.join(random.choice(chars) for i in range(char_len))
        if secret not in used_secret:
            used_secret.append(secret)
            break
    return secret

Function from onepasskey to generate OTP from time_interval

def get_hotp(
        secret,
        intervals_no,
        as_string=False,
        casefold=True,
        digest_method=hashlib.sha1,
        token_length=6,
):
    if isinstance(secret, six.string_types):
        # It is unicode, convert it to bytes
        secret = secret.encode('utf-8')

    # Get rid of all the spacing:
    secret = secret.replace(b' ', b'') # Okay
    
    try:
        key = base64.b32decode(secret, casefold=casefold)
    except (TypeError):
        raise TypeError('Incorrect secret')
    msg = struct.pack('>Q', intervals_no)

    hmac_digest = hmac.new(key, msg, digest_method).digest()

    ob = hmac_digest[19] if six.PY3 else ord(hmac_digest[19])
    o = ob & 15
    token_base = struct.unpack('>I', hmac_digest[o:o + 4])[0] & 0x7fffffff
    token = token_base % (10 ** token_length)
    if as_string:
        # TODO: should as_string=True return unicode, not bytes?
        return six.b('{{:0{}d}}'.format(token_length).format(token))
    else:
        return token

Then the main function

while True:
    valid_otp_matched = 0
    random_secret = get_random_secret()
    for time_interval, valid_otp in tested_otp.items():
        to_try_otp = get_hotp(random_secret, interval)
        if to_try_otp != valid_otp:
            break # skip current random secret
        else:
            # For every random secret, that matched with OTP + 1
            valid_otp_matched += 1
            # If random secret generated OTP matched with all my Valid OTP
            if valid_otp_matched == len(tested_otp):
                # Save to file
                with open('Found Key.txt', 'w') as f:
                    f.write(random_secret)

Have written the script in multiprocessing, so that it can use up all the processor, hopefully it will be faster.

Also wanted to try to do it with permutation, but failed badly. Too many permutation to generate. Get the list, of permutation, then pass it to a multiprocessing queue.

b32_chars = string.ascii_lowercase + '234567'
chars = list(b32_chars)
permutations = list(itertools.permutations(chars))

Understand that it might not be possible. Just wanted curious to see if there is any more efficient method to do this, more for educational purposes.

Cytan
  • 53
  • 6
  • 4
    I didn't read the details of your question, but if you plan to break solid cryptography only with brute force, using python on a personal computer, expect to fail. – mozway Jun 23 '22 at 19:22
  • Yup, last statement did state that, I know that it is unlikely to be possible. Just wanna see where efficiency can be improved. Can be applied to other use case. – Cytan Jun 23 '22 at 19:56

1 Answers1

0

Upon further reading up, found out that using hashcat will be better for this. For anyone that is interested, for shorter key it is possible to do it with hashcat. Idea is the same, where you need min. 2 OTP that was generated by you. But in the below article, you will get the secret from a encoded base 32 secret. https://www.unix-ninja.com/p/attacking_google_authenticator

Understood the concept, but can't work for Google Auth at least. The key space is too large, seems like Google has increase the length of the secret, thus it will have too many possibilities as of 2022, it's 32^16. My little script up there won't even cause a dent.

Cytan
  • 53
  • 6