# Challenge example

## Code source example

```python
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import json

FLAG = ?
KEY = ?

def admin():
    print("welcome admin")
    print(f"Flag = {FLAG}")


def guest():
    print("welcome guest")


def auth():
    user = dict()
    user["username"] = input("username : ")
    user["password"] = input("password : ")
    user["uid"] = int(user["username"] == "admin" and user["password"] == FLAG)
    
    plaintext = json.dumps(user).encode()
    padded = pad(plaintext, 16)
    cipher = AES.new(KEY, AES.MODE_ECB)
    try:
        encrypted = cipher.encrypt(padded)
    except ValueError as e:
        return {"error": str(e)}

    print(f"Here is your token : {encrypted.hex()}")

    return user


def authToken():
    token = input("Token : ")
    token = bytes.fromhex(token)

    cipher = AES.new(KEY, AES.MODE_ECB)
    try:
        plaintext = cipher.decrypt(token)
        unpadded = unpad(plaintext, 16)
        user = json.loads(unpadded)
    except ValueError as e:
        return {"error": str(e)}

    return user


print("Welcome !")
print("Please login first.")

choice = input("How would you login ?\n1) Token\n2) Credentials\n\nChoice :")

if choice == "1":
    user = authToken()
elif choice == "2":
    user = auth()
else:
    print("error... goodbye")
    exit(0)

if "error" in user:
    print(f"ERROR : {user['error']}")
    exit(0)

elif user["uid"] == 1:
    admin()
else:
    guest()
```

The challenge objectif is to log as admin user.

## Exploitation

When the user authenticates using their credentials, the application will send a token that can be reuse to directly access the app.

The token contain :

* `username` : provided by the user
* `password` : provided by the user
* `uid` : set by the application depending if the provided credentials are admin or not.

Can the user forge an arbitrary block ?

{% code overflow="wrap" %}

```bash
$ python3 -c 'print("2\n" + "A"*2 + "A"*16*2 + "\na\n")' | python3 chall.py
# Sending choice 2 to authenticate using credentials
# Sending as username : 2 bytes to complete the first block + 16 bytes * 2 to check if the block are correctly forged ( second and third block may be the same ) 
# Sending anything (a) as password... this pars isn't needed to exploit. 

Welcome !
Please login first.
How would you login ?
1) Token
2) Credentials

Choice :username : password : Here is your token : 0f0db6ff7eb32259e2ab26faad5bea05eb159765773a70532da4789b0305a592eb159765773a70532da4789b0305a59265c235f39040a271d8e612fcb3cf0e3863e9de070bb91f9a2714f8b733371020
welcome guest

# Checking Token : 
>>> result[32:64]
'eb159765773a70532da4789b0305a592'
>>> result[64:96]
'eb159765773a70532da4789b0305a592'
```

{% endcode %}

The user can effectively forge arbitrary blocks.

In order to obtain admin access the user need to replace the block containing `'uid': 0`

Here we will manipulate the input to have this at the end of the penultimate block :

```
+------------------+------------------+------------------+--------------+
| {'username': "AA | ", 'password': ' | AAAAAA', 'uid':  | 0} + padding |
+------------------+------------------+------------------+--------------+
```

{% code overflow="wrap" %}

```bash
$ python3 -c 'print("2\n" + "A"*2 + "\n" + "A"*6 + "\n")' | python3 chall.py
Welcome !
Please login first.
How would you login ?
1) Token
2) Credentials

Choice :username : password : Here is your token : 0f0db6ff7eb32259e2ab26faad5bea05c5f31a2e59a38743077acd22e4512d69eb6c532e83118ae9a89f176933e2cd0f7e8c582ca3535bc548f256f69832be4f
welcome guest
```

{% endcode %}

We now need to forge the block `1`

{% code overflow="wrap" %}

```bash
$ python3 -c 'print("2\n" + "A"*2 + " "*15 + "1" +  "\n" + "A"*6 + "\n")' | python3 chall.py
Welcome !
Please login first.
How would you login ?
1) Token
2) Credentials

Choice :username : password : Here is your token : 0f0db6ff7eb32259e2ab26faad5bea050ebd6b5fd3363bc885c982fd44c1e7f3c5f31a2e59a38743077acd22e4512d69eb6c532e83118ae9a89f176933e2cd0f7e8c582ca3535bc548f256f69832be4f
welcome guest
```

{% endcode %}

We need a last block :

{% code overflow="wrap" %}

```bash
# } + padding
$ python3 -c 'print("2\n" + "A"*2 + "\n" + "A"*5 + "\n")' | python3 chall.py
Welcome !
Please login first.
How would you login ?
1) Token
2) Credentials

Choice :username : password : Here is your token : 0f0db6ff7eb32259e2ab26faad5bea05c5f31a2e59a38743077acd22e4512d6907687b0f26a0485b8e70f508c3c2902b6e1373a93bd585792817170366e57f43
welcome guest
```

{% endcode %}

Now to obtain the admin access we need to replace the last block of the first token (containing `0} + padding`) by the second block of the second token ( containing `1`) and concat the last block of the last token ( containing `} + padding` )

{% hint style="info" %}
At this moment the json value will be :

{% code overflow="wrap" %}

```
{"username": "AA", "password": "AAAAAA", "uid":                1}
```

{% endcode %}
{% endhint %}

{% code overflow="wrap" %}

```python
$ python3
>>> token1 = "0f0db6ff7eb32259e2ab26faad5bea05c5f31a2e59a38743077acd22e4512d69eb6c532e83118ae9a89f176933e2cd0f7e8c582ca3535bc548f256f69832be4f"
>>> token2 = "0f0db6ff7eb32259e2ab26faad5bea050ebd6b5fd3363bc885c982fd44c1e7f3c5f31a2e59a38743077acd22e4512d69eb6c532e83118ae9a89f176933e2cd0f7e8c582ca3535bc548f256f69832be4f"
>>> token3 = "0f0db6ff7eb32259e2ab26faad5bea05c5f31a2e59a38743077acd22e4512d6907687b0f26a0485b8e70f508c3c2902b6e1373a93bd585792817170366e57f43"
>>> forged = token1[:-32] + token2[32:64] + token3[-32:]
>>> forged
'0f0db6ff7eb32259e2ab26faad5bea05c5f31a2e59a38743077acd22e4512d69eb6c532e83118ae9a89f176933e2cd0f0ebd6b5fd3363bc885c982fd44c1e7f36e1373a93bd585792817170366e57f43'
```

{% endcode %}

And when we use it :

{% code overflow="wrap" %}

```bash
$ python3 chall.py
Welcome !
Please login first.
How would you login ?
1) Token
2) Credentials

Choice :1
Token : 0f0db6ff7eb32259e2ab26faad5bea05c5f31a2e59a38743077acd22e4512d69eb6c532e83118ae9a89f176933e2cd0f0ebd6b5fd3363bc885c982fd44c1e7f36e1373a93bd585792817170366e57f43
welcome admin
Flag = FLAG{ECBForgingBlock}
```

{% endcode %}

## Exploitation Script

```python
from pwn import *
import string

context.log_level = 'error'

def getToken(username, password):

    p = remote("127.0.0.1", 1337)
    p.recvuntil(b":")
    p.sendline(b"2")
    p.recvuntil(b": ")
    p.sendline(username.encode())
    p.recvuntil(b": ")
    p.sendline(password.encode())
    p.recvuntil(b": ")
    token = p.recvline()[:-1].decode()

    p.close()
    return token


def authToken(token):
    p = remote("127.0.0.1", 1337)
    p.recvuntil(b":")
    p.sendline(b"1")
    p.recvuntil(b": ")
    p.sendline(token.encode())

    print(p.clean().decode())

    p.close()

padding = 0
token = getToken("A" * 16 * 2,"")

# Getting padding size to end the first block and then write an arbitrary block
while token[32:64] != token[64:96]:
    padding += 1
    token = getToken("A"* padding + "A" * 16 * 2, "")

print(f"padding is size {padding}")

token1 = getToken("A"* padding, "A" * 6)
token2 = getToken("A"* padding + " "*15 + "1", "")
token3 = getToken("A"* padding, "A" * 5)

forged = token1[:-32] + token2[32:64] + token3[-32:]

authToken(forged)
```

## Exercice

If you want to try this exploit by yourself, you can pull [this docker image](https://hub.docker.com/r/thectfrecipes/crypto/) :

```
docker pull thectfrecipes/crypto:ECBForgingBlock
```

Deploy the image using the followed command :

```
docker run --name thectfrecipes_crypto_ECBForgingBlock -it --rm -d -p 1337:1337 thectfrecipes/crypto:ECBForgingBlock
```

The service is available on port 1337

```
nc 127.0.0.1 1337
```


---

# 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://www.ctfrecipes.com/cryptography/symmetric-cryptography/aes/mode-of-operation/ecb/block-shuffling/challenge-example.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.
