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.
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.
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:
Sim, as chaves estão codificadas em base64. Precisamos decodificá-las para obter a chave TOTP.
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
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:
Até a próxima!
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!