TOTP brute-force search incomplete?
#1
I'm using Windows 10 Pro (1909, build 18363.900), and hashcat 5.1.0.
I tried following the "tutorial" for doing TOTP (HMAC-SHA1) that's at https://www.unix-ninja.com/p/attacking_g...henticator, but couldn't reproduce the results - I didn't get any duplicate secret to isolate the correct match. I decided to try a shorter (5 character), simplified example that I could cross-check easily. Using Python and the pyotp module:

Code:
>>> import pytop
>>> import base64
>>> base64.b32encode(b'ahash')
b'MFUGC43I'
>>> totp = pyotp.TOTP(b'MFUGC43I')
>>> totp.at(1000000000)
'671202'
>>> totp.at(1000001000)
'455543'

So let's make a totp.hash file that contains:

Code:
671202:1000000000
455543:1000001000

Run the command (note the mask, and I don't think mode 18100 requires the --keep-guessing, but I included it anyway):

Code:
>hashcat64.exe --potfile-path=totp.potfile -a 3 -m 18100 --keep-guessing totp.hash ?l?l?l?l?l
hashcat (v5.1.0) starting...

* Device #1: WARNING! Kernel exec timeout is not disabled.
            This may cause "CL_OUT_OF_RESOURCES" or related errors.
            To disable the timeout, see: https://hashcat.net/q/timeoutpatch
* Device #2: Intel's OpenCL runtime (GPU only) is currently broken.
            We are waiting for updated OpenCL drivers from Intel.
            You can use --force to override, but do not report related errors.
OpenCL Platform #1: NVIDIA Corporation
======================================
* Device #1: Quadro P2200, 1280/5120 MB allocatable, 10MCU

OpenCL Platform #2: Intel(R) Corporation
========================================
* Device #2: Intel(R) UHD Graphics 630, skipped.
* Device #3: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz, skipped.

Hashes: 2 digests; 2 unique digests, 2 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates

Applicable optimizers:
* Zero-Byte
* Not-Iterated
* Brute-Force

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Watchdog: Temperature abort trigger set to 90c

671202:1000000000:O5RWS6DB
455543:1000001000:PBYWM2LO
671202:1000000000:OBWW4ZDH
455543:1000001000:NZTG46LE
Approaching final keyspace - workload adjusted.

671202:1000000000:NJ5GM5LK
455543:1000001000:O5SHA4DY
Session..........: hashcat
Status...........: Exhausted
Hash.Type........: TOTP (HMAC-SHA1)
Hash.Target......: .\totp.hash
Time.Started.....: Fri Jun 19 10:28:21 2020 (0 secs)
Time.Estimated...: Fri Jun 19 10:28:21 2020 (0 secs)
Guess.Mask.......: ?l?l?l?l?l [5]
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:  308.8 MH/s (7.66ms) @ Accel:64 Loops:26 Thr:256 Vec:1
Recovered........: 0/2 (0.00%) Digests, 0/2 (0.00%) Salts
Progress.........: 23762752/23762752 (100.00%)
Rejected.........: 0/23762752 (0.00%)
Restore.Point....: 456976/456976 (100.00%)
Restore.Sub.#1...: Salt:1 Amplifier:0-26 Iteration:0-26
Candidates.#1....: sefjy -> xqxvq
Hardware.Mon.#1..: Temp: 38c Fan: 47% Util: 85% Core:1455MHz Mem:5005MHz Bus:16

This console output matches the totp.potfile - 6 matches (three for each timestamp). Also, the Progress number appears correct: 26^5 (for length 5 lowercase) * 2 hashes to test = 23762752, leading me to believe this is correctly done. Unfortunately, this is very incomplete!!

Using a some more Python:

Code:
>>> import itertools
>>> import string
>>> def exhaustive_search(charset, length, code, timestamp):
        matches = list() # keep a list of matches
        num_searched = 0 # count how many we've checked
        for candidate in itertools.product(charset, repeat=length):
                cadidate_bytes = ''.join(candidate).encode()
                cadidate_b32 = base64.b32encode(cadidate_bytes)
                if pyotp.TOTP(cadidate_b32).at(timestamp) == code:
                        matches.append((cadidate_bytes, cadidate_b32))
                num_searched += 1
        return (num_searched, matches)

>>> exhaustive_search(string.ascii_lowercase, 5, '671202', 1000000000) # took ~ 3 minutes
(11881376, [(b'abmrc', b'MFRG24TD'), (b'ahash', b'MFUGC43I'), (b'cygfx', b'MN4WOZTY'), (b'hcvyo', b'NBRXM6LP'), (b'jwjwq', b'NJ3WU53R'), (b'jzfuj', b'NJ5GM5LK'), (b'mdvbx', b'NVSHMYTY'), (b'nkqzv', b'NZVXC6TW'), (b'pmndg', b'OBWW4ZDH'), (b'qbbim', b'OFRGE2LN'), (b'tcmia', b'ORRW22LB'), (b'wcixa', b'O5RWS6DB')])
>>> exhaustive_search(string.ascii_lowercase, 5, '455543', 1000001000) # also took ~ 3 minutes
(11881376, [(b'ahash', b'MFUGC43I'), (b'awiai', b'MF3WSYLJ'), (b'bdlly', b'MJSGY3DZ'), (b'bunvx', b'MJ2W45TY'), (b'cfraz', b'MNTHEYL2'), (b'ctwlb', b'MN2HO3DC'), (b'dqakb', b'MRYWC23C'), (b'dxtah', b'MR4HIYLI'), (b'fvvoe', b'MZ3HM33F'), (b'gcckn', b'M5RWG23O'), (b'gtpjs', b'M52HA2TT'), (b'jnpfj', b'NJXHAZTK'), (b'kzmhd', b'NN5G22DE'), (b'nfnyd', b'NZTG46LE'), (b'nrkfd', b'NZZGWZTE'), (b'odant', b'N5SGC3TU'), (b'vlivu', b'OZWGS5TV'), (b'wdppx', b'O5SHA4DY'), (b'wmdlu', b'O5WWI3DV'), (b'xqfin', b'PBYWM2LO'), (b'xuazs', b'PB2WC6TT')])

So my Python code found 12 and 21 matches (over the same 23762752 tests), not just 3 and 3. Also, the expected duplicate for 'ahash' can be identified. ASIDE: Yes, I know my Python is sub-optimal, I just wanted to bang out a quick-ish cross-check.
So what gives? Did I miss something in my command-line? Can someone reproduce my issue? The totp.hash file and command-line are right above and takes '0 seconds' (there's more overhead than actual runtime for this) to run.
Reply
#2
I think it has to do that a single kernel call can't crack a hash multiple times. There are just way too many collisions for a fast cracker like hashcat.

I think a similar problem/limitation/restriction is explained in docs/limits.txt
Reply
#3
I was able to add -T 1 (and, as required by -T, --force) to set the kernel thread count to 1. This technically slows things down, but still runs in 1 second.
Code:
hashcat64.exe --potfile-path=totp.potfile -a 3 -m 18100 --keep-guessing -T 1 --force totp.hash ?l?l?l?l?l
hashcat (v5.1.0) starting...

OpenCL Platform #1: NVIDIA Corporation
======================================
* Device #1: Quadro P2200, 1280/5120 MB allocatable, 10MCU

OpenCL Platform #2: Intel(R) Corporation
========================================
* Device #2: Intel(R) UHD Graphics 630, 4095/13039 MB allocatable, 24MCU
* Device #3: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz, skipped.

Hashes: 2 digests; 2 unique digests, 2 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates

Applicable optimizers:
* Zero-Byte
* Not-Iterated
* Brute-Force

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Watchdog: Temperature abort trigger set to 90c

671202:1000000000:O5RWS6DB
455543:1000001000:PBYWM2LO
671202:1000000000:ORRW22LB
671202:1000000000:MFUGC43I
455543:1000001000:MFUGC43I
455543:1000001000:MJSGY3DZ
671202:1000000000:NBRXM6LP
455543:1000001000:OZWGS5TV
455543:1000001000:O5WWI3DV
455543:1000001000:N5SGC3TU
455543:1000001000:M5RWG23O
455543:1000001000:MR4HIYLI
455543:1000001000:NZTG46LE
671202:1000000000:OBWW4ZDH
455543:1000001000:MF3WSYLJ
455543:1000001000:NJXHAZTK
455543:1000001000:M52HA2TT
671202:1000000000:MFRG24TD
455543:1000001000:PB2WC6TT
455543:1000001000:MZ3HM33F
455543:1000001000:MRYWC23C
455543:1000001000:MNTHEYL2
455543:1000001000:NN5G22DE
671202:1000000000:NJ5GM5LK
671202:1000000000:NJ3WU53R
455543:1000001000:O5SHA4DY
671202:1000000000:NVSHMYTY
671202:1000000000:MN4WOZTY
455543:1000001000:MJ2W45TY
Approaching final keyspace - workload adjusted.

671202:1000000000:NZVXC6TW
Session..........: hashcat
Status...........: Exhausted
Hash.Type........: TOTP (HMAC-SHA1)
Hash.Target......: .\totp.hash
Time.Started.....: Fri Jun 19 15:25:36 2020 (1 sec)
Time.Estimated...: Fri Jun 19 15:25:37 2020 (0 secs)
Guess.Mask.......: ?l?l?l?l?l [5]
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 15497.5 kH/s (7.05ms) @ Accel:512 Loops:26 Thr:1 Vec:1
Speed.#2.........:  2728.6 kH/s (6.93ms) @ Accel:32 Loops:26 Thr:1 Vec:1
Speed.#*.........: 18226.2 kH/s
Recovered........: 0/2 (0.00%) Digests, 0/2 (0.00%) Salts
Progress.........: 23762752/23762752 (100.00%)
Rejected.........: 0/23762752 (0.00%)
Restore.Point....: 453938/456976 (99.34%)
Restore.Sub.#1...: Salt:1 Amplifier:0-26 Iteration:0-26
Restore.Sub.#2...: Salt:1 Amplifier:0-26 Iteration:0-26
Candidates.#1....: suxjq -> xqxvq
Candidates.#2....: sdmfq -> xipfq
Hardware.Mon.#1..: Temp: 44c Fan: 51% Util: 94% Core:1746MHz Mem:5005MHz Bus:16
Hardware.Mon.#2..: N/A

Strangely, it still skipped 3 (it gave me 30, I expected 33): OFRGE2LN, NZZGWZTE, & MN2HO3DC. Any ideas if other switches might help? I understand it will be slower, but obviously much faster than my Python example.
Reply
#4
Code:
-n 1
?
Reply
#5
I swapped in '-n 1' for '-T 1' and it ran basically the same and got the same incomplete (only 30 of 33) list.
I tried both together i.e. '-n 1 -T 1' and it was quite slow, relatively speaking (somewhere between 100 and 1000 times slower), so it took 20 seconds, but it caught all 33.

I retried the example from the tutorial to get a better feel for the timing difference
Code:
Switches  |  Hits | Time
----------+-------+-----
<neither> |  3274 |  33s
-n 1      | 11966 |  65s
-T 1      | 14945 | 747s
-T 1 -n 1 | 16058 | 2h34m
The 14945 and 11966 were complete enough to find the duplicate, but the 3274 wasn't. The (probably) exhaustive one is just too slow to be useful.
So -n 1 is the best choice for this setup, but different workloads may behave differently.
Reply