mirror of https://github.com/xBytez/duo-cli
Cleanup and built a barebones command line interface
This commit is contained in:
parent
a29ae1f57d
commit
5a410e565f
23
README.md
23
README.md
|
@ -1,6 +1,9 @@
|
||||||
## Duo One Time Password Generator
|
## 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 write bots for MIT Services that require auth.
|
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
|
### Usage
|
||||||
|
|
||||||
|
@ -10,18 +13,20 @@ Install stuff,
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
Just grab the QR Code URL that starts with `duo://` and execute,
|
Just grab the QR Code URL and copy the string after value
|
||||||
|
|
||||||
|
https://api-XXX.duosecurity.com/frame/qr?value={VALUE}
|
||||||
|
|
||||||
```
|
```
|
||||||
python duo_bypass.py duo://urlhere
|
./duo_activate.py {VALUE}
|
||||||
```
|
```
|
||||||
|
|
||||||
### How does this work?
|
If everything worked you can then generate a code by running:
|
||||||
|
|
||||||
It's pretty simple so I won't explain. The hard part was to read DUO's obfuscated code, because obfuscation makes things so secure.
|
```
|
||||||
|
./duo_gen.py
|
||||||
|
```
|
||||||
|
|
||||||
Why didn't I sniff? Because HTTPS and because they apparantly ignore trusted CA's on the Android Device and also the fact that I was too lazy to get a USB cable from my room and also that I didn't want to download a gigabyte of emulator.
|
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.
|
||||||
|
|
||||||
When I almost got all of it I realized I could have probably decompiled their Windows app, coz .NET and and coz they didn't obfuscate that. rip me.
|
|
||||||
|
|
||||||
Anyway, it's 9 AM and I should sleep.
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
|
||||||
import pyotp
|
import pyotp
|
||||||
import requests
|
import requests
|
||||||
import base64
|
import base64
|
||||||
|
@ -9,21 +11,37 @@ if len(sys.argv) < 2:
|
||||||
print "Usage: python duo_bypass.py <url to duo qr>"; exit()
|
print "Usage: python duo_bypass.py <url to duo qr>"; exit()
|
||||||
|
|
||||||
qr_url = sys.argv[1]
|
qr_url = sys.argv[1]
|
||||||
data = unquote(qr_url.split('=')[1])
|
data = qr_url #unquote(qr_url.split('=')[1])
|
||||||
|
|
||||||
hostb64 = data.split('-')[1]
|
hostb64 = data.split('-')[1]
|
||||||
|
|
||||||
|
print "hostb64", hostb64
|
||||||
|
|
||||||
host = base64.b64decode(hostb64 + '='*(-len(hostb64) % 4))
|
host = base64.b64decode(hostb64 + '='*(-len(hostb64) % 4))
|
||||||
code = data.split('-')[0].replace('duo://', '')
|
code = data.split('-')[0]
|
||||||
|
|
||||||
|
print "host", host
|
||||||
|
print "code", code
|
||||||
|
|
||||||
url = 'https://{host}/push/v2/activation/{code}'.format(host=host, code=code)
|
url = 'https://{host}/push/v2/activation/{code}'.format(host=host, code=code)
|
||||||
r = requests.post(url)
|
r = requests.post(url)
|
||||||
response = json.loads(r.text)
|
response = json.loads(r.text)
|
||||||
|
|
||||||
|
print "url", url
|
||||||
|
print "r", r
|
||||||
|
print "response", response
|
||||||
secret = base64.b32encode(response['response']['hotp_secret'])
|
secret = base64.b32encode(response['response']['hotp_secret'])
|
||||||
|
|
||||||
|
print "secret", secret
|
||||||
|
|
||||||
print "10 Next OneTime Passwords!"
|
print "10 Next OneTime Passwords!"
|
||||||
# Generate 10 Otps!
|
# Generate 10 Otps!
|
||||||
hotp = pyotp.HOTP(secret)
|
hotp = pyotp.HOTP(secret)
|
||||||
for _ in xrange(10):
|
for _ in xrange(10):
|
||||||
print hotp.at(_)
|
print hotp.at(_)
|
||||||
|
|
||||||
|
f = open('duotoken.hotp', 'w')
|
||||||
|
f.write(secret + "\n")
|
||||||
|
f.write("0")
|
||||||
|
f.close()
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
|
||||||
|
import pyotp
|
||||||
|
import requests
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from urllib2 import unquote
|
||||||
|
|
||||||
|
f = open("duotoken.hotp","r+");
|
||||||
|
secret = f.readline()[0:-1]
|
||||||
|
offset = f.tell()
|
||||||
|
count = int(f.readline())
|
||||||
|
|
||||||
|
print "secret", secret
|
||||||
|
print "count", count
|
||||||
|
|
||||||
|
hotp = pyotp.HOTP(secret)
|
||||||
|
print "Code:", hotp.at(count)
|
||||||
|
|
||||||
|
f.seek(offset)
|
||||||
|
f.write(str(count + 1))
|
||||||
|
f.close()
|
||||||
|
|
Loading…
Reference in New Issue