### General idea
We had a server, which encrypt json file with AES-CBC with unusual padding, send it to us with IV and let us change it by sending encrypted bytes back.Besides, server returns us different types of mistakes: decoding error and padding error.
json format: `{'admin': False, 'flag': open("flag1.txt").read()}`
We need to get 2 flags ( actually two parts of one ):
1st one - from json field 'flag'
2nd one will be given when we change `admin` from `False` to `True`
#### 1st flag
First of all, let's analyze json and split it into blocks. We will get something like:
```py['\x02 {"admin": fals', 'e, "flag": "TetC','TF{***********"}']```Now it is clear that we need only last block.
Attack is very similar to [classical Padding-Oracle Attack on AES-CBC](https://robertheaton.com/2013/07/29/padding-oracle-attack/), but has some features, because:
1) padding function is unusual:
```pyPAD = bytes.fromhex("2019") * 8
def pad(s):    pad_length = 16 - len(s) % 16    return bytes([pad_length]) + PAD[:pad_length - 1] + s```
2) plain text is padded at the beginning
To get to know 1st part of flag, we will send changed 3rd (penultimate, it will be IV for server) and 4st (last, it will be ciper-text) block of ciper-text.
We start from 1st byte of 3rd block.
1) Change the byte to \x002) Send the blocks to server3) If we get answer 'incorrect padding', we change the byte to the next value and go to step 2. 4) If we got another answer, it means that we can calculate the byte from original text (flag byte)5) Store this byte and go to change 2nd byte
This way we can get all block and recover the first flag byte by byte.
#### 2nd flag
We want make `obj['admin']` == `True`. Here server doesn't check flag accuracy, so we can just change `obj` to `{"admin": true}`. This json will take one block with minimal padding (`\x01`).
We will send two blocks: `IV^(original_2nd_block_pt)^(desired_2nd_block_pt)` and `encrypted 2nd block`
We don't know padding value in original plaintext, so we will try several variant until attack is successful. (`'\x02 '` will be correct)
### Exploits
#### 1st
```pyimport jsonfrom os import urandomfrom pwn import remote, processfrom string import ascii_letters, digitsfrom itertools import product
ADDR = "207.148.119.58", 5555PAD = (("2019") * 8).decode('hex')
def get_paddings_dict(n):    ans = {}    for i in range(n):        ans[i] = pad(i+1)    return ans
def pad(n):    pad_length = n    return chr(pad_length) + PAD[:pad_length - 1]
def crack_byte(token, pos, i):    token[pos] = i    return ''.join('{:02x}'.format(x) for x in token)
def find_pad(r, token, pos, last_x):    '''    get bytes    '''    token = bytearray(token)    padings = get_paddings_dict(pos+1)    if pos:        token[0] = last_x[0] ^ ord(padings[0][0]) ^ ord(padings[pos][0])        for j in range(1, pos):            token[j] = last_x[j]
    for i in range(256):        payload = crack_byte(token, pos, i)        r.sendline(payload)        ans = r.recvline()        if i % 64 == 0:            print("current step: ", pos, i, ans)        if 'padding' in ans:            continue        else:            return i    raise Exception("All padings are incorrect")
if __name__ == '__main__':    #r = remote(*ADDR)    r = process('./padwith2019.py')    token_hex = r.recvline(False)    print(token_hex)    token = token_hex.decode('hex')    parts = [token[i:i+16] for i in range(0, len(token), 16)]    known = "TF{***********"    flag = ''    exp_pad = pad(1)    c1 = parts[2]    c2 = parts[3]    last_x = []    for i in range(len(known)):        exp_pad = pad(i+1)        x = find_pad(r, c1+c2, i, last_x)        i2 = x ^ ord(exp_pad[i])        ch = chr(i2 ^ ord(c1[i]))        if i < len(known) and ch == known[i]:            flag += ch            print("Horay!", flag)            last_x.append(x)        else:            flag += ch            print("Is it right?", flag)            last_x.append(x)    print('TetC' + flag[:-2])```
#### 2nd
```pyimport jsonfrom os import urandomfrom pwn import remote, processfrom string import ascii_letters, digitsfrom itertools import product
ADDR = "207.148.119.58", 5555
def crack(token):    test_token = bytearray(token)
    test = b'\x02 {"admin": fals'    for i, (x, y) in enumerate(zip(test, b'\x01{"admin": true}')):        test_token[i] ^= ord(x) ^ ord(y)
    return ''.join('{:02x}'.format(x) for x in test_token[:32])
if __name__ == '__main__':    #r = remote(*ADDR)    r = process('./padwith2019.py')    token = r.recvline(False).decode('hex')    new_token = crack(token)    r.sendline(new_token)    r.interactive()```
