Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BitWarden Support + iOS instructions #2

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,21 @@

After discontinuation of Authy Desktop app, it is no longer possible to neither access nor export your 2FA tokens on desktop.

This script makes it possible to **partially** (around 90% in my case) extract your tokens from cached data of **Authy iOS app installed on macOS**.
This script makes it possible to **partially** (around 90% in my case) extract your tokens from cached data of the **Authy iOS app**, on macOS or iOS.

I wrote it for myself in about an hour, so it's far from perfect, but should work. Feel free to improve it :-)

There are likely much better ways to do this, but I found it utterly boring to deeper reverse-engineer the app.
The previous author wrote it for themselves in about an hour, so it's far from perfect, but should work. I imporved it slightly by having it output in BitWarden format.

## Step 1 – Locate cached keychain

What we are looking for is an encrypted keychain file in iOS Storage Container.

You can use this command to find the right Container, but it requires a
Full Disk Access permission for your Terminal app:

```shell
find ~/Library/Containers/ \
-maxdepth 1 -type d \
-regex ".*/[A-F0-9]\{8\}-[A-F0-9]\{4\}-[A-F0-9]\{4\}-[A-F0-9]\{4\}-[A-F0-9]\{12\}" \
-exec find {} -type f -path "*/Data/Library/Caches/com.authy/fsCachedData/*" \; | \
xargs grep -l "authenticator_tokens"
You can use this command to find the right Container, using Python:
```py
import os
for root, dirs, files in os.walk("/Users/MYUSERNAME/Library/Containers"):
for d in dirs:
if "fsCachedData" in os.path.join(root, d) and "com.authy" in os.path.join(root, d):
print(os.path.join(root, d))
```

Otherwise, you can search for the keychain file manually:
Expand All @@ -30,6 +26,24 @@ Otherwise, you can search for the keychain file manually:
3. In each directory, look for `Data/Library/Caches/com.authy/fsCachedData` folder.
4. If you find such a folder, look for a file with JSON content and `authenticator_tokens` key in it.

If you are doing this on a live (jailbroken) iOS device:

1. Launch Filza
2. Press the Star button on the bottom
3. Press "Apps Manager"
4. Select "Authy"
5. Go to `Library > Caches > com.authy > fsCachedData`
6. Press "Edit"
7. Select all files
8. Press "More"
9. Press "Create Zip"
10. Press on the "i" next to the zip file
11. Press the Share icon in the upper right
12. Press "QuickLook"
13. Press the Share icon in the upper right
14. Airdrop to your Mac
15. Inspect the files for JSON with `authenticator_tokens`

## Step 2 – Decrypt keychain

**First, audit the `decrypt.mjs` script contents.**
Expand All @@ -42,4 +56,4 @@ Then, run the script with the path to the keychain file as an argument:
cat ~/Library/Containers/.../00000000-0000-0000-0000-000000000000 | BACKUP_KEY="your-cool-bACKup-KEY" node decrypt.mjs
```

You should now see your Authy tokens decrypted.
You should now see your Authy tokens decrypted in the console, and a new file called `authyout.json`, which you can import into BitWarden of KeePassXC.
36 changes: 35 additions & 1 deletion decrypt.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { readFileSync } from "fs";
import { createDecipheriv, pbkdf2Sync } from "crypto";
import fs from 'fs';
import { v4 as uuidv4, v6 as uuidv6 } from 'uuid';

// Read the JSON from stdin, then filter out non-TOTP tokens
const contents = JSON.parse(
Expand Down Expand Up @@ -35,9 +37,41 @@ function decryptSync(seed, salt) {
return decrypted.toString("utf8");
}

// Iterate over each token and decrypt the seed phrase, then output it to stdout
// BitWarden format header
let tobewritten = {
"encrypted": false,
"folders": [
{
"id": uuidv4(),
"name": "Extracted from Authy"
}
],
"items": []
}

// Iterate over each token and decrypt the seed phrase, then output it to stdout, as well as the BitWarden JSON format
// along with the token's name and original name
for (const token of contents) {
const decrypted = decryptSync(token.encrypted_seed, token.salt);
console.log(`${token.name} (${token.original_name})\t ${decrypted}`);
fs.appendFileSync('authyout.txt', `${token.name} (${token.original_name})\t ${decrypted}\n`);
tobewritten.items.push({
"id": uuidv4(),
"organizationId": null,
"folderId": tobewritten.folders[0].id,
"type": 1,
"reprompt": 0,
"name": token.name,
"notes": token.original_name,
"favorite": false,
"login": {
"username": null,
"password": null,
"totp": `otpauth://totp/${token.name}?secret=${decrypted}&digits=${token.digits}&period=30`
},
"collectionIds": null
})
}

// Write the BitWarden JSON to a file
fs.writeFileSync('authyout.json', JSON.stringify(tobewritten, null, 2));