def detect_mode(enc_function): sample = enc_function(b"A" * 256) blocks = to_blocks(sample, 16) # We should've made at least 14 identical blocks of all "A"'s if len(blocks) - len(set(blocks)) >= 14: return AES.MODE_ECB else: return AES.MODE_CBC
def aes_ctr(key, nonce, data): crypt = AES.new(key, AES.MODE_ECB) blocks = to_blocks(data, 16) plaintext = b'' for i in range(0, len(blocks)): f = struct.pack("<qq", nonce, i) k = crypt.encrypt(f) plaintext += xorstr(blocks[i], k) return plaintext
def cbc_decrypt(data, key, iv): crypt = AES.new(key, AES.MODE_ECB) blocks = [iv] + to_blocks(data, len(key)) blocks = [ xorstr(crypt.decrypt(blocks[i]), blocks[i - 1]) for i in range(1, len(blocks)) ] plaintext = b"".join(blocks) assert check_padding(plaintext) return plaintext
def padding_oracle_attack(ciphertext, oracle): def padding_oracle(c1, c2): token = b"".join([c1,c2]) try: return oracle(token) except: return False blocks = to_blocks(ciphertext, 16) known = b'' for i in range(len(blocks)-1): known += padding_oracle_block(blocks[i], blocks[i+1], padding_oracle) return known
def detect_ecb(data, blocksize): blocks = to_blocks(data, 16) if len(blocks) != len(set(blocks)): return True return False
def check_admin(token): global key crypt = AES.new(key, AES.MODE_CBC, token[0:16]) plaintext = unpad(crypt.decrypt( token[16:])) # oh no, padding oracles! (for later) pairs = [i.rsplit(b"=") for i in plaintext.rsplit(b";")] return ([b"admin", b"true"] in pairs) ########################################################## # Generate a token, what is in it doesn't really matter the way we're # doing it, since we know what lives in the last block - let's just make it # "TOKEN," because that makes us land on an even padding :) token = gen_token(b"TOKEN") # The last two blocks are going to be filled with, like, a pound of bacon # so we know what they are, and we know we can edit them at will without # really breaking anything important. blocks = to_blocks(token, 16) bits_to_flip = xorstr(b";admin=true;A=A\x01", b"nd%20of%20bacon\x01") blocks[-2] = xorstr(blocks[-2], bits_to_flip) token = b"".join(blocks) print(check_admin(token))
# Boundry is how far we have to go to force it into a new block # # "email=AAAAAAAAAA|&uid=10&role=use|r boundry, blocksize = get_blocksize_and_boundry(encrypted_profile_for) # Everything after boundry gets put into a new block, so we can control # a whole block this way, in this case, our block will be "admin&uid=10&rol" # which is close enough for urldecoding! # # "email=AAAAAAAAAA|admin&uid=10&rol|e=user payload = (b"A" * boundry) + b"admin" admin_block = to_blocks(encrypted_profile_for(payload), blocksize)[1] # Now we push off just enough to get the "user" alone in the last block # So, "A"*(len("user"))-1 will do exactly that, and give us: # # "email=AAAAAAAAAA|AAA&uid=10&role=|user payload = b'A' * (boundry + len("user") - 1) blocks = to_blocks(encrypted_profile_for(payload), blocksize)[:-1] # Then, swap the last block with our new one that starts with "admin" # # "email=AAAAAAAAAA|AAA&uid=10&role=|admin&uid=10&rol blocks.append(admin_block) modified_payload = b"".join(blocks)
def get_boundry(enc_function, blocksize): for i in range(blocksize,blocksize*3): b = to_blocks(enc_function(b"A"*i), blocksize) for x in range(len(b)-1): if b[x]==b[x+1]: return (x+2)*blocksize, i