diff --git a/.gitignore b/.gitignore index 7e99e36..9a23e3f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -*.pyc \ No newline at end of file +*.pyc +duotoken.hotp +response.json diff --git a/README.md b/README.md index 4c629d9..d630831 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ ## Duo One Time Password Generator -This is a little script I put together after I reverse engineered the Duo 2FA -Mobile App and figured out how their auth flow works. This can be ported into -probably a useful desktop app or chrome extention and can probably be used to +This is a little script I put together after I reverse engineered the Duo 2FA +Mobile App and figured out how their auth flow works. This can be ported into +probably a useful desktop app or chrome extention and can probably be used to write bots for MIT Services that require auth. ### Usage @@ -27,6 +27,12 @@ If everything worked you can then generate a code by running: ./duo_gen.py ``` -Warning: These are HOTP tokens and generate codes increments a counter. If you +Warning: These are HOTP tokens and generate codes increments a counter. If you get too far out of sync with the server it will stop accepting your codes. +``` +./duo_export.py +``` + +Export the duo hotp secret as a QR code for inclusion in third-party hotp apps +like freeotp. diff --git a/duo_activate.py b/duo_activate.py index 30a3204..6d2ed44 100755 --- a/duo_activate.py +++ b/duo_activate.py @@ -1,47 +1,59 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 import pyotp import requests import base64 import json import sys -from urllib2 import unquote if len(sys.argv) < 2: - print "Usage: python duo_bypass.py "; exit() + print("Usage: python duo_bypass.py ") + sys.exit() qr_url = sys.argv[1] -data = qr_url #unquote(qr_url.split('=')[1]) -hostb64 = data.split('-')[1] +host = 'api-%s' % (qr_url.split('/')[2].split('-')[1],) +code = qr_url.rsplit('/',1)[1] -print "hostb64", hostb64 +url = 'https://{host}/push/v2/activation/{code}?customer_protocol=1'.format(host=host, code=code) +headers = {'User-Agent': 'okhttp/2.7.5'} +data = {'jailbroken': 'false', + 'architecture': 'armv7', + 'region': 'US', + 'app_id': 'com.duosecurity.duomobile', + 'full_disk_encryption': 'true', + 'passcode_status': 'true', + 'platform': 'Android', + 'app_version': '3.23.0', + 'app_build_number': '323001', + 'version': '8.1', + 'manufacturer': 'unknown', + 'language': 'en', + 'model': 'Pixel C', + 'security_patch_level': '2018-12-01'} -host = base64.b64decode(hostb64 + '='*(-len(hostb64) % 4)) -code = data.split('-')[0] - -print "host", host -print "code", code - -url = 'https://{host}/push/v2/activation/{code}'.format(host=host, code=code) -r = requests.post(url) +r = requests.post(url, headers=headers, data=data) response = json.loads(r.text) -print "url", url -print "r", r -print "response", response -secret = base64.b32encode(response['response']['hotp_secret']) +try: + secret = base64.b32encode(response['response']['hotp_secret']) +except KeyError: + print(response) + sys.exit(1) -print "secret", secret +print("secret", secret) -print "10 Next OneTime Passwords!" +print("10 Next OneTime Passwords!") # Generate 10 Otps! hotp = pyotp.HOTP(secret) for _ in xrange(10): - print hotp.at(_) + print(hotp.at(_)) f = open('duotoken.hotp', 'w') f.write(secret + "\n") f.write("0") f.close() +with open('response.json', 'w') as resp: + resp.write(r.text) + diff --git a/duo_export.py b/duo_export.py new file mode 100755 index 0000000..d69683d --- /dev/null +++ b/duo_export.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +import pyotp +import pyqrcode +import json +import base64 +import sys + +file_json = "response.json" + +with open('response.json', "r") as f: + response = json.loads(f.read())['response'] + +with open('duotoken.hotp', "r") as f: + counter = int(f.readlines()[1]) + +label = response['customer_name'] +issuer = 'Duo' +# base32 encoded hotp secret, with the padding ("=") stripped. +secret = base64.b32encode(bytes(response['hotp_secret'], 'utf-8')).decode('utf-8').replace('=', '') +qrdata = 'otpauth://hotp/{label}?secret={secret}&issuer={issuer}&counter={counter}'.format(label=label, secret=secret, issuer=issuer, counter=counter) +qrcode = pyqrcode.create(qrdata) +print(qrcode.terminal(quiet_zone=1)) +print(qrdata) diff --git a/duo_gen.py b/duo_gen.py index c001ccd..da92bc2 100755 --- a/duo_gen.py +++ b/duo_gen.py @@ -1,24 +1,24 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 import pyotp -import requests -import base64 -import json import sys -from urllib2 import unquote -f = open("duotoken.hotp","r+"); +if len(sys.argv) == 2: + file = sys.argv[1] +else: + file = "duotoken.hotp" + +f = open(file, "r+"); secret = f.readline()[0:-1] offset = f.tell() count = int(f.readline()) -print "secret", secret -print "count", count +print("secret", secret) +print("count", count) hotp = pyotp.HOTP(secret) -print "Code:", hotp.at(count) +print("Code:", hotp.at(count)) f.seek(offset) f.write(str(count + 1)) f.close() -