From b23e69ee51b94d4007c4bb3f1c424b14944fc9be Mon Sep 17 00:00:00 2001 From: 9021007 <24487638+9021007@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:00:00 +0000 Subject: [PATCH 1/3] Add Bitwarden support --- decrypt.mjs | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/decrypt.mjs b/decrypt.mjs index 8d6c47e..dcd6173 100644 --- a/decrypt.mjs +++ b/decrypt.mjs @@ -1,17 +1,25 @@ 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( readFileSync(0, "utf8") ).authenticator_tokens.filter((t) => t.encrypted_seed !== undefined); +console.log(1); + // The IV is static and equals 16 NULL bytes const IV = Buffer.from("00000000000000000000000000000000", "hex"); +console.log(2); + // Obtain your backup key from the environment variable const backupKey = process.env.BACKUP_KEY; +console.log(3); + /** * Decrypts the seed using the backup key and the account's salt * @param {String} seed Encrypted seed @@ -35,9 +43,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)); From 9fa5b1d1b94c6b070046cc662f649bf5bf6d91d3 Mon Sep 17 00:00:00 2001 From: 9021007 <24487638+9021007@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:07:01 +0000 Subject: [PATCH 2/3] iOS instructions --- README.md | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 77e0925..80906d9 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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.** @@ -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. From 0b33b6ed629998b2bbab6dd34bfcf183fafbf688 Mon Sep 17 00:00:00 2001 From: 9021007 <24487638+9021007@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:08:03 +0000 Subject: [PATCH 3/3] Update decrypt.mjs --- decrypt.mjs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/decrypt.mjs b/decrypt.mjs index dcd6173..eca1baa 100644 --- a/decrypt.mjs +++ b/decrypt.mjs @@ -8,18 +8,12 @@ const contents = JSON.parse( readFileSync(0, "utf8") ).authenticator_tokens.filter((t) => t.encrypted_seed !== undefined); -console.log(1); - // The IV is static and equals 16 NULL bytes const IV = Buffer.from("00000000000000000000000000000000", "hex"); -console.log(2); - // Obtain your backup key from the environment variable const backupKey = process.env.BACKUP_KEY; -console.log(3); - /** * Decrypts the seed using the backup key and the account's salt * @param {String} seed Encrypted seed