Cifra de César
Dado o texto cifrado vxshyk{g9g9g099-hg33-4f60-90gk-ikff1f36953j}, aplica-se uma rotação de caracteres. Aplicando deslocamento reverso, obtém-se a flag.
# Decodificação de César
def caesar_decodificar(texto, deslocamento):
resultado = []
for caractere in texto:
if caractere.isalpha():
base = ord('a') if caractere.islower() else ord('A')
resultado.append(chr((ord(caractere) - base - deslocamento) % 26 + base))
else:
resultado.append(caractere)
return ''.join(resultado)
mascara = "vxshyk{g9g9g099-hg33-4f60-90gk-ikff1f36953j}"
print(caesar_decodificar(mascara, 6))
# qsnctf{b9b9b099-cb33-4a60-90bf-dfaa1a36953e}
Base64 e Base32
Strings codificadas em Base64 podem ser reconhecidas pelo preenchimento e alfabeto. Para o exemplo cXNuY3RmezY4NjkwOGJjLTFiZjItNGMxOS04YTAxLWIyYzc3NjAwOTkyOH0=, a decodificação direta retorna a flag.
Para cadeias hexadecimais seguidas de Base32 e Base64, o pipeline é: hexadecimal → Base32 → Base64.
Código Morse
O sinal .----/-----/.-/----./...-- segue o padrão Morse internacional. Cada sequência separada por barra representa um caractere.
Transformações e Esteganografia
Bear / 熊曰
Textos que começam com "熊曰" indicam codificação específica de uma ferramenta chinesa. O processo consiste em colar o conteúdo no decodificador correspondente.
Decodificação por Alfabeto de Sobrenomes
Um texto extenso composto por sobrenomes chineses codifica, na verdade, uma sequência hexadecimal. Cada sobrenome mapeia para um valor numérico que, concatenado, forma bytes hexadecimais. Convertendo para ASCII, revela-se o conteúdo original.
Desafio ASCII e César Combinados
Quando um desafio entrega uma sequência de números decimais, o primeiro passo é converter para caracteres ASCII. Se o resultado parecer um UUID com máscara, testa-se deslocamentos de César. Uma vez identificado o padrão qsnctf{...}, resta fazer força bruta sobre os últimos caracteres e verificar o hash MD5.
import hashlib
import string
alfabeto = string.digits + string.ascii_letters
def forcar_uuid(prefixo, alvo):
for a in alfabeto:
for b in alfabeto:
for c in alfabeto:
candidato = prefixo + a + b + c
digest = hashlib.md5(candidato.encode()).hexdigest()
if digest[:36] == alvo:
return candidato
return None
prefixo_uuid = "0ef1ea8c-2e72-4b9f-87e1-08d53a80c"
hash_alvo = "8a2a057c9bb3a3463e7583663341e9e5"
print(forcar_uuid(prefixo_uuid, hash_alvo))
# 0ef1ea8c-2e72-4b9f-87e1-08d53a80c743
RSA — Níveis Iniciais
Baby RSA com Soma e Produto
Conhecendo s = p + q e t = (p+1)(q+1), calcula-se n = t - s - 1. Com e e d, aplica-se a exponenciação modular para obter o plaintext.
from Crypto.Util.number import long_to_bytes
def resolver_baby_rsa(s, t, e, d, cifra):
modulo = t - s - 1
return long_to_bytes(pow(cifra, d, modulo))
s = 0x1232fecb92adead91613e7d9ae5e36fe6bb765317d6ed38ad890b4073539a6231a6620584cea5730b5af83a3e80cf30141282c97be4400e33307573af6b25e2ea
t = 0x5248becef1d925d45705a7302700d6a0ffe5877fddf9451a9c1181c4d82365806085fd86fbaab08b6fc66a967b2566d743c626547203b34ea3fdb1bc06dd3bb765fd8b919e3bd2cb15bc175c9498f9d9a0e216c2dde64d81255fa4c05a1ee619fc1fc505285a239e7bc655ec6605d9693078b800ee80931a7a0c84f33c851740
e = 0xe6b1bee47bd63f615c7d0a43c529d219
d = 0x2dde7fbaed477f6d62838d55b0d0964868cf6efb2c282a5f13e6008ce7317a24cb57aec49ef0d738919f47cdcd9677cd52ac2293ec5938aa198f962678b5cd0da344453f521a69b2ac03647cdd8339f4e38cec452d54e60698833d67f9315c02ddaa4c79ebaa902c605d7bda32ce970541b2d9a17d62b52df813b2fb0c5ab1a5
c = 0x50ae00623211ba6089ddfae21e204ab616f6c9d294e913550af3d66e85d0c0693ed53ed55c46d8cca1d7c2ad44839030df26b70f22a8567171a759b76fe5f07b3c5a6ec89117ed0a36c0950956b9cde880c575737f779143f921d745ac3bb0e379c05d9a3cc6bf0bea8aa91e4d5e752c7eb46b2e023edbc07d24a7c460a34a9a
print(resolver_baby_rsa(s, t, e, d, c))
RSA Básico com Fatoração Direta
Para pequenos primos, a fatoração é direta. Com p e , computa-se φ(n) = (p-1)(q-1), inverte-se e e descriptografa-se.
import gmpy2
from Crypto.Util.number import long_to_bytes
p = 262248800182277040650192055439906580479
q = 262854994239322828547925595487519915551
e = 65533
c = 27565231154623519221597938803435789010285480123476977081867877272451638645710
phi = (p - 1) * (q - 1)
n = p * q
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
Desafios RSA Avançados
RSA com Fatoração por Pequenos Fatores
Quando n é produto de vários primos pequenos, utiliza-se a função totiente de Euler para obter φ(n) sem fatoração tradicional.
import gmpy2
import libnum
n = 157077292656328898849823499976497003976795705913326943955927601882559735301000546878663484930436631929909115065166613744548816622146802007640124796249330573411377703969505934904150600987843325674764620305047603408490558134670867673308099650843329640744997672015466571290660161290811275435569339606335117906571999000341133024698424364682800683662193063661214736762852739324479859236963365531207752799197178993887860855078852702337761399225640575281412171035871278933493943575572155382899938265639764715616686123949482372238288859715465115400317136714757882965887595246507450491169518000205087415380208167764110920711042584766805992237919576823121108078407699912757901788925718859790257450499775129521327827653298451904392241906547672843110356658889638496906522290674659574024024440113632175010053065452660076447040937842478007881589334096496073556056726805396937630799201696246079227214272205462258357482722478243481697053301054600954126539848778226175296162997813416634702496577009409960503948474494741296663849482119365434792563324547643352816519125305335959420429699475765642610737903235960423173
c = 44457399775772165283580795763046604956432217865936749114390645714446263790235445725770165521476841968764175721036280702731933849090719866149354613431301887740671003826556620460836983488011711209908075106260857650574672356032244606425941095128801765463716482316101398637519304864271794460829068714740938719022156283319142938782439784724450045931039355442034325311037568791297455084676548879770834712506552233840348850684727096270392080049993135041218143811167688449496243036317450681348089315258831745988434134987055263393540923865029931594717328162951158311497514418799360413513590684301435386737514918075848373373755748782672860711406169316940293554209702288482064854840802876490202123903888235028119047988176327629542924415737212649237787748145773301112682790682933658516724691338727523894513267588035437093188599375494920656327919129240066252636130803666175859640361767805549884909317548802917210333235914904622641997249853362378711924024129399688535136879208010081166848163897114124726692078532337827810846421365846926064892472698603597461932481745017020417072013702099809833423003201003030492
e = 65537
phi = euler_phi(n)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(libnum.n2s(int(m)))
RSA com Derivadas e Relação Especial
Dado z = 1/(arctan(p))' - 1/(arth(q))', sabe-se que (arctan x)' = 1/(1+x²) e (arth x)' = 1/(1-x²). Logo, z = p² + q². Combinando com n = pq, temos p + q = √(z + 2n) e φ(n) = n - (p+q) + 1.
import gmpy2
from Crypto.Util.number import long_to_bytes
c = 7922547866857761459807491502654216283012776177789511549350672958101810281348402284098310147796549430689253803510994877420135537268549410652654479620858691324110367182025648788407041599943091386227543182157746202947099572389676084392706406084307657000104665696654409155006313203957292885743791715198781974205578654792123191584957665293208390453748369182333152809882312453359706147808198922916762773721726681588977103877454119043744889164529383188077499194932909643918696646876907327364751380953182517883134591810800848971719184808713694342985458103006676013451912221080252735948993692674899399826084848622145815461035
z = 32115748677623209667471622872185275070257924766015020072805267359839059393284316595882933372289732127274076434587519333300142473010344694803885168557548801202495933226215437763329280242113556524498457559562872900811602056944423967403777623306961880757613246328729616643032628964072931272085866928045973799374711846825157781056965164178505232524245809179235607571567174228822561697888645968559343608375331988097157145264357626738141646556353500994924115875748198318036296898604097000938272195903056733565880150540275369239637793975923329598716003350308259321436752579291000355560431542229699759955141152914708362494482
n = 15310745161336895413406690009324766200789179248896951942047235448901612351128459309145825547569298479821101249094161867207686537607047447968708758990950136380924747359052570549594098569970632854351825950729752563502284849263730127586382522703959893392329333760927637353052250274195821469023401443841395096410231843592101426591882573405934188675124326997277775238287928403743324297705151732524641213516306585297722190780088180705070359469719869343939106529204798285957516860774384001892777525916167743272419958572055332232056095979448155082465977781482598371994798871917514767508394730447974770329967681767625495394441
e = 65537
soma_pq = gmpy2.iroot(z + 2 * n, 2)[0]
phi = n - soma_pq + 1
d = gmpy2.invert(e, phi)
print(long_to_bytes(pow(c, d, n)))
Cifras Clássicas e Modernas
Cifra de Hill
Para uma cifra de Hill 2×2, descobre-se a matriz chave a partir de pares de letras conhecidos (como o prefixo utflag) e aplica-se a inversa modular para decifrar.
Cifra de Teclado
Sequências como ooo yyy ii w uuu ee iii ee uuu ooo r yyy yyy e representam teclas pressionadas repetidamente em teclado telefônico. O número de repetições indica a letra dentro da tecla.
def decodificar_teclado(texto):
mapeamento = [" ", " ", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]
resultado = ""
for parte in texto.split():
tecla = len(parte)
indice = len(parte) - 1
resultado += mapeamento[tecla][indice]
return resultado
print(decodificar_teclado("ooo yyy ii w uuu ee iii ee uuu ooo r yyy yyy e"))
# youareverygood
Bacon e Códigos Especiais
Na cifra de Bacon, . e - (ou A/B) representam 5 bits que mapeiam para letras. Em alguns desafios, é necessário trocar o papel dos símbolos e aplicar análise de frequência.
AES com Chave e IV Derivos
Quando a chave é gerada por repetição de 2 bytes e o IV é derivado de XOR entre chave e um valor conhecido, pode-se recuperar ambos a partir do output.
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Cipher import AES
valor_x = 91144196586662942563895769614300232343026691029427747065707381728622849079757
cifra = b'\x8c-\xcd\xde\xa7\xe9\x7f.b\x8aKs\xf1\xba\xc75\xc4d\x13\x07\xac\xa4&\xd6\x91\xfe\xf3\x14\x10|\xf8p'
dados = long_to_bytes(valor_x)
chave = dados[:16] * 2
iv_numerico = bytes_to_long(chave[16:]) ^ bytes_to_long(dados[16:])
iv = long_to_bytes(iv_numerico)
aes = AES.new(chave, AES.MODE_CBC, iv)
print(aes.decrypt(cifra))
Hashes, Base Variantes e Outras Técnicas
Ataque a Hashes por Dicionário
Quando cada caractere de uma flag é hasheado individualmente com dupla aplicação de MD5, itera-se sobre caracteres imprimíveis até encontrar correspondências.
from hashlib import md5
import string
hashes = ['999a215b1f8372bb0f1c84c467a1506b', '02b0b94ee1fa195ae7959560893f7e3c'] # exemplo
caracteres = string.printable
def decodificar_hash(lista_hash):
flag = ""
for h in lista_hash:
for c in caracteres:
d1 = md5(c.encode()).hexdigest()
if md5(d1.encode()).hexdigest() == h:
flag += c
break
return flag
Base64 com Alfabeto Personalizado
Para alfabetos Base64 modificados, cria-se um mapeamento entre o alfabeto padrão e o personalizado, depois decodifica-se normalmente.
import base64
def decodificar_base64_customizado(cifra, alfabeto_personalizado):
alfabeto_padrao = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
tabela = str.maketrans(alfabeto_personalizado, alfabeto_padrao)
return base64.b64decode(cifra.translate(tabela))
exemplo = "FlZNfnF6Qol6e9w17WwQQoGYBQCgIkGTa9w3IQKw"
alfabeto = "".join([chr(65+i) for i in range(26)])
# decodificar_base64_customizado(exemplo, alfabeto)
Desafios de Fatoração e Chaves Públicas
Para arquivos PEM/DER com chave pública RSA, após fatorar n, utiliza-se rsatool para gerar a chave privada e o OpenSSL para decifrar.
# Gerar chave privada
# python rsatool.py -f DER -o chave.der -p <primo_p> -q <primo_q>
# Decodificar base64 da cifra
# openssl enc -base64 -d -in entrada.enc -out saida.enc
# Decifrar
# openssl pkeyutl -decrypt -in saida.enc -inkey chave.der -out flag.txt
</primo_q></primo_p>
Conceitos Adicionais
- Cifra de Vigenère com chave conhecida: quando a chave é
qsnctfou derivada do contexto, aplica-se a decodificação polialfabética direta. - ROT13 e codificações múltiplas: muitos desafios empliham ROT13, Base85, Morse e gzip. A ordem de aplicação é determinada pelo contexto.
- Rabbit e outras cifras: textos começando com
U2FsdGVkX1indicam cifras derivadas de OpenSSL, frequentemente Rabbit ou AES com senha. - Esquemas de limiar: desafios que dividem segredos entre múltiplos guardiões usam Shamir's Secret Sharing; com 3 de 5 partes, recupera-se o polinômio.
- Mersenne Twister: quando números pseudoaleatórios são gerados com
randomdo Python, a bibliotecarandcrackpode prever valores futuros a partir de 624 bits consecutivos.