# Locked Dungeon 2### Points : 498### Solved by 25 Teams
Here we get python script :
```python#!/usr/bin/env python2.7
from hashlib import sha256, md5from Crypto.Cipher import AESimport osimport randomfrom binascii import hexlify, unhexlifyfrom base64 import b64decode, b64encodeimport sys
BLOCK_SIZE = 16KEY = os.urandom(16)IV = os.urandom(16)
pad_len = lambda inp: (BLOCK_SIZE - len(inp) % BLOCK_SIZE)pad = lambda inp: inp + chr(pad_len(inp))*pad_len(inp)unpad = lambda inp: inp[:-ord(inp[-1])]
class AESCipher:    def __init__(self, key):        self.key = sha256(key).digest()
    def __encrypt(self, _str, iv):        cipher = AES.new(self.key, AES.MODE_CBC, iv)        padded_str = pad(_str)        return cipher.encrypt(padded_str)     def encrypt_wrapper(self, _str, iv):        return b64encode(iv + self.__encrypt(_str, iv))
    def __decrypt(self, enc_str, iv):        cipher = AES.new(self.key, AES.MODE_CBC, iv)        decrypted_str = cipher.decrypt(enc_str)        decrypted_str = unpad(decrypted_str)        return decrypted_str
    def decrypt_wrapper(self, encoded_enc_str):        enc_str = b64decode(encoded_enc_str)        return self.__decrypt(enc_str[16:], enc_str[:16])
if __name__ == "__main__":    with open("flag.txt") as fd:        flag = fd.read()    flag_size = len(flag)    key=KEY    insertion_range = flag_size//BLOCK_SIZE    insertion_position = random.randrange(insertion_range)*BLOCK_SIZE    mod_flag = flag[:insertion_position] + "send_modflag_enc" + flag[insertion_position:]    aescipher = AESCipher(key)    enc_mod_flag = aescipher.encrypt_wrapper(mod_flag, IV)    sys.stdout.write(enc_mod_flag)    sys.stdout.write('\n')    sys.stdout.flush()    next_level = False    for i in range(insertion_range):        sys.stdout.write("What do you want me to do?\n")        sys.stdout.flush()        enc_recv_str = raw_input()        dec_recv_str = aescipher.decrypt_wrapper(enc_recv_str)        print dec_recv_str        if "get_modflag_md5" in dec_recv_str:            next_level = True            sys.stdout.write("Dungeon goes deeper..\n")            sys.stdout.flush()            break        else:            sys.stdout.write("I am gonna ask again!\n")            sys.stdout.flush()
    if next_level:        len_enc_mod_flag = len(enc_mod_flag)        inp_size_limit = int(len_enc_mod_flag*4/3) + 50        for i in xrange(500):            enc_recv_str = raw_input()            if len(enc_recv_str) > inp_size_limit:                continue            dec_recv_str = aescipher.decrypt_wrapper(enc_recv_str)            sys.stdout.write(b64encode(md5(dec_recv_str).digest()))            sys.stdout.write("\n")            sys.stdout.flush()```
So basically here we given a script of encryption,the first step is to modify dec_recv_str to contain string `get_modflag_md5` instead of `send_modflag_enc` .Encryption using AES-CBC so,IV is applied after the Block-Key Encryption .We can modify our IV payload with `new_iv` = `target string` ^  `real string` ^ `old_iv` .Using this `new_iv` as our payload,we can get to the `The next_level` .This only can work if result of `insertion_position = random.randrange(insertion_range)*BLOCK_SIZE` is 0.So we need to retry multiple times.
After that in `next_level` we can bruteforce flag ber byte using md5 encryption.It works if we can adjust the last byte of IV to desired pad because padding of message use `unpad = lambda inp: inp[:-ord(inp[-1])]` .It caused we can easily generate first byte of message until n-1 length of message per process(16 byte).We can only get 15 out of 16 character of flag per process cause `[:-ord(inp[-1])]` cannot get the entire message.Use some bruteforce after that and get the flag.
## Solve.py```pythonfrom pwn import *from base64 import *import hashlib
flag=[]tes=16tes2=32jadi=''while True:    try:        p = remote("chal1.swampctf.com" ,1460)        # p = process('./dungeon2.py')        data = p.recvline()[:-1]        data = b64decode(data)
        iv = data[:16]        awal = data[16:32]        mula = "send_modflag_enc"        target = "get_modflag_md5\x01"
        iv_baru =''        for i in range(16):                         #change IV to make decrypted string to "target" from "mula"            iv_baru += chr(ord(target[i]) ^ ord(iv[i]) ^ ord(mula[i]))
        payload = iv_baru + awal        payload = b64encode(payload)
        p.recv(1024)        p.sendline(payload)        lol2 = p.recv(1024)
        if len(jadi) == 15:         #max 15 byte flag per process            flag.append(jadi)            print flag            tes+=16            tes2+=16            jadi = ''
        if len(flag) == 3:          #get the entire flag,the last 16 byte blocks are just padding             break
        if "Dungeon goes deeper" in lol2 :            print "Succes Entering Dungeon,processing..."            for j in range(256):                            #Bruteforce last byte IV to search flag per byte using md5                payload2 = data[tes:tes2][:-1] + chr(j) + data[tes2:tes2+16]                p.sendline(b64encode(payload2))                hasil = p.recvline()[:-1]                for k in range(0x20,0x7f):                    if hashlib.md5(jadi+chr(k)).digest() == b64decode(hasil):                        print "Get flag byte :" , chr(k)                        jadi+=chr(k)                        break                if len(jadi)==15:                    break            print "Piece of flag gathered per process : ",jadi        else:            print "Can't enter,try again ..."        p.close()    except:        print "Service Error :("        pass
#After we got 3 separate flag string,we need to join them and bruteforce some word#cause every process we only can get 15/16 character
#Possible Flag :# flag{Ev3n_dunge --> 0 or o or O# ns_are_un5af3_w --> i or I or 1# th_vu1n_padding --> we know last char is } , so just need brute 2 wordprint flagprint "Possible FLAG :"for i in ("0oO"):    for j in ("1iI"):        print flag[0] + i + flag[1] + j + flag[2] + "}"```
### Wrote by : Yeraisci77