92 lines
2.6 KiB
Python
92 lines
2.6 KiB
Python
'''
|
|
Solution for https://cryptohack.org/courses/symmetric/bean_counter/
|
|
'''
|
|
|
|
import os
|
|
import requests
|
|
from math import ceil
|
|
|
|
from Crypto.Cipher import AES
|
|
|
|
from celeste.math.util import xor_bytes
|
|
|
|
class StepUpCounter(object):
|
|
def __init__(self, step_up=False):
|
|
self.value = os.urandom(16).hex()
|
|
self.step = 1
|
|
self.stup = step_up
|
|
|
|
def increment(self):
|
|
if self.stup:
|
|
self.newIV = hex(int(self.value, 16) + self.step)
|
|
else:
|
|
self.newIV = hex(int(self.value, 16) - self.stup)
|
|
self.value = self.newIV[2:len(self.newIV)]
|
|
return bytes.fromhex(self.value.zfill(32))
|
|
|
|
def __repr__(self):
|
|
self.increment()
|
|
return self.value
|
|
|
|
'''
|
|
NOTE: Since step_up is used ONLY as false, we can simplify:
|
|
class StepUpCounter(object):
|
|
def __init__(self):
|
|
self.value = os.urandom(16).hex()
|
|
|
|
# SAME AS DOING NOTHING
|
|
def increment(self):
|
|
return self.value()
|
|
|
|
def __repr__(self):
|
|
return self.value
|
|
'''
|
|
|
|
URL = 'https://aes.cryptohack.org/bean_counter'
|
|
|
|
def get_encrypted() -> bytes:
|
|
resp = requests.get(f'{URL}/encrypt/')
|
|
encrypted = bytes.fromhex(resp.json()['encrypted'])
|
|
return encrypted
|
|
|
|
PNG_HEADER = [
|
|
'89', '50', '4e', '47',
|
|
'0d', '0a', '1a', '0a',
|
|
'00', '00', '00', '0d',
|
|
'49', '48', '44', '52'
|
|
]
|
|
|
|
def main() -> None:
|
|
# NOTE: the counter is constant!
|
|
ctr = StepUpCounter()
|
|
# init = ctr.increment()
|
|
# init_val = ctr.value
|
|
# for i in range(2560000):
|
|
# if ctr.increment() != init:
|
|
# print('Counter Changed')
|
|
# print(i)
|
|
# break
|
|
# elif ctr.value != init_val:
|
|
# print('valued changed')
|
|
# print(i)
|
|
# break
|
|
|
|
# PNGs *should* have a constant header, we will use the first 16 bytes
|
|
# to determine what the counter value must be set to
|
|
encrypted = get_encrypted()
|
|
png_header = bytes.fromhex(''.join(PNG_HEADER))
|
|
ctr_value = xor_bytes(encrypted[:16], png_header)
|
|
# apply the counter value to the entire encrypted image (in blocks of 16 bytes)
|
|
BLOCK_SIZE = 16
|
|
with open('bean_flag.png', 'wb') as f:
|
|
for i in range(ceil(len(encrypted) / BLOCK_SIZE)):
|
|
block = encrypted[i*BLOCK_SIZE : (i+1)*BLOCK_SIZE]
|
|
# NOTE: the block we get is not guaranteed to be block size (ie if at end)
|
|
plaintext_block = xor_bytes(block, ctr_value[:len(block)])
|
|
f.write(plaintext_block)
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
main()
|
|
except (KeyboardInterrupt, EOFError):
|
|
print('\n[!] Interrupt')
|