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: 97 ]
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.
Otimizando seu web server com Apache2 + Lighttpd
RedBug: Lista dos autores de artigos sorteados do mês
Expressões Regulares - Entenda o que são Lookahead e Lookbehind
IA chega ao desktop e impulsiona produtividade no mundo Linux
Usando o "at" para agendamento de tarefas
Como baixar fotos e vídeos do Instagram com Python
ISO8583py - Utilizando Python para o tratamento de mensagens ISO8583
Como baixar vídeos do Facebook via terminal
Introdução ao clib (Command Line Book)
Clicador automático de Tinder com Python
Nenhum comentário foi encontrado.
Como extrair chaves TOTP 2FA a partir de QRCODE (Google Authenticator)
Linux em 2025: Segurança prática para o usuário
Desktop Linux em alta: novos apps, distros e privacidade marcam o sábado
IA chega ao desktop e impulsiona produtividade no mundo Linux
Novos apps de produtividade, avanços em IA e distros em ebulição agitam o universo Linux
Digitando underscore com "shift" + "barra de espaços"
Como ativar a lixeira e recuperar aquivos deletados em um servidor Linux
Como mudar o nome de dispositivos Bluetooth via linha de comando
Problema em SSD ao dar boot LinuxMint LMDE FAYE 64 (0)
Baixar jogos Independentes para Ubuntu [RESOLVIDO] (4)
PIP3 - erro ao instalar módulo do mariadb para o Python (1)
Linux x Plataformas de Trading - um problema (in-)solúvel? (4)