New Caesar
We found a brand new type of encryption, can you break the secret code? (Wrap with picoCTF{}) apbopjbobpnjpjnmnnnmnlnbamnpnononpnaaaamnlnkapndnkncamnpapncnbannaapncndnlnpna new_caesar.py
So we are given a script new_caesar.py, let's analyse it.
LOWERCASE_OFFSET = ord("a")
ALPHABET = string.ascii_lowercase[:16]
[...]
flag = "redacted"
key = "b"
assert all([k in ALPHABET for k in key])
assert len(key) == 1
b16 = b16_encode(flag)
enc = ""
for i, c in enumerate(b16):
enc += shift(c, key[i % len(key)])
print(enc)So first off we know that the ALPHABET is the first 16 letters in the lowercase alphabet. The key is one character long, and is also in ALPHABET. The flag has b16_encode() run on it, and every letter in the result of that is run under shift(). To reverse, it, we have to first undo shift() and then b16_encode().
def b16_encode(plain):
enc = ""
for c in plain:
binary = "{0:08b}".format(ord(c))
enc += ALPHABET[int(binary[:4], 2)]
enc += ALPHABET[int(binary[4:], 2)]
return enc
def shift(c, k):
t1 = ord(c) - LOWERCASE_OFFSET
t2 = ord(k) - LOWERCASE_OFFSET
return ALPHABET[(t1 + t2) % len(ALPHABET)]shift() is a very simple function, although the arithmetic looks a bit strange. t1 is actually the index of c in ALPHABET, as it grabs the character code and then subtracts off that of a. t2 is the same, but for the key. t2 is then added as a shift to t1 and the index in ALPHABET is returned, so it's essentially a Caesar with a shift dictated by k. We can undo this easily, by subtracting instead of adding:
def unshift(p, k):
t1 = ord(p) - LOWERCASE_OFFSET
t2 = ord(k) - LOWERCASE_OFFSET
return ALPHABET[(t1 - t2) % len(ALPHABET)]As for b16encode(), it calculates the 8-bit binary representation of the character code and then splits it into two 4-bit values, which are used as indices in ALPHABET. We can undo this too.
def b16_decode(enc):
enc = [enc[x:x + 2] for x in range(0, len(enc), 2)] # split into pairs
dec = ''
for pair in enc:
bin1 = ALPHABET.index(pair[0])
bin2 = ALPHABET.index(pair[1])
combined = int(bin(bin1)[2:].zfill(4) + bin(bin2)[2:].zfill(4), 2)
dec += chr(combined)
return decWe then just have to brute force for every possible key in ALPHABET, getting 16 possibilities:
ÐÒÓÛßÐ
ÈèËÌËÊÀûÎÍÍÎÏÿûÊÉþÂÉÁûÎþÁÀüÏþÁÂÊÎÏ
íü×üý·×º»º¹¿ê½¼¼½¾îê¹¸í±¸°ê½í°¿ë¾í°±¹½¾
ÜëÆëì¦Æ©ª©¨®Ù¬««¬ÝÙ¨§Ü §¯Ù¬Ü¯®Úܯ ¨¬
ËÚµÚÛµÈÌÈËÈËÉË
ºÉ¤Éʤ·»·
º
·º¸º
©¸¸¹svwvu{¦yxxyzª¦ut©}t|¦y©|{§z©|}uyz
§§¨befedjhgghidclckhkjikldhi
qQqTUTSYWVVWXSR[RZWZY
XZ[SWX
v
`
@`CDCBHsFEEFGwsBAvJAIsFvIHtGvIJBFG
et_tu?_23217b54456fb10e908b5e87c6e89156
TcNcd.N!"! &Q$##$%UQ /T(/'Q$T'&R%T'( $%
CR=RS=@D@C@CAC
?202
!001ûþÿþýó.ñððñò".ýü!õüô.ñ!ôó/ò!ôõýñò
/
/ ê
íîíìâàïïàáìëäëãàãâáãäìàá
Process finished with exit code 0
The only one that looks right is et_tu?_23217b54456fb10e908b5e87c6e89156 (given the title), and we submit that as the flag.
Full script:
import string
LOWERCASE_OFFSET = ord("a")
ALPHABET = string.ascii_lowercase[:16]
def unshift(p, k):
t1 = ord(p) - LOWERCASE_OFFSET
t2 = ord(k) - LOWERCASE_OFFSET
return ALPHABET[(t1 - t2) % len(ALPHABET)]
def b16_decode(enc):
enc = [enc[x:x + 2] for x in range(0, len(enc), 2)]
dec = ''
for pair in enc:
bin1 = ALPHABET.index(pair[0])
bin2 = ALPHABET.index(pair[1])
combined = int(bin(bin1)[2:].zfill(4) + bin(bin2)[2:].zfill(4), 2)
dec += chr(combined)
return dec
enc_flag = 'apbopjbobpnjpjnmnnnmnlnbamnpnononpnaaaamnlnkapndnkncamnpapncnbannaapncndnlnpna'
for key in ALPHABET:
shifted_flag = ''
for i, p in enumerate(enc_flag):
shifted_flag += unshift(p, key)
d = b16_decode(shifted_flag)
print(d)
# picoCTF{et_tu?_23217b54456fb10e908b5e87c6e89156}Last updated
Was this helpful?