> For the complete documentation index, see [llms.txt](https://docs.cooku222.kr/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.cooku222.kr/security/crypto/dreamhack/dreamhack-textbook-cbc.md).

# \[DreamHack] Textbook-CBC

#### 문제링크

<https://dreamhack.io/wargame/challenges/175>

[ Textbook-CBCDescription 드림이가 AES 플래그 서버를 운영하고 있어요. 무엇이든 암호화하고 복호화 할 수 있다던데... 플래그를 획득해주세요! References https://dreamhack.io/lecture/courses/74dreamhack.io](https://dreamhack.io/wargame/challenges/175)

&#x20;

#### 문제

<figure><img src="https://blog.kakaocdn.net/dna/EAWZL/btsNsiSWlcZ/AAAAAAAAAAAAAAAAAAAAAOJC9_573DI9Vo3jr9qJnBuWkphRzN8Pwk_wc_psuRyr/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&#x26;expires=1782831599&#x26;allow_ip=&#x26;allow_referer=&#x26;signature=aLg%2Fi7TZTKYYhvsqdUFi%2Br6SxF0%3D" alt="" height="405" width="1106"><figcaption></figcaption></figure>

***

#### WriteUp

```
from Crypto.Util.Padding import pad, unpad
from random import choices, randint
from Crypto.Cipher import AES

BLOCK_SIZE = 16
flag = open("flag", "rb").read()
key = bytes(randint(0, 255) for i in range(BLOCK_SIZE))

encrypt = lambda pt: AES.new(key, AES.MODE_CBC, key).encrypt(pad(pt, BLOCK_SIZE))
decrypt = lambda ct: unpad(AES.new(key, AES.MODE_CBC, key).decrypt(ct), BLOCK_SIZE)

print("Welcome to dream's AES server")
while True:
    print("[1] Encrypt")
    print("[2] Decrypt")
    print("[3] Get Flag")

    choice = input()

    if choice == "1":
        print("Input plaintext (hex): ", end="")
        pt = bytes.fromhex(input())
        print(encrypt(pt).hex())

    elif choice == "2":
        print("Input ciphertext (hex): ", end="")
        ct = bytes.fromhex(input())
        print(decrypt(ct).hex())

    elif choice == "3":
        print(f"flag = {encrypt(flag).hex()}")
        exit()

    else:
        print("Nope")
```

-> challenge.py 파일

-> AES - CBC 모드의 암/복호화 기능이 구현되어 있는 파일임

-> AES.new(key, AES.MODE\_CBC, key)에서 초기화 벡터의 값과 키의 값이 동일함

사용자에게 암호화 기능과 복호화 기능이 모두 주어져 있고, GET Flag을 선택할 경우 플래그의 암호화 결과를 얻은 후 종료하기 때문에, 암/복호화 기능으로 키를 알아낸 후 플래그의 암호문을 전송받아 로컬에서 복호화를 진행해야 한다.

암/복호화 기능으로 키를 알아내는 것은 현대 기술로 불가능하지만 초기화 벡터의 값은 알아낼 수 있다.

<figure><img src="https://blog.kakaocdn.net/dna/cvBUKO/btsNrjZUO7W/AAAAAAAAAAAAAAAAAAAAAL44kvK1K17zBTjiCWkdKehhRT0Q6FClREsACSdUcyEx/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&#x26;expires=1782831599&#x26;allow_ip=&#x26;allow_referer=&#x26;signature=B0aUDN0jroUoNMqmBW3LPyMW7%2BE%3D" alt="" height="422" width="630"><figcaption></figcaption></figure>

-> 초기화 벡터 값 도출하기

#### 1. 초기화 벡터를 암호화하여 다시 복호화하기

암호화 기능을 사용하여 평문으로 0을 넣게 되면 0과 초기화 벡터를 XOR 연산한 결과를 암호화함

\=> 첫 블록은 초기화 벡터를 암호화한 결과인 enc K(초기화 벡터)임을 알 수 있다.

복호화 과정에서 첫 블록은 초기화 벡터와 연산이 이루어져 원하는 연산이 어렵지만, 둘째 블록부터는 암호문의 이전 블록을 기반으로 복호화가 이루어지기 때문에 원하는 연산을 진행함

-> 0 || enc K(초기화 벡터)를 복호화하면 둘째 블록은

<figure><img src="https://blog.kakaocdn.net/dna/cMNL5c/btsNsGS82Gy/AAAAAAAAAAAAAAAAAAAAAEvjSfAuQE8werI0gC9rUHzsuYQ-Agxsk7isns0egj3I/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&#x26;expires=1782831599&#x26;allow_ip=&#x26;allow_referer=&#x26;signature=ANZ%2BD%2BMXdg%2BIgJcSz2qaiMDXKEc%3D" alt="" height="30" width="171"><figcaption></figcaption></figure>

가 되어-> 초기화 벡터와 동일한 값을 가짐

패딩이 올바르지 않을 시 unpad 에러로 프로세스가 종료되니 0을 암호화할 때 얻은 둘째 블록도 암호문의 셋째 블록으로 추가해줌

&#x20;

#### 2. 초기화 벡터의 XOR 연산을 이용하여 추출하기

-> 0 || 0을 복호화할 경우 첫 블록과 둘째 블록은 어떤 값을 가지는가?

-> 첫 블록은

<figure><img src="https://blog.kakaocdn.net/dna/kBIRS/btsNpW3ZR7S/AAAAAAAAAAAAAAAAAAAAABsDVc8U2aesFDjr4aT0K1KrF2_F0P19sA3yEzF3N-LN/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&#x26;expires=1782831599&#x26;allow_ip=&#x26;allow_referer=&#x26;signature=ceB3FOdE2U9dbaVU6GKRz6A97AY%3D" alt="" height="37" width="117"><figcaption></figcaption></figure>

의 값을 가지고, 둘째 블록은&#x20;

<figure><img src="https://blog.kakaocdn.net/dna/kQCgY/btsNsl9WQaU/AAAAAAAAAAAAAAAAAAAAAArDp-iTX_lnkGDiKjAmJEvO21NNGikpwcqT-ezB7wOR/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&#x26;expires=1782831599&#x26;allow_ip=&#x26;allow_referer=&#x26;signature=wceudkAj%2B%2FAx15dt9f7m8Gxd9ng%3D" alt="" height="33" width="192"><figcaption></figcaption></figure>

의 값을 가진다.&#x20;

\=> 0 || 0을 복호화한 후 첫 블록과 둘째 블록을 XOR 연산해주는 것만으로도 초기화 벡터의 값을 구할 수 있다.

but, unpad 과정이 실패하기 때문에 암호화 과정에서 임의의 값을 암호화한 후 뒤의 두 블록을 가져와서 0 || 0 뒤에 붙여줘야함.

&#x20;

#### exploit

```
from pwn import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import os

io = remote("host3.dreamhack.games", 9090)

def encrypt(pt):
    io.sendline(b"1")
    io.sendlineafter(b"(hex): ", bytes.hex(pt).encode())
    return bytes.fromhex(io.recvline().decode())

def decrypt(pt):
    io.sendline(b"2")
    io.sendlineafter(b"(hex): ", bytes.hex(pt).encode())
    return bytes.fromhex(io.recvline().decode())

def getflag():
    io.sendline(b"3")
    io.recvuntil(b"flag = ")
    return bytes.fromhex(io.recvline().decode())

zeroblock = bytes(16)

# TODO
key = ?

enc_flag = getflag()

flag = AES.new(key, AES.MODE_CBC, key).decrypt(enc_flag)
flag = unpad(flag, 16).decode()

print(flag)
```

첫 번째 방법(초기화 벡터를 암호화하여 다시 복호화하기)에 해당하는 코드

```
enc = encrypt(zeroblock)
key = decrypt(zeroblock + enc)[16:32]
```

두 번째 방법(초기화 벡터의 XOR 연산을 이용하여 추출하기)에 해당하는 코드

```
enc = encrypt(os.urandom(16))
dec = decrypt(zeroblock * 2 + enc)
key = xor(dec[:16], dec[16:32])
```

&#x20;

#### 최종 솔브 코드

```
from pwn import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

io = remote("host3.dreamhack.games", 9090)

def encrypt(pt):
    io.sendline(b"1")
    io.sendlineafter(b"(hex): ", bytes.hex(pt).encode())
    return bytes.fromhex(io.recvline().decode())

def decrypt(pt):
    io.sendline(b"2")
    io.sendlineafter(b"(hex): ", bytes.hex(pt).encode())
    return bytes.fromhex(io.recvline().decode())

def getflag():
    io.sendline(b"3")
    io.recvuntil(b"flag = ")
    return bytes.fromhex(io.recvline().decode())

zeroblock = bytes(16)

METHOD = 1
# METHOD = 2

if METHOD == 1:
    enc = encrypt(zeroblock)
    key = decrypt(zeroblock + enc)[16:32]
else:
    enc = encrypt(os.urandom(16))
    dec = decrypt(zeroblock * 2 + enc)
    key = xor(dec[:16], dec[16:32])

enc_flag = getflag()

flag = AES.new(key, AES.MODE_CBC, key).decrypt(enc_flag)
flag = unpad(flag, 16).decode()

print(flag)
```

&#x20;


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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://docs.cooku222.kr/security/crypto/dreamhack/dreamhack-textbook-cbc.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.
