Careless Padding
28 minutos de lectura
Se nos proporciona el código fuente del servidor en Python:
#!/usr/local/bin/python
import random
import os
from secret import flag
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import json
N = 16
# 0 -> 0, 1~N -> 1, (N+1)~(2N) -> 2 ...
def count_blocks(length):
block_count = (length-1) // N + 1
return block_count
def find_repeat_tail(message):
Y = message[-1]
message_len = len(message)
for i in range(len(message)-1, -1, -1):
if message[i] != Y:
X = message[i]
message_len = i + 1
break
return message_len, X, Y
def my_padding(message):
message_len = len(message)
block_count = count_blocks(message_len)
result_len = block_count * N
if message_len % N == 0:
result_len += N
X = message[-1]
Y = message[(block_count-2)*N+(X%N)]
if X==Y:
Y = Y^1
padded = message.ljust(result_len, bytes([Y]))
return padded
def my_unpad(message):
message_len, X, Y = find_repeat_tail(message)
block_count = count_blocks(message_len)
_Y = message[(block_count-2)*N+(X%N)]
if (Y != _Y and Y != _Y^1):
raise ValueError("Incorrect Padding")
return message[:message_len]
def chal():
k = os.urandom(16)
m = json.dumps({'key':flag}).encode()
iv = os.urandom(16)
cipher = AES.new(k, AES.MODE_CBC, iv)
padded = my_padding(m)
enc = cipher.encrypt(padded)
print(f"""
*********************************************************
You are put into the careless prison and trying to escape.
Thanksfully, someone forged a key for you, but seems like it's encrypted...
Fortunately they also leave you a copied (and apparently alive) prison door.
The replica pairs with this encrypted key. Wait, how are this suppose to help?
Anyway, here's your encrypted key: {(iv+enc).hex()}
*********************************************************
""")
while True:
enc = input("Try unlock:")
enc = bytes.fromhex(enc)
iv = enc[:16]
cipher = AES.new(k, AES.MODE_CBC, iv)
try:
message = my_unpad(cipher.decrypt(enc[16:]))
if message == m:
print("Hey you unlock me! At least you know how to use the key")
else:
print("Bad key... do you even try?")
except ValueError:
print("Don't put that weirdo in me!")
except Exception:
print("What? Are you trying to unlock me with a lock pick?")
if __name__ == "__main__":
chal()
Análisis del código fuente
El servidor genera una clave aleatoria y un IV aleatorio, crea un cifrador AES en modo CBC y cifra la flag. Se nos da el IV y el texto cifrado y se nos permite descifrar textos cifrados arbitrarios con un IV elegido, aunque no vemos el resultado descifrado.
Hay una implementación de relleno personalizado. Dado que my_unpad
lanza ValueError
, tenemos un oráculo de relleno. Si al texto claro se le quita el relleno correctamente, el servidor mostrará Bad key... do you even try?
, pero si el relleno está mal, el servidor dice Don't put that weirdo in me!
:
try:
message = my_unpad(cipher.decrypt(enc[16:]))
if message == m:
print("Hey you unlock me! At least you know how to use the key")
else:
print("Bad key... do you even try?")
except ValueError:
print("Don't put that weirdo in me!")
Implementación del relleno
Echemos un vistazo a la implementación del relleno:
def my_padding(message):
message_len = len(message)
block_count = count_blocks(message_len)
result_len = block_count * N
if message_len % N == 0:
result_len += N
X = message[-1]
Y = message[(block_count-2)*N+(X%N)]
if X==Y:
Y = Y^1
padded = message.ljust(result_len, bytes([Y]))
return padded
def my_unpad(message):
message_len, X, Y = find_repeat_tail(message)
block_count = count_blocks(message_len)
_Y = message[(block_count-2)*N+(X%N)]
if (Y != _Y and Y != _Y^1):
raise ValueError("Incorrect Padding")
return message[:message_len]
En my_padding
, el servidor toma el último byte del mensaje como X = message[-1]
e Y = message[(block_count-2)*N+(X%N)]
. Suponiendo que enviaremos al menos 2 bloques, Y
is solo el byte en la posición X % N
del penúltimo bloque. Y el mensaje estará rellenado con bytes Y
(si X
coincide con Y
, entonces Y = Y ^ 1
).
Veamos algunos ejemplos:
>>> my_padding(b'0123456789abcdef' + b'A')
b'0123456789abcdefA111111111111111'
>>> my_padding(b'0123456789abcdef' + b'B')
b'0123456789abcdefB222222222222222'
>>> my_padding(b'0123456789abcdef' + b'C')
b'0123456789abcdefC333333333333333'
>>> my_padding(b'0123456789abcdef' + b'D')
b'0123456789abcdefD444444444444444'
>>> my_padding(b'0123456789abcdef' + b'E')
b'0123456789abcdefE555555555555555'
Como el valor ASCII para A
es 0x41
, entonces X
es 0x41
y X % N
es simplemente 1
, por tanto, Y
es message[1]
, que es '1'
. De ahí que el relleno del primer texto es un montón de '1'
s.
Observe que si usamos B
en lugar de A
, ahora el relleno es message[2]
. Luego, usando C
, el relleno es message[3]
y sucesivamente.
Como N = 16
, hay un bucle:
>>> my_padding(b'0123456789abcdef' + b'!')
b'0123456789abcdef!111111111111111'
>>> my_padding(b'0123456789abcdef' + b'1')
b'0123456789abcdef1000000000000000'
>>> my_padding(b'0123456789abcdef' + b'A')
b'0123456789abcdefA111111111111111'
>>> my_padding(b'0123456789abcdef' + b'Q')
b'0123456789abcdefQ111111111111111'
>>> my_padding(b'0123456789abcdef' + b'a')
b'0123456789abcdefa111111111111111'
>>> my_padding(b'0123456789abcdef' + b'q')
b'0123456789abcdefq111111111111111'
Como se puede ver, cada valor ASCII que da un resto de 1
al dividir entre N
resultará en un relleno de message[1]
. Esto es, 0x21
(!
), 0x31
(1
), 0x41
(A
), 0x51
(Q
), 0x61
(a
) y 0x71
(q
). Hay una excepción con el 0x31
(1
), donde el relleno es 0
(0x30 = 0x31 ^ 1
) porque el último byte del mensaje también es un 1
.
Padding Oracle Attack
La idea del Padding Oracle Attack es modificar los últimos bytes del penúltimo bloque de texto cifrado, para que afecten los últimos bytes del último bloque. Dado que el relleno está al final del último bloque, sabremos si al mensaje se le quita el relleno correctamente o no y adivinar bytes de texto claro en función de eso:
La implementación de relleno típica es PKCS7, que es predecible y permite recuperar el texto claro (para más información, véase I know Mag1k). Esta vez, la implementación del relleno no nos dará el texto claro directamente, sino un conjunto de valores posibles de bytes de texto claro.
Solución
Mi solución se basa en el Padding Oracle Attack y un poco de intuición para recuperar el mensaje en texto claro (la flag).
En primer lugar, lo que se está cifrando es un objeto JSON que contiene la flag. El mensaje con la flag de prueba es:
{"key": "hitcon{tmperory_flag_for_testing_and_no_more}"}
Vamos a dividirlo en bloques:
>>> import json
>>> flag = "hitcon{tmperory_flag_for_testing_and_no_more}"
>>> m = json.dumps({'key': flag}).encode()
>>> padded = my_padding(m)
>>> print('\n'.join(padded[i:i+N].decode() for i in range(0, len(padded), N)))
{"key": "hitcon{
tmperory_flag_fo
r_testing_and_no
_more}"}________
Nótese que ya conocemos el primer bloque de texto claro ({"key": "hitcon{
). Podemos usarlo para adivinar el byte de relleno del último bloque (enviaremos siempre dos bloques).
Si mandamos IV, ct[:16]
y ct[16:32]
al servidor, el texto claro será exactamente {"key": "hitcon{
y tmperory_flag_fo
. El servidor intentará quitar el relleno del resultado. En este caso, o
es el byte de relleno, y X
es 'f'
(0x66
). Por tanto, el relleno esperado es message[6]
, que es ':'
, y no es correcto:
>>> import os
>>> from Crypto.Cipher import AES
>>>
>>> k = os.urandom(16)
>>> iv = os.urandom(16)
>>>
>>> ct = AES.new(k, AES.MODE_CBC, iv).encrypt(padded)
>>> ct
b'\x0f\xae\xa0\x18\xceh\x06\x08\xd42\xc6=\xf8\xce\x1c\xc4\xcbUn\xfd4\xd7+\x95\xc1\x94\xa5o\xaf\x01\xcb\x0c\xea\x05\xee\xb1\xbf\xb0\xd7\xdb\xa2Cw\x12A\x94\xad\xb6\xe2\x16A$\xe4\xb4r\x1d\x0c\xeadW\xe4\n\xb1\x8f'
>>>
>>> AES.new(k, AES.MODE_CBC, iv).decrypt(ct[:16] + ct[16:32])
b'{"key": "hitcon{tmperory_flag_fo'
>>> my_unpad(AES.new(k, AES.MODE_CBC, iv).decrypt(ct[:16] + ct[16:32]))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in my_unpad
ValueError: Incorrect Padding
En lugar de modificar el segundo bloque de texto cifrado, modificaré el IV. Mi idea es poner una o
en message[6]
. En este punto, el relleno será correcto.
Haciendo un poco de trampa, sé que el índice es 6
, y que quiero una o
y tengo :
.
>>> iv[6] ^ ord('o') ^ ord(':')
112
>>> AES.new(k, AES.MODE_CBC, iv[:6] + bytes([112]) + iv[7:]).decrypt(ct[:16] + ct[16:32])
b'{"key"o "hitcon{tmperory_flag_fo'
>>> my_unpad(AES.new(k, AES.MODE_CBC, iv[:6] + bytes([112]) + iv[7:]).decrypt(ct[:16] + ct[16:32]))
b'{"key"o "hitcon{tmperory_flag_f'
Supongamos que no sabemos que necesitamos una o
, podemos iterar hasta que no recibamos un error de relleno:
>>> for b in range(256):
... try:
... my_unpad(AES.new(k, AES.MODE_CBC, iv[:6] + bytes([iv[6] ^ b ^ ord(':')]) + iv[7:]).decrypt(ct[:16] + ct[16:32]))
... print(b, chr(b))
... except ValueError:
... pass
...
b'{"key"n "hitcon{tmperory_flag_f'
110 n
b'{"key"o "hitcon{tmperory_flag_f'
111 o
Obtenemos o
(n
también es válido debido al XOR con 1).
Y podemos iterar a través de todos los índices del IV para determinar dónde está el byte de relleno en el primer bloque de texto claro:
>>> pt = b'{"key": "hitcon{'
>>>
>>> for i in range(16):
... for b in range(256):
... try:
... my_unpad(AES.new(k, AES.MODE_CBC, iv[:i] + bytes([iv[i] ^ b ^ pt[i]]) + iv[i + 1:]).decrypt(ct[:16] + ct[16:32]))
... print(i, b, chr(b))
... except ValueError:
... pass
...
b'{"key"n "hitcon{tmperory_flag_f'
6 110 n
b'{"key"o "hitcon{tmperory_flag_f'
6 111 o
En este punto, sabemos exactamente que el último byte del último bloque de texto clari es una n
o una o
.
Además, sabemos que el valor ASCII del penúltimo byte da un resto de 6
cuando se divide por N
(es decir, debe ser uno de 0x26
, 0x36
, 0x46
, 0x56
, 0x66
, 0x76
). En efecto, es f
(0x66
).
Para el siguiente byte (el antepenúltimo), debemos adivinar el último (n
ó o
) y el penúltimo (&
, 6
, F
, V
, f
o v
). Tomaremos una combinación (digamos n
, &
) e intentaremos alterar el primer bloque de texto cifrado para que haga que el segundo bloque de texto cifrado termine enn nn
y luego en &&
:
>>> pt = b'{"key": "hitcon{..............&n'
>>>
>>> padding = pt[-1]
>>> AES.new(k, AES.MODE_CBC, iv).decrypt(ct[:14] + bytes([ct[14] ^ pt[16:32][14] ^ padding]) + ct[15:16] + ct[16:32])
b'\x1b\xd1\x06;\x16{\xfe\x03NIN\xe5\xd5\xcd\x1d\x11tmperory_flag_.o'
>>>
>>> padding = pt[-2]
>>> AES.new(k, AES.MODE_CBC, iv).decrypt(ct[:15] + bytes([ct[15] ^ pt[16:32][15] ^ padding]) + ct[16:32])
b'\xf9_\xc9\x00wG\xf0C\x8aX\xa4h\xba"\x1f\xbatmperory_flag_f\''
Vaya, no se ve como se esperaba. Pero esto es correcto, ya que &n
es una suposición errónea. En su lugar, si usamos fo
como suposición, tendremos el resultado esperado:
>>> pt = b'{"key": "hitcon{..............fo'
>>>
>>> padding = pt[-1]
>>> AES.new(k, AES.MODE_CBC, iv).decrypt(ct[:14] + bytes([ct[14] ^ pt[16:32][14] ^ padding]) + ct[15:16] + ct[16:32])
b'\x98u\xbc\xc8\xba^\xfa\xe14\xd5%\x86\x1a\xa2o\xf1tmperory_flag_oo'
>>>
>>> padding = pt[-2]
>>> AES.new(k, AES.MODE_CBC, iv).decrypt(ct[:15] + bytes([ct[15] ^ pt[16:32][15] ^ padding]) + ct[16:32])
b'\xd8k\x041$Y\x84\xa66\xb2\x0eu\xdf[\x91\xa5tmperory_flag_ff'
Pero recordemos que solo tenemos un oráculo de relleno, por lo que iteraremos nuevamente sobre el IV para ver los resultados en función del relleno.
Tomando nuevamente los ejemplos anteriores, vemos estos resultados para &n
:
>>> pt = b'{"key": "hitcon{..............&n'
>>>
>>> for padding in (pt[-1], pt[-2]):
... print('Force padding to', chr(padding) * 2)
... for i in range(16):
... for b in range(256):
... try:
... _iv = iv[:i] + bytes([b]) + iv[i + 1:]
... _ct = ct[:14] + bytes([ct[14] ^ pt[16:32][14] ^ padding, ct[15] ^ pt[16:32][15] ^ padding]) + ct[16:32]
... my_unpad(AES.new(k, AES.MODE_CBC, _iv).decrypt(_ct))
... print([chr(x) for x in range(0x20, 0x7f) if x % N == i])
... except ValueError:
... pass
...
Force padding to nn
b'\x1b\xd1\x06;\x16{\xfe\x03NIN\xe5\xd5\xcdn\x11tmperory_flag_.'
['.', '>', 'N', '^', 'n', '~']
b'\x1b\xd1\x06;\x16{\xfe\x03NIN\xe5\xd5\xcdo\x11tmperory_flag_.'
['.', '>', 'N', '^', 'n', '~']
Force padding to &&
b'\xf9_\xc9\x00wG\'C\x8aX\xa4h\xba"\x1f\xbatmperory_flag_f'
['&', '6', 'F', 'V', 'f', 'v']
b'\xf9_\xc9\x00wG&C\x8aX\xa4h\xba"\x1f\xbatmperory_flag_f'
['&', '6', 'F', 'V', 'f', 'v']
Y estos para fo
:
>>> pt = b'{"key": "hitcon{..............fo'
>>>
>>> for padding in (pt[-1], pt[-2]):
... print('Force padding to', chr(padding) * 2)
... for i in range(16):
... for b in range(256):
... try:
... _iv = iv[:i] + bytes([b]) + iv[i + 1:]
... _ct = ct[:14] + bytes([ct[14] ^ pt[16:32][14] ^ padding, ct[15] ^ pt[16:32][15] ^ padding]) + ct[16:32]
... my_unpad(AES.new(k, AES.MODE_CBC, _iv).decrypt(_ct))
... print([chr(x) for x in range(0x20, 0x7f) if x % N == i])
... except ValueError:
... pass
...
Force padding to oo
b'\x98u\xbc\xc8\xba^\xfa\xe14\xd5%\x86\x1a\xa2ontmperory_flag_'
['/', '?', 'O', '_', 'o']
b'\x98u\xbc\xc8\xba^\xfa\xe14\xd5%\x86\x1a\xa2ootmperory_flag_'
['/', '?', 'O', '_', 'o']
Force padding to ff
b'\xd8k\x041$Y\x84\xa66\xb2\x0eu\xdf[\x91ftmperory_flag_'
['/', '?', 'O', '_', 'o']
b'\xd8k\x041$Y\x84\xa66\xb2\x0eu\xdf[\x91gtmperory_flag_'
['/', '?', 'O', '_', 'o']
¿Ves la diferencia? Si suponemos correctamente, el conjunto de caracteres esperados para estar en el antepenúltimo byte será el mismo (esta vez esperamos un _
, por lo que ['/', '?', 'O', '_', 'o']
tiene buena pinta).
Continuemos con el ejemplo para ?fo
y luego para _fo
:
>>> pt = b'{"key": "hitcon{.............?fo'
>>>
>>> for padding in (pt[-1], pt[-2], pt[-3]):
... print('Force padding to', chr(padding) * 3)
... for i in range(16):
... for b in range(256):
... try:
... _iv = iv[:i] + bytes([b]) + iv[i + 1:]
... _ct = ct[:13] + bytes([ct[j] ^ pt[16:32][j] ^ padding for j in range(16 - 3, 16)]) + ct[16:32]
... my_unpad(AES.new(k, AES.MODE_CBC, _iv).decrypt(_ct))
... print([chr(x) for x in range(0x20, 0x7f) if x % N == i])
... except ValueError:
... pass
...
Force padding to ooo
b'\x19\x91\x94\xd0P\xdf\xeb\x110\xd0\xb8 \t\tSntmperory_flag\x0f'
['/', '?', 'O', '_', 'o']
b'\x19\x91\x94\xd0P\xdf\xeb\x110\xd0\xb8 \t\tSotmperory_flag\x0f'
['/', '?', 'O', '_', 'o']
Force padding to fff
b'\x16\xae\xf0\xddMzf\x7f\xb2SAx\xf5^;\xa6tmperory_flag\x06'
['&', '6', 'F', 'V', 'f', 'v']
b'\x16\xae\xf0\xddMzg\x7f\xb2SAx\xf5^;\xa6tmperory_flag\x06'
['&', '6', 'F', 'V', 'f', 'v']
Force padding to ???
b'@\xcb\xa1\x16\x8f\xe2FYt\x1cy\x1b\xed\xf2o?tmperory_flag_'
['/', '?', 'O', '_', 'o']
b'@\xcb\xa1\x16\x8f\xe2FYt\x1cy\x1b\xed\xf2o>tmperory_flag_'
['/', '?', 'O', '_', 'o']
>>>
>>> pt = b'{"key": "hitcon{............._fo'
>>>
>>> for padding in (pt[-1], pt[-2], pt[-3]):
... print('Force padding to', chr(padding) * 3)
... for i in range(16):
... for b in range(256):
... try:
... _iv = iv[:i] + bytes([b]) + iv[i + 1:]
... _ct = ct[:13] + bytes([ct[j] ^ pt[16:32][j] ^ padding for j in range(16 - 3, 16)]) + ct[16:32]
... my_unpad(AES.new(k, AES.MODE_CBC, _iv).decrypt(_ct))
... print([chr(x) for x in range(0x20, 0x7f) if x % N == i])
... except ValueError:
... pass
...
Force padding to ooo
b'A\x95Y\x9d\xbcf\x1co\x90\x94\xba{\xfb\x0bn\x1etmperory_flag'
["'", '7', 'G', 'W', 'g', 'w']
b'A\x95Y\x9d\xbcf\x1cn\x90\x94\xba{\xfb\x0bn\x1etmperory_flag'
["'", '7', 'G', 'W', 'g', 'w']
Force padding to fff
b'\xc8\xd67\xcf\xbe~\x9afAf\xf7\xe0\x9eZ,\x7ftmperory_flag'
["'", '7', 'G', 'W', 'g', 'w']
b'\xc8\xd67\xcf\xbe~\x9agAf\xf7\xe0\x9eZ,\x7ftmperory_flag'
["'", '7', 'G', 'W', 'g', 'w']
Force padding to ___
b'\x9aZK\xee1\xc4\xc8^\x99\x15\xc0a\xc2/Vctmperory_flag'
["'", '7', 'G', 'W', 'g', 'w']
b'\x9aZK\xee1\xc4\xc8_\x99\x15\xc0a\xc2/Vctmperory_flag'
["'", '7', 'G', 'W', 'g', 'w']
Nuevamente, si suponemos correctamente, esperamos que todos los conjuntos de caracteres posibles sean los mismo.
Usando esto, podemos adivinar los caracteres de la flag uno a uno mediante prueba y error y un poco de intuición.
Implementación
Haremos muchas consultas al servidor, y el servidor se desconecta a los 20 segundos. Por lo tanto, usaremos el truco de Share para enviar varios payloads en la misma solicitud y recibir los resultados en la misma respuesta, y también renovaremos la conexión cuando sea necesario (obsérvese que el Padding Oracle Attack es agnóstico a la clave de AES).
Usaré estas funciones auxiliares:
def get_process():
if len(sys.argv) < 3:
return process(['python3', 'chal.py'], level='CRITICAL')
host, port = sys.argv[1], sys.argv[2]
return remote(host, port, level='CRITICAL')
def prepare():
io = get_process()
io.recvuntil(b"Anyway, here's your encrypted key: ")
enc_data = bytes.fromhex(io.recvline().decode())
io.recv()
iv, ct = enc_data[:16], enc_data[16:]
if len(sys.argv) < 3:
pt = sys.argv[1].encode()
else:
pt = sys.argv[3].encode()
assert len(pt) % 16 == 0
if len(pt) > 32:
iv = ct[(len(pt) // 16 - 3) * 16: (len(pt) // 16 - 2) * 16]
ct = ct[(len(pt) // 16 - 2) * 16:]
pt = pt[(len(pt) // 16 - 2) * 16:]
known = pt[::-1].index(b'.')
Usaré un argumento de línea de comandos para especificar el texto claro conocido, el script mostrará el conjunto de caracteres posibles del próximo carácter de texto claro desconocido.
En la función prepare
creo la conexión y formateo el IV, el texto cifrado y el texto claro para comportarse como IV más dos bloques de texto cifrado (si ya conocemos dos bloques de texto claro, entonces el IV será el primer bloque de texto cifrado).
Como en los ejemplos anteriores, uso puntos (.
) para indicar caracteres desconocidos de texto claro.
A continuación, realizamos el Padding Oracle Attack durante al último byte del bloque:
io, iv, ct, pt, known = prepare()
if known == 0:
for i in range(16):
payload = ''
for b in range(256):
_iv = iv[:i] + bytes([b]) + iv[i + 1:]
_ct = ct[:16] + ct[16:32]
payload += _iv.hex() + _ct.hex() + '\n'
io.send(payload.encode())
for b in range(256):
if b'weirdo' not in io.recvline():
print(chr(iv[i] ^ b ^ pt[i]))
io.recv()
exit()
Nótese cómo envío 256 consultas dentro de un solo mensaje y busco las que no contienen la palabra weirdo
(estas no han lanzado un error ValueError
). El resultado para este procedimiento es un conjunto de dos bytes consecutivos.
Si se conocen algunos bytes de texto claro, entonces hacemos el otro algoritmo para obtener un conjunto de 5 caracteres posibles. Si todos los conjuntos son iguales, podemos elegir uno y probar con el siguiente índice:
for k in range(1, known + 1):
padding = pt[-k]
print('Force padding to', chr(padding) * known)
for i in range(16):
payload = ''
for b in range(256):
_iv = iv[:i] + bytes([b]) + iv[i + 1:]
_ct = ct[:16 - known] + \
bytes([ct[j] ^ pt[16:32][j] ^ padding for j in range(16 - known, 16)]) + \
ct[16:32]
payload += _iv.hex() + _ct.hex() + '\n'
io.send(payload.encode())
for b in range(256):
if b'weirdo' not in io.recvline():
print([chr(x) for x in range(0x20, 0x7f) if x % N == i])
io.recv()
io.close()
io, iv, ct, pt, _ = prepare()
Flag
Empecemos a consultar:
$ H=chal-careless-padding.chal.hitconctf.com
$ P=11111
$ python3 solve.py $H $P '{"key": "hitcon{................'
w
v
Intentemos ambos caracteres posibles:
$ python3 solve.py $H $P '{"key": "hitcon{...............w'
Force padding to w
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
$ python3 solve.py $H $P '{"key": "hitcon{...............v'
Force padding to v
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
Muy bien, intentemos 4
, d
y t
con w
y con v
:
$ python3 solve.py $H $P '{"key": "hitcon{..............4w'
Force padding to ww
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
Force padding to 44
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
$ python3 solve.py $H $P '{"key": "hitcon{..............4v'
Force padding to vv
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 44
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
$ python3 solve.py $H $P '{"key": "hitcon{..............dw'
Force padding to ww
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
Force padding to dd
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
$ python3 solve.py $H $P '{"key": "hitcon{..............dv'
Force padding to vv
['&', '6', 'F', 'V', 'f', 'v']
['&', '6', 'F', 'V', 'f', 'v']
Force padding to dd
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
$ python3 solve.py $H $P '{"key": "hitcon{..............tw'
Force padding to ww
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
Force padding to tt
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
$ python3 solve.py $H $P '{"key": "hitcon{..............tv'
Force padding to vv
['&', '6', 'F', 'V', 'f', 'v']
['&', '6', 'F', 'V', 'f', 'v']
Force padding to tt
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
Parece claro que 4v
es correcto, así que continuamos con ['#', '3', 'C', 'S', 'c', 's']
:
$ python3 solve.py $H $P '{"key": "hitcon{.............#4v'
Force padding to vvv
['&', '6', 'F', 'V', 'f', 'v']
['&', '6', 'F', 'V', 'f', 'v']
Force padding to 444
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
Force padding to ###
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
$ python3 solve.py $H $P '{"key": "hitcon{.............34v'
Force padding to vvv
['&', '6', 'F', 'V', 'f', 'v']
['&', '6', 'F', 'V', 'f', 'v']
Force padding to 444
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
Force padding to 333
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
$ python3 solve.py $H $P '{"key": "hitcon{.............C4v'
Force padding to vvv
['&', '6', 'F', 'V', 'f', 'v']
['&', '6', 'F', 'V', 'f', 'v']
Force padding to 444
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
Force padding to CCC
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
$ python3 solve.py $H $P '{"key": "hitcon{.............S4v'
Force padding to vvv
['&', '6', 'F', 'V', 'f', 'v']
['&', '6', 'F', 'V', 'f', 'v']
Force padding to 444
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
Force padding to SSS
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
$ python3 solve.py $H $P '{"key": "hitcon{.............c4v'
Force padding to vvv
['&', '6', 'F', 'V', 'f', 'v']
['&', '6', 'F', 'V', 'f', 'v']
Force padding to 444
['$', '4', 'D', 'T', 'd', 't']
['$', '4', 'D', 'T', 'd', 't']
Force padding to ccc
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
$ python3 solve.py $H $P '{"key": "hitcon{.............s4v'
Force padding to vvv
['/', '?', 'O', '_', 'o']
['/', '?', 'O', '_', 'o']
Force padding to 444
['/', '?', 'O', '_', 'o']
['/', '?', 'O', '_', 'o']
Force padding to sss
['/', '?', 'O', '_', 'o']
['/', '?', 'O', '_', 'o']
Vale, entonces s4v
, Los siguientes caracteres posibles son ['/', '?', 'O', '_', 'o']
. Intentemos directamente con _
:
$ python3 solve.py $H $P '{"key": "hitcon{............_s4v'
Force padding to vvvv
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
Force padding to 4444
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
Force padding to ssss
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
Force padding to ____
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
¡Genial! Si continuamos este proceso adivinando y probando caracteres, terminaremos con:
$ python3 solve.py $H $P '{"key": "hitcon{.4dd1ng_w0n7_s4v'
Force padding to vvvvvvvvvvvvvvv
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to 444444444444444
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to sssssssssssssss
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to _______________
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to 777777777777777
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to nnnnnnnnnnnnnnn
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to 000000000000000
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to wwwwwwwwwwwwwww
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to _______________
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to ggggggggggggggg
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to nnnnnnnnnnnnnnn
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to 111111111111111
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to ddddddddddddddd
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to ddddddddddddddd
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to 444444444444444
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Dado el contexto del mensaje de la flag, podemos estar seguros de que el próximo byte es P
o p
, pero no sabemos cuál es exactamente.
Entonces, pensemos que es p
y pasemos al siguiente bloque:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v................'
a
`
Está bastante claro que el carácter correcto es a
debido a los formatos de las flags:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v...............a'
Force padding to a
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
El siguiente es r
:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v..............ra'
Force padding to aa
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Force padding to rr
[' ', '0', '@', 'P', '`', 'p']
[' ', '0', '@', 'P', '`', 'p']
Y así sucesivamente hasta que tenemos:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v._y0u_Fr0m_4_0ra'
Force padding to aaaaaaaaaaaaaaa
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to rrrrrrrrrrrrrrr
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 000000000000000
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to _______________
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 444444444444444
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to _______________
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to mmmmmmmmmmmmmmm
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 000000000000000
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to rrrrrrrrrrrrrrr
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to FFFFFFFFFFFFFFF
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to _______________
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to uuuuuuuuuuuuuuu
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 000000000000000
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to yyyyyyyyyyyyyyy
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to _______________
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Entonces no hay duda. Es un 3
.
Ahora, nuevamente hacemos el mismo proceso:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0ra................'
7
6
Y finalmente tenemos:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0ra.13_617aa68c06d7'
Force padding to 777777777777777
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to ddddddddddddddd
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 666666666666666
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 000000000000000
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to ccccccccccccccc
...
Force padding to 888888888888888
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 666666666666666
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to aaaaaaaaaaaaaaa
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to aaaaaaaaaaaaaaa
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 777777777777777
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 111111111111111
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 666666666666666
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to _______________
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 333333333333333
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Force padding to 111111111111111
['#', '3', 'C', 'S', 'c', 's']
['#', '3', 'C', 'S', 'c', 's']
Entonces, de nuevo, sabemos que podemos tener una C
o una c
. Por el momento, supongamos que es una c
.
Obsérvese que hay algunos caracteres hexadecimales al final de la flag, que no son predecibles a partir el contexto…
Esta parte fue un poco más difícil:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7................'
8
9
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7...............8'
Force padding to 8
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7...............9'
Force padding to 9
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Como estamos lidiando con dígitos hexadecimales, intentemos solo con 5
y e
:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7..............58'
Force padding to 88
['(', '8', 'H', 'X', 'h', 'x']
['(', '8', 'H', 'X', 'h', 'x']
Force padding to 55
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7..............59'
Force padding to 99
[')', '9', 'I', 'Y', 'i', 'y']
[')', '9', 'I', 'Y', 'i', 'y']
Force padding to 55
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7..............e8'
Force padding to 88
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to ee
['(', '8', 'H', 'X', 'h', 'x']
['(', '8', 'H', 'X', 'h', 'x']
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7..............e9'
Force padding to 99
[')', '9', 'I', 'Y', 'i', 'y']
[')', '9', 'I', 'Y', 'i', 'y']
Force padding to ee
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Ninguno de los resultados anteriores se ve correcto… esto sucede porque el próximo carácter también es uno de los personajes probados… Después de muchas pruebas, descubrí que e8e8
worfuncionaks:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7............e8e8'
Force padding to 8888
[')', '9', 'I', 'Y', 'i', 'y']
[')', '9', 'I', 'Y', 'i', 'y']
Force padding to eeee
[')', '9', 'I', 'Y', 'i', 'y']
[')', '9', 'I', 'Y', 'i', 'y']
Force padding to 8888
[')', '9', 'I', 'Y', 'i', 'y']
[')', '9', 'I', 'Y', 'i', 'y']
Force padding to eeee
[')', '9', 'I', 'Y', 'i', 'y']
[')', '9', 'I', 'Y', 'i', 'y']
Desde aquí, podemos continuar sin problemas hasta:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7.b91f57d1969e8e8'
Force padding to 888888888888888
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to eeeeeeeeeeeeeee
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to 888888888888888
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to eeeeeeeeeeeeeee
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to 999999999999999
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to 666666666666666
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to 999999999999999
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to 111111111111111
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to ddddddddddddddd
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to 777777777777777
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to 555555555555555
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to fffffffffffffff
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to 111111111111111
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to 999999999999999
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Force padding to bbbbbbbbbbbbbbb
['!', '1', 'A', 'Q', 'a', 'q']
['!', '1', 'A', 'Q', 'a', 'q']
Entonces, podemos estar seguros de que es un 1
o una a
.
Para el último bloque, podemos tener en cuenta que el texto claro termina con }"}
. Por lo tanto, el relleno tomará X
como 0x7d
(el valor ASCII de }
) y entonces X % N
es 13
, por lo que el byte de relleno será message[13]
del penúltimo bloque, que ya sabemos que es 8
. Así, podemos aumentar el relleno hasta que veamos un conjunto normal de valores:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d71b91f57d1969e8e8............}"}8'
Force padding to 8888
['-', '=', 'M', ']', 'm', '}']
['-', '=', 'M', ']', 'm', '}']
Force padding to }}}}
['(', '8', 'H', 'X', 'h', 'x']
['(', '8', 'H', 'X', 'h', 'x']
Force padding to """"
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
Force padding to }}}}
['(', '8', 'H', 'X', 'h', 'x']
['(', '8', 'H', 'X', 'h', 'x']
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d71b91f57d1969e8e8...........}"}88'
Force padding to 88888
['-', '=', 'M', ']', 'm', '}']
['-', '=', 'M', ']', 'm', '}']
Force padding to 88888
['-', '=', 'M', ']', 'm', '}']
['-', '=', 'M', ']', 'm', '}']
Force padding to }}}}}
['(', '8', 'H', 'X', 'h', 'x']
['(', '8', 'H', 'X', 'h', 'x']
Force padding to """""
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
Force padding to }}}}}
['(', '8', 'H', 'X', 'h', 'x']
['(', '8', 'H', 'X', 'h', 'x']
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d71b91f57d1969e8e8..........}"}888'
Force padding to 888888
['-', '=', 'M', ']', 'm', '}']
['-', '=', 'M', ']', 'm', '}']
Force padding to 888888
['-', '=', 'M', ']', 'm', '}']
['-', '=', 'M', ']', 'm', '}']
Force padding to 888888
['-', '=', 'M', ']', 'm', '}']
['-', '=', 'M', ']', 'm', '}']
Force padding to }}}}}}
['(', '8', 'H', 'X', 'h', 'x']
['(', '8', 'H', 'X', 'h', 'x']
Force padding to """"""
["'", '7', 'G', 'W', 'g', 'w']
["'", '7', 'G', 'W', 'g', 'w']
Force padding to }}}}}}
['(', '8', 'H', 'X', 'h', 'x']
['(', '8', 'H', 'X', 'h', 'x']
Hasta que conseguimos:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d71b91f57d1969e8e8...}"}8888888888'
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to 8888888888888
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to }}}}}}}}}}}}}
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to """""""""""""
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Force padding to }}}}}}}}}}}}}
['"', '2', 'B', 'R', 'b', 'r']
['"', '2', 'B', 'R', 'b', 'r']
Entonces, tenemos un 2
o una B
. Siguiendo el mismo procedimiento, tendremos:
$ python3 solve.py $H $P '{"key": "hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d71b91f57d1969e8e8.32}"}8888888888'
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 888888888888888
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to }}}}}}}}}}}}}}}
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to """""""""""""""
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to }}}}}}}}}}}}}}}
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 222222222222222
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Force padding to 333333333333333
['%', '5', 'E', 'U', 'e', 'u']
['%', '5', 'E', 'U', 'e', 'u']
Entonces, el siguiente (y último) carácter debe ser un 5
o una e
.
Después de probar todas las posibilidades (P
o p
/ C
o c
/ 1
o a
/ 5
o e
) en la página del CTF, terminamos encontrando la flag:
hitcon{p4dd1ng_w0n7_s4v3_y0u_Fr0m_4_0rac13_617aa68c06d7ab91f57d1969e8e8532}
El script completo se puede encontrar aquí: solve.py
.