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: 66 ]

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

Criando sites para celular com WML

Transferindo arquivos com o rsync

Capturando tela do Android usando shell script

RedBug: Lista dos autores de artigos sorteados do mês

Como fazer publicações pelo Instagram Web

Leitura recomendada

Port Scanner com Python

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

Integração do ChatGPT em uma API Python

Introdução ao clib (Command Line Book)

Arduino com Bluetooth e pySerial

  
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