# N-AES

So, at first this seems literally impossible.

The rand\_block function takes a random 1 byte seed(initialising it to the output of `os.urandom(1)` if the argument isnt given) and then generates a 16-byte random block from this.

The encryption is simple - given a stream of seed bytes and a plaintext, iterate through the seed bytes.

On each byte, set the current ciphertext variable to the current ciphertext variable encrypted with the key generated by the random block that is create using `rand_block(cur_byte_of_seed)`

Finally, the challenge is generated like so. A random base64 string is generated using urandom, then encrypted in the algorithm. We get the base64 of the encryption.

```python
def gen_chall(text):
    text = pad(text, BLOCK_SIZE)
    for i in range(128):
        text = AES.new(rand_block(), AES.MODE_ECB).encrypt(text) # VULN

    return b64encode(text)
```

So, what's so fishy about this?

Notice no argument is passed to rand\_block, it simply gets a rand block and encrypts 128 times.

The argument is supposed to be `os.urandom(1)` by default, right? The problem is, everytime you call the function with no argument, the os.urandom(1) isnt regenerated. Instead, the value is first generated when the function is defined and then set to that again and again and again.

Long story short - chall is generated with the same key 128 times. Since it's os.urandom(1), we'll only have to bruteforce 256 possible keys. That can be done easily, and we can check if we've decrypted correctly by checking if the padding matches.

From there, we can simply enter the decrypted ciphertext.

```python
import os
os.environ['TERM'] = 'linux'
os.environ['TERMINFO'] = '/etc/terminfo'
from pwn import *
from base64 import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
from os import urandom
from random import seed,randint
BLOCK_SIZE = 16
def rand_block(key_seed=urandom(1)):
    seed(key_seed)
    return bytes([randint(0, 255) for _ in range(BLOCK_SIZE)])
def gen_chall(text):
    text = pad(text, BLOCK_SIZE)
    for i in range(128):
        text = AES.new(rand_block(), AES.MODE_ECB).encrypt(text)
    return b64encode(text)
p = remote('167.172.123.213', 34567)
enc = p.recvline()[:-1]
#print(enc)
for byte in range(256):
    text = base64.b64decode(enc)
    for i in range(128):
        text = AES.new(rand_block(bytes([byte])), AES.MODE_ECB).decrypt(text)
    try:
        text = unpad(text,16)
        print(base64.b64encode(text))
    except:
        pass
p.interactive()
```

^ script prints out the needed base64, you'll have to manually enter it into the prompt to get the flag from there

## Flag: rgbCTF{i\_d0nt\_7hink\_7his\_d03s\_wh47\_y0u\_7hink\_i7\_d03s}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://the-winrars.gitbook.io/writeups/2020-writeups/rgbctf/crypto/n-aes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
