added default.nix and various ctf examples
This commit is contained in:
parent
b9c5a5bf3e
commit
89973b803c
6 changed files with 197 additions and 1 deletions
92
ctf-solutions/bean_counter.py
Normal file
92
ctf-solutions/bean_counter.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
'''
|
||||
Solution for https://cryptohack.org/courses/symmetric/bean_counter/
|
||||
'''
|
||||
|
||||
import os
|
||||
import requests
|
||||
from math import ceil
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
from imp.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')
|
||||
55
ctf-solutions/flipping_cookie.py
Normal file
55
ctf-solutions/flipping_cookie.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
'''
|
||||
Solution to https://cryptohack.org/courses/symmetric/flipping_cookie/
|
||||
'''
|
||||
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
URL = 'https://aes.cryptohack.org/flipping_cookie'
|
||||
|
||||
# NOTE: assumes A and B are equal length
|
||||
def xor_bytes(A: bytes, B: bytes) -> bytes:
|
||||
return b''.join([(a ^ b).to_bytes() for (a, b) in zip(A, B)])
|
||||
def xor_str(A: str, B: str) -> str:
|
||||
return ''.join([chr(ord(a) ^ ord(b)) for (a, b) in zip(A, B)])
|
||||
|
||||
def gen_expiry() -> str:
|
||||
return (datetime.today() + timedelta(days=1)).strftime("%s")
|
||||
|
||||
def get_cookie() -> tuple[bytes, bytes]:
|
||||
resp = requests.get(f'{URL}/get_cookie/')
|
||||
cookie = resp.json()['cookie']
|
||||
iv = bytes.fromhex(cookie[:32])
|
||||
ciphertext = bytes.fromhex(cookie[32:])
|
||||
return iv, ciphertext
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# cookie flipping preprocessing step
|
||||
admin_len = len('admin=')
|
||||
expiry_len = len(';expiry=') + len(gen_expiry())
|
||||
admin_mask = '\x00' * admin_len
|
||||
expiry_mask = '\x00' * expiry_len
|
||||
# we aim to replace "admin=False;" with "admin=True;;"
|
||||
# NOTE: double semicolon ("True;;") is intentional
|
||||
# NOTE: and the server won't ever realise it happened!
|
||||
deletion = admin_mask + 'False' + expiry_mask
|
||||
insertion = admin_mask + 'True;' + expiry_mask
|
||||
# determine the value that replaces deletion with insertion
|
||||
cookie_flip = xor_str(deletion, insertion)
|
||||
|
||||
# get our new cookie and apply the cookie flip!
|
||||
iv, ciphertext = get_cookie()
|
||||
flipped_iv = xor_bytes(cookie_flip.encode(), iv)
|
||||
|
||||
print('Flipped Cookie:')
|
||||
print('IV:', flipped_iv.hex())
|
||||
print('Body:', ciphertext.hex())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
39
ctf-solutions/symmetry.py
Normal file
39
ctf-solutions/symmetry.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
'''
|
||||
Solution to https://cryptohack.org/courses/symmetric/symmetry/
|
||||
'''
|
||||
|
||||
import os
|
||||
import requests
|
||||
|
||||
from imp.math.util import xor_bytes, xor_str
|
||||
|
||||
URL = 'https://aes.cryptohack.org/symmetry'
|
||||
|
||||
def get_encrypted_flag() -> tuple[bytes, bytes]:
|
||||
resp = requests.get(f'{URL}/encrypt_flag/')
|
||||
ciphertext = resp.json()['ciphertext']
|
||||
iv = bytes.fromhex(ciphertext[:32])
|
||||
flag = bytes.fromhex(ciphertext[32:])
|
||||
return iv, flag
|
||||
|
||||
def encrypt(plaintext: bytes, iv: bytes) -> bytes:
|
||||
plaintext, iv = plaintext.hex(), iv.hex()
|
||||
resp = requests.get(f'{URL}/encrypt/{plaintext}/{iv}')
|
||||
return bytes.fromhex(resp.json()['ciphertext'])
|
||||
|
||||
def main() -> None:
|
||||
iv, flag = get_encrypted_flag()
|
||||
# generate a random plaintext of equal length to the flag
|
||||
random_plaintext = os.urandom(len(flag))
|
||||
random_ciphertext = encrypt(random_plaintext, iv)
|
||||
# XOR random plaintext with corresponding ciphertext to
|
||||
# get the set generated by IV under the keyed AES permutation
|
||||
aes_orbit = xor_bytes(random_plaintext, random_ciphertext)
|
||||
# XOR this orbit with the flag to get the hexed flag plaintext
|
||||
flag_plaintext = xor_bytes(flag, aes_orbit)
|
||||
print('Flag:', flag_plaintext.decode())
|
||||
|
||||
print('IV:', iv.hex())
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
10
default.nix
Normal file
10
default.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
let
|
||||
sources = import ./nix/sources.nix;
|
||||
pkgs = import sources.nixpkgs {};
|
||||
# Let all API attributes like "poetry2nix.mkPoetryApplication"
|
||||
# use the packages and versions (python3, poetry etc.) from our pinned nixpkgs above
|
||||
# under the hood:
|
||||
poetry2nix = import sources.poetry2nix {inherit pkgs;};
|
||||
myPythonApp = poetry2nix.mkPoetryApplication {projectDir = ./.;};
|
||||
in
|
||||
myPythonApp
|
||||
|
|
@ -34,7 +34,7 @@ def encrypt(b: bytes) -> bytes:
|
|||
return bytes.fromhex(resp['ciphertext'])
|
||||
|
||||
def main() -> None:
|
||||
paddingoracle.crack(encrypt, pad, CHARSET, 64, batch_size=2, debug=True)
|
||||
paddingoracle.crack(encrypt, pad, CHARSET, 16, batch_size=20, debug=True)
|
||||
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue