Two Factor Authentication from Dumped Sqlite Databases

Some of you may be using Google’s “Authenticator” app for Android in order to achieve a higher security level for your Google account.

(If you are not yet using it, I recommend setting it up, you can find the details here).

I used to have my web browser set up to always launch in incognito mode (not saving cookies, etc), and as such, Google regularily prompted me for the two-factor authentication token. I did not want to switch to my phone every time, so I decided to reimplement the Google Authenticator as Python script on Windows.

The general algorithm is well known and documented in RFC 6238. There is even a pseudo code implementation available on Wikipedia, so my contribution is the Python implementation featuring simplified usability by reading from an sqlite database.

In order to use the script, you will have to get the “secret” keys from your Android phone to your computer. This is easily possible if you already have root access to your Android phone. In this case, you can use the Android Debugging Bridge (adb) to pull the database from /data/data/com.google.android.apps.authenticator2/databases. An excellent tutorial on how to acquire the database can be found here.

In the end, for other reasons, I decided to switch out of Chrome’s incognito mode, so I don’t use the authentication script as often as I used to.
But I decided to share it anyways:

importargparseimporttimeimportbase64importhmacimporthashlib# pads the string to a multiple of 8 bytes using "="defpad_base32(string):returnstring+"="*(8-((len(string)-1)%8)-1)# calculates the authentification key for the specified secret and timestamp# more info: https://en.wikipedia.org/wiki/Google_Authenticatordefcalckey(secret,timestamp):# pad secret to multiple of 8 characterssecret=pad_base32(secret)# decode base32 to byte string (ignoring case)key=base64.b32decode(secret,casefold=True)# interpret Unix timestamp / 30sec as 8 byte unsigned long longmessage=int(timestamp/30).to_bytes(8,byteorder="big",signed=False)# combine message (time) and key (secret) into one authentification code using sha1code=hmac.new(key,message,hashlib.sha1).digest()# extract offset (last 4 bits of HMAC)offset=code[-1]&0x0f# extract 4 bytes starting at offset, interpret as 4 byte unsigned inttruncatedHash=int.from_bytes(code[offset:offset+4],byteorder="big",signed=False)# unset the first bittruncatedHash&=~(1<<(8*4-1))# restrict code to 6 digits and convert to stringcode=truncatedHash%1000000codeStr="%06d"%codereturncodeStr# to pull the database from Android, see https://nucleussystems.com/blog/android-backup-google-authenticator/# general steps:# 1. have root access# 2. use adb shell to pull /data/data/com.google.android.apps.authenticator2/databasesdeffrom_database(dbfile,enable_plot=False):importsqlite3now=int(time.time())remaining=30-(now%30)withsqlite3.connect(dbfile)asconnection:cursor=connection.cursor()cursor.execute("SELECT email, secret FROM accounts")# output code is a little ugly :/width=50print("="*width)print("Two-Step-Token generator (RFC 6238)".center(width))print("="*width)print("Loaded from databases file '{:s}'.".format(dbfile).center(width))print("Remaining time: {:d} sec\n".format(remaining).center(width))print("{:>25} {}".format("Account","Key"))print("-"*width)forname,secretincursor:key=calckey(secret,now)print("{:>25} {}".format(name,key))ifenable_plot:plot(name,secret)print("-"*width)# plots the QR-code using the matplotlib library:# the QR code contains a URI, but is encoded in the QR-code "text" mode# this can currently not be fixed, it works with the Google Authentificator App thoughdefplot(name,secret):importqrcodeimportmatplotlib.pyplotaspltqr=qrcode.QRCode(version=None,error_correction=qrcode.constants.ERROR_CORRECT_L,box_size=1,border=1,)url="otpauth://totp/{:s}?secret={:s}".format(name,secret)qr.add_data(url)qr.make(fit=True)img=qr.make_image()plt.clf()plt.imshow(img,cmap="Greys",interpolation="nearest")plt.axis('off')plt.title(name)plt.show()if__name__=="__main__":parser=argparse.ArgumentParser(description="Two-Step-Token generator (RFC 6238)")parser.add_argument("-p","--plot",action="store_true",help="plot the secrets via the qrcode module and Matplotlib (for transfering to a new device)")parser.add_argument("database",type=str,help="sqlite database (dumped from Android)")args=parser.parse_args()from_database(args.database,args.plot)