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: 2.305 ]
Por: Fábio Berbert de Paula em 19/10/2025 | Blog: https://fabio.automatizando.dev
QR-Code:otpauth-migration://offline?data=CkwKFBVIs9WO4MSysvXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXNTNhOGYxNzAzODQ1MDc0MTM5EAIYASAA scanned 1 barcode symbols from 1 images in 0 seconds
#!/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.
")
=== 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.
Python - Usando requests anônimos através da rede Tor
Desktop Linux ganha fôlego enquanto distros refinam experiência e segurança
Organizando projetos com a classe FastTemplate (parte 1)
Corretor Ortográfico no Vim - Guia definitivo
Adote um projeto, ajude o Viva o Linux
Python - Usando requests anônimos através da rede Tor
Monitorando produtos no ML com Python 3 via BeautifulSoup
ISO8583py - Utilizando Python para o tratamento de mensagens ISO8583
Crie um bot para Telegram e gerencie a escala de plantonistas
Qu1cksc0pe - All-in-One Static Malware Analysis Tool
Nenhum comentário foi encontrado.
O Journal no Linux para a guarda e consulta de logs do sistema
A evolução do Linux e as mudanças que se fazem necessárias desde o seu lançamento
Maquina modesta - a vez dos navegadores ferrarem o usuario
Fscrypt: protegendo arquivos do seu usuário sem a lentidão padrão de criptograr o disco
Sway no Arch Linux: configuração Inicial sem enrolação
Resolvendo o bloqueio do Módulo Warsaw no Arch Linux (Porta 30900)
Continuando meus tópicos anteriores (0)
Saída de loop após teste de if. (2)
Governo da França vai trocar Windows por Linux (9)
Warsaw não é reconhecido no Google Chrome 147.0.7727.55 [RESOLVIDO] (9)









