Como extrair chaves TOTP 2FA a partir de QRCODE (Google Authenticator)

Aprenda a extrair chaves TOTP de QRCODEs usados em autenticação de dois fatores (2FA) com ferramentas Linux e um script Python.

[ Hits: 96 ]

Por: Fábio Berbert de Paula em 19/10/2025 | Blog: https://fabio.automatizando.dev


Introdução



Hoje é prática recomendade e diria até mesmo obrigatória o uso de autenticação de dois fatores (2FA) para aumentar a segurança das contas online. Um dos métodos mais comuns de 2FA é o uso de códigos TOTP (Time-based One-Time Password), frequentemente gerados por aplicativos como Google Authenticator, Authy, entre outros.

Um dos formatos mais comuns para configurar esses aplicativos é através de QRCODES, que contêm as informações necessárias para gerar os códigos TOTP. Neste artigo, vamos explorar como extrair a chave secreta TOTP a partir de um QRCODE.

E por que isso é importante? Bem, ter acesso à chave secreta permite que você configure o 2FA em diferentes dispositivos ou aplicativos, garantindo que você não perca o acesso à sua conta caso perca o dispositivo original.

Eu por exemplo utilizo a chave secreta para obter os códigos TOTP via linha de comando no Linux, utilizando o utilitário oathtool. Assim como também via add-on no navegador Firefox, chamado Authenticator (https://github.com/Authenticator-Extension/Authenticator).

O grande problema é que muitos serviços não fornecem a chave secreta diretamente, apenas o QRCODE. Felizmente, existem maneiras de extrair essa informação usando ferramentas no Linux.

Extraindo a chave TOTP do QRCODE

O primeiro passo é obter o QRCODE que você deseja extrair a chave TOTP. Abra o Google Authenticator ou o aplicativo que você está usando, localize a opção de exportar ou visualizar o QRCODE e salve a imagem do QRCODE em seu computador.

Meu Google Authenticator está no Android, então o que fiz foi utilizar o utulitário scrcpy para espelhar a tela do celular no Linux e capturar a imagem do QRCODE com o Flameshot. Esse procedimento pode ser feito da forma que você preferir, não me aprofundarei nessa etapa.

Vou assumir que você salvou a imagem do QRCODE como "qrcode.png".

Agora, precisamos instalar a ferramenta que nos ajudará a decodificar o QRCODE. Digite:

sudo apt-get install zbar-tools

Com a ferramenta instalada, podemos usar o comando zbarimg para ler o QRCODE e extrair a URL que contém a chave TOTP. Execute o seguinte comando:

zbarimg qrcode.png

A saída será algo parecido com isto:

QR-Code:otpauth-migration://offline?data=CkwKFBVIs9WO4MSysvXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXNTNhOGYxNzAzODQ1MDc0MTM5EAIYASAA
scanned 1 barcode symbols from 1 images in 0 seconds

Sim, as chaves estão codificadas em base64. Precisamos decodificá-las para obter a chave TOTP.

Decodificando a chave TOTP

Para decodificar a chave TOTP, podemos usar o comando base64. A parte que vem depois de data= é a que nos interessa. Ela é uma URL codificada em base64.

Primeiro vamos extrair apenas a parte da chave:

ENCODED_KEY=$(zbarimg qrcode.png | grep -oP '(?<=data=)[^ ]+')

Agora, vamos decodificar a URL:

DECODED_DATA=$(printf '%b' "${ENCODED_KEY//%/\x}")

E finalmente, podemos decodificar a chave TOTP em base64 (ou se tiver mais de uma chave, decodificá-las todas):

echo -n "$DECODED_DATA" | base64 --decode

Automatizando o processo

Para facilitar, vou deixar aqui um script em Python que automatiza todo o processo de extração da chave TOTP a partir da imagem do QRCODE:

#!/usr/bin/env python3
# --------------------------------------------------------------------
# Extract Google Authenticator secrets from a QR Code image
#
# DEPENDÊNCIAS (Ubuntu/Debian):
#   sudo apt install zbar-tools
#
# USO:
#   python3 extrair-google-auto-qrcode imagem.png
#
# O QUE O SCRIPT FAZ:
#   1. lê a imagem usando `zbarimg --raw`
#   2. extrai o "data=..." do esquema otpauth-migration
#   3. decodifica o protobuf manualmente (sem dependências externas)
#   4. imprime os segredos 2FA em Base32 e otpauth://totp
#
# ATENÇÃO:
#   O segredo extraído permite gerar códigos 2FA. Guarde com segurança.
# --------------------------------------------------------------------

import sys
import subprocess
from urllib.parse import unquote
import base64


# ---------------------- CHECAGEM DO ARGUMENTO ----------------------
if len(sys.argv) < 2:
    print("ERRO: informe a imagem como parâmetro.")
    print("Exemplo: python3 extract_gauth_from_qr.py qrcode.png")
    sys.exit(1)

img = sys.argv[1]


# ---------------------- LÊ O QR COM zbarimg ------------------------
try:
    output = subprocess.check_output(["zbarimg", "--raw", img], stderr=subprocess.DEVNULL)
    qr_text = output.decode().strip()
except FileNotFoundError:
    print("ERRO: zbarimg não encontrado. Instale com: sudo apt install zbar-tools")
    sys.exit(1)
except subprocess.CalledProcessError:
    print("ERRO: não foi possível ler o QRCode da imagem.")
    sys.exit(1)


# ---------------------- VALIDA O FORMATO ---------------------------
if not qr_text.startswith("otpauth-migration://"):
    print("ERRO: QRCode não é do tipo otpauth-migration.")
    sys.exit(1)

# extrai valor do data=
if "data=" not in qr_text:
    print("ERRO: não foi encontrado parâmetro data= no QRCode.")
    sys.exit(1)

DATA_PARAM = qr_text.split("data=")[1]


# ---------------------- FUNÇÕES DE DECODE --------------------------
def read_varint(b, i):
    shift = 0
    result = 0
    while True:
        byte = b[i]; i += 1
        result |= (byte & 0x7F) << shift
        if not (byte & 0x80): break
        shift += 7
    return result, i

def read_length_delimited(b, i):
    length, i = read_varint(b, i)
    data = b[i:i+length]
    return data, i+length


# ---------------------- DECODE PAYLOAD -----------------------------
raw = base64.b64decode(unquote(DATA_PARAM))
i = 0
otp_messages = []

while i < len(raw):
    tag, i = read_varint(raw, i)
    field_num = tag >> 3
    wire_type = tag & 0x7
    if field_num == 1 and wire_type == 2:  # repeated OTPParameters
        msg_bytes, i = read_length_delimited(raw, i)
        otp_messages.append(msg_bytes)
    else:
        if wire_type == 0:  _, i = read_varint(raw, i)
        elif wire_type == 2: _, i = read_length_delimited(raw, i)
        elif wire_type == 1: i += 8
        elif wire_type == 5: i += 4


def parse_otp_params(b):
    out = {}; i = 0
    while i < len(b):
        tag, i = read_varint(b, i)
        field_num = tag >> 3
        wire_type = tag & 0x7
        if wire_type == 2:
            data, i = read_length_delimited(b, i)
            out[field_num] = data.decode('utf-8') if field_num in (2,3) else data
        elif wire_type == 0:
            val, i = read_varint(b, i)
            out[field_num] = val
        elif wire_type == 1: out[field_num] = b[i:i+8]; i+=8
        elif wire_type == 5: out[field_num] = b[i:i+4]; i+=4
    return out

def to_base32(raw):
    return base64.b32encode(raw).decode('utf-8').rstrip('=')


# ---------------------- RESULTADO FINAL ----------------------------
print("
=== RESULTADOS EXTRAÍDOS ===")

for idx, msg in enumerate(otp_messages, 1):
    f = parse_otp_params(msg)
    secret = f.get(1, b'')
    name = f.get(2, '')
    issuer = f.get(3, '')
    digits = f.get(5, 6)

    b32 = to_base32(secret)
    label = f"{issuer}:{name}" if issuer else name
    otpauth = f"otpauth://totp/{label}?secret={b32}&digits={digits}&algorithm=SHA1"

    if issuer:
        otpauth += f"&issuer={issuer}"

    print(f"
--- ENTRY {idx} ---")
    print("Name   :", name)
    print("Issuer :", issuer)
    print("Secret :", b32)
    print("URL    :", otpauth)

print("
Concluído.
")

Salve o script acima como extrair-google-auto-qrcode.py, dê permissão de execução (chmod +x extrair-google-auto-qrcode.py) e execute-o passando a imagem do QRCODE como parâmetro:

./extrair-google-auto-qrcode.py qrcode.png

A saída será algo parecido com isto:

=== RESULTADOS EXTRAÍDOS ===

--- ENTRY 1 ---
Name   : FBP12#775678
Issuer : Registro.br
Secret : CVELHVMO4DCLFMX44UGBXWBRVDENTFP3
URL    : otpauth://totp/Registro.br:FBP12#775678?secret=CVELHVMO4DCLFMX44UGBXWBRVDENTFP3&digits=1&algorithm=SHA1&issuer=Registro.br

--- ENTRY 2 ---
Name   : Fberbert Instagram
Issuer :
Secret : 3D5ZICX6IC6SENWKSEVNAQPBAV4ZJZMZ
URL    : otpauth://totp/Fberbert Instagram ?secret=3D5ZICX6IC6SENWKSEVNAQPBAV4ZJZMZ&digits=1&algorithm=SHA1

Concluído.

Até a próxima!

   

Páginas do artigo
   1. Introdução
Outros artigos deste autor

GitHub Copilot - Inteligência Artificial no NeoVim

Edital de política de contribuição de artigos

Resumo do VOL DAY I

Clicador automático de Tinder com Python

Semana da velharia no VOL

Leitura recomendada

Breve Estudo Sobre Ransomwares e Análise Estática/Dinâmica do WannaCry

Python: automatizando a extração de informações na web com expressões regulares

Como gerar qualquer emoji ou símbolo unicode a partir do seu teclado

Splash Screen para Inkscape

Trabalhando com permutações em ordem lexicográfica crescente

  
Comentários

Nenhum comentário foi encontrado.


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts