Exporting Authy Tokens

First up, a note: exporting your Authy tokens presents a security risk, especially if you save them to a file. Anyone that is able to view the tokens can then create the codes needed to access the associated accounts. So… use your head and be safe. With that out of the way:

I’ve recently become very frustrated at the major second-factor authentication (2FA) apps, particularly around the lack of features needed to manage a large number of tokens.

In the security industry, the guideline has long been “use a second factor” mechanism — giving rise to mantras like “something you have and something you know”, which is really the point. However, currently, nearly nobody uses 2FA strictly in that manner: most folks view 2FA as “a second password” mechanism. One they choose, one is based on whatever (generally a seed that’s based on time — the first ‘T’ in TOTP). While that defeats some of the intent behind 2FA, in practice that’s actually probably just fine.

So: as an industry we push vendors and developers to require 2FA. Good news! Many of them are starting to. Herein comes the problem: users now have to manage a lot of tokens. In fact, they have to manage at least one for each app they use.

So what’s the big deal? NONE of the major apps used for 2FA (Authy, 1Password, Duo, Google Authenticator, LastPass, etc.) really provide a useful way to manage a large number of accounts:

  • Users can’t rename them based on what they want to call them (most of the apps use a combination of the Seed Provider name + username to refer to them. That may be OK with a small number of accounts, but wait until you have 15 “Google” accounts and need to figure out which one is which but you can only see the first 2 or 3 chars of what comes after “Google” on the screen…)
  • Tagging doesn’t exist in this world
  • Neither does sorting, for most of them
  • In some of them, you can’t delete accounts without having to wait at least 24 hours.
  • There’s no such thing as export

Now, some of those have good security reasons behind them (well… OK. Just one, really. The one that most applies to this post: lack of export). Why can’t you export from these apps? Simple: allowing export breaks the 2FA model of “something you have, something you know”. If you can get a dump of seeds, that becomes two things you know, since the device is no longer required. Except… for day to day use, nearly everyone is totally fine with 2FA just being 2 pieces of information that you know. Not to mention: what happens if a software company goes away? Do I lose access to my accounts because I can’t access the tokens anymore? Pretty much zero app developers allow someone that has already configured 2FA to display a scannable QR code (or even just get the seed) once the initial configuration is complete (which, again, is in harmony with good “something you have” mode, but contrary to “good user interface” requirements).

So… if you are one of those that wants to dump your tokens out of an existing app you are using — and if that app happens to be Authy — you are in luck! It turns out you can do this. It just isn’t super straight foward.

  1. Open the Authy application, such that you are viewing the accounts you have saved
  2. Open your browser extensions manager. In Chrome, this can be done by going to chrome://extensions
  3. In the extensions manager, click “Developer Mode” on the top right
  4. Find the Authy application (note: this is different than the Authy extension. It should be at the bottom of the page in the “Chrome Apps” section)
  5. You should see a bit that says “Inspect Views”. It may say “background page, 1 more”. Click the link, until you see “main.html”.
  6. Click “main.html”, and the Chrome developer tools window should open
  7. Open the Console (this is the Javascript Console)
  8. Paste the codeblock below into the console window
  9. Enjoy your list of accounts. (I recommend doing something like right clicking on the console and “save as” to export the data to a file — but please understand you do this at your own risk, as anyone that is able to access the file can then create a 2FA token for the accounts in the list).

Here’s the code to paste in step 8 (hat tip to gboudreau and nmurthy):

/* base32 / /
 Copyright (c) 2011, Chris Umbel
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */

var charTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
function quintetCount (buff) {
  var quintets = Math.floor(buff.length / 5)
  return buff.length % 5 === 0 ? quintets : quintets + 1
}

const encode = function (plain) {
  var i = 0
  var j = 0
  var shiftIndex = 0
  var digit = 0
  var encoded = new Array(quintetCount(plain) * 8)
  /* byte by byte isn't as pretty as quintet by quintet but tests a bit faster. will have to revisit. */
  while (i < plain.length) {
    var current = plain[i]; if (shiftIndex > 3) {
      digit = current & (0xff >> shiftIndex)
      shiftIndex = (shiftIndex + 5) % 8
      digit = (digit << shiftIndex) | ((i + 1 < plain.length) ? plain[i + 1] : 0) >> (8 - shiftIndex)
      i++
    } else {
      digit = (current >> (8 - (shiftIndex + 5))) & 0x1f
      shiftIndex = (shiftIndex + 5) % 8
      if (shiftIndex === 0) i++
    }
    encoded[j] = charTable.charCodeAt(digit); j++
  }
  for (i = j; i < encoded.length; i++) {
    encoded[i] = 0x3d // '='.charCodeAt(0)
  }
  return encoded.join('')
}
/* base32 end */

var hexToInt = function (str) {
  var result = []
  for (var i = 0; i < str.length; i += 2) {
    result.push(parseInt(str.substr(i, 2), 16))
  }
  return result
}
function hexToB32 (str) {
  return encode(hexToInt(str))
}

const getTotps = function () {
  var totps = []
  console.warn("Here's your Authy tokens:")
  appManager.getModel().forEach(function (i) {
    var secret = (i.markedForDeletion === false || !i.secretSeed) ? i.decryptedSeed : hexToB32(i.secretSeed)
    console.group(i.name)
    console.log('TOTP Secret: ' + secret)
    totps.push({
      name: i.name,
      secret: secret
    })
    console.groupEnd()
  })
  console.log(JSON.stringify(totps))
  return totps
}
getTotps()

Simple NodeJS Web Proxy

I was on a project recently where we were performing a network penetration test against an internal network, remotely. To facilitate this, we shipped a server to the client so they could install it into their datacenter. We then log in to this host, and perform our testing from it, rather than sending consultants onsite. This is super efficient, and cost-effective. The host we send out in these cases is referred to as a ‘jump box’ — because it offers a jumping-off point into the network.

For whatever reason, this time the jump box did not have the full set of tools installed. In many cases, this wouldn’t be a terrible problem, we’d just install the tools we need and move on. However, in this case, the client was performing egress filtering — meaning we had no way to get out to the internet from our jump box. This led to a dilemma: how can I access the remote services (like web sites) easily? Usually I’d set up tinyproxy or something similar, and set up a tunnel over SSH to access it. Unfortunately, I didn’t have tinyproxy or any other web proxy servers installed, and had no easy way to get one.

However, I did have nodejs installed on the box: which is quite useful for this type of thing! I did a quick search for nodejs proxies, and found a suitable base code over here. (Remember, I can’t get out to the internet from the jump box, so I need to limit my proxy code to only core nodejs modules, I can’t ‘npm install’ anything.)

That post is quite old (6 years!) and some things have changed since it was written. For one thing, the http.createServer() method has been replaced by http.request(). The sample code there also contained some things I didn’t need, like a blacklist of sites to prevent access to, but it had some things I definitely did want (like a whitelist of IPs that are allowed to talk to the proxy. That’s an important factor when doing this sort of test, if you are going to open up services on a client network, you need to take steps to minimize security risks they may cause. Restricting access to this proxy to only the localhost of the jump box helps me do that.)

You can check out the final results at my pentools github repo.

Once I had the code in place, I simply opened up an SSH tunnel to the jump box, and set up port 8080 on my laptop to tunnel to port 8080 on the jump box, like this:

ssh -L 8080:localhost:8080 user@jumpbox

Once I was logged in, I configured Firefox on my laptop to use localhost port 8080 for a web proxy, and I could now point my browser to the client’s internal network addresses and browse their websites from my browser, through the jump box proxy.

AJAX Fun

A little snippet of code I’m playing with. This started as me learning more about XST, to understand why TRACE being enabled was considered a BadThing(tm). [see: this white paper (.pdf format) for more on that].

In my opinion, the best way to learn is to do, so I quickly whipped up the following so I could play, and handily, this finally gives me a good reason to write my first bit of AJAX even =)

A couple of points:

    • If you change the method from GET to HEAD, this makes a handy banner grabber

 

    • If you change the method to TRACE, it may or may not work, depending on the browser you are using.

To explain the latter item:

The current versions of both Firefox and IE refuse to run TRACE via XMLHttpRequest.
This is the correct behaviour, per the spec, and is certainly more secure (it goes a fair way to mitigate XST in general in fact). I have not tried older versions or other browsers to see how they handle it.

Note that I snarfed bits and pieces of this code from various places on the net, and didn’t create all of this from scratch. However, I have tweaked and changed things enough to feel OK calling this “my code”.

I’ll probably tweak this further. I’m considering just making different buttons for the different types of requests and letting the function figure out what method to use based on that, for example.

Anyway, here’s the code as it stands after about 20 minutes of crash course in AJAX:

[EDIT:
if anyone knows how to post HTML/Javascript to blogspot, I’d be grateful for the tip, it keeps trying to render regardless of my use of pre or code. I even tried to settle for textarea, but it borked the formatting of the code unfortunately and added br tags all over the place. *sigh*. ]

Here’s a pastebin of the code instead

[EDIT 2010-03-02:
Oh for … Apparently IE8 renders the pastebin code as HTML instead of displaying it as text/plain. *cry*.