cipyer.py 파일
class STREAM:
def __init__(self, seed, size):
self.state = self.num2bits(seed, size)
def num2bits(self, num, size):
assert num < (1 << size)
return bin(num)[2:].zfill(size)
def bits2num(self, bits):
return int('0b' + bits, 2)
def shift(self):
new_bit = self.state[-1]
self.state = new_bit + self.state[:-1]
return new_bit
def getNbits(self, num):
sequence = ""
for _ in range(num):a
sequence += self.shift()
return sequence
def encrypt(self, plaintext):
ciphertext = b""
for p in plaintext:
stream = self.bits2num(self.getNbits(8))
c = p ^ stream
ciphertext += bytes([c])
return ciphertext
def decrypt(self, ciphertext):
plaintext = b""
for c in ciphertext:
stream = self.bits2num(self.getNbits(8))
p = c ^ stream
plaintext += bytes([p])
return plaintext
if __name__ == "__main__":
import os
for seed in range(0x100):
Alice = STREAM(seed, 16)
Bob = STREAM(seed, 16)
plaintext = os.urandom(128)
ciphertext = Alice.encrypt(plaintext)
assert plaintext == Bob.decrypt(ciphertext)
prob.py 파일
#!/usr/bin/env python3
from cipher import STREAM
import random
if __name__ == "__main__":
with open("flag", "rb") as f:
flag = f.read()
assert flag[:3] == b'DH{' and flag[-1:] == b'}'
seed = random.getrandbits(16)
stream = STREAM(seed, 16)
print(f"encrypted flag > {stream.encrypt(flag).hex()}")
output.txt파일
encrypted flag > 3cef03c64ac240c349971d9e4c951cc14ec4199f409249c21e964ac540c540944f901c934cc240934d96419f4b9e4d9f1cc41dc61dc34e9219c31bc11a914f9141c61ada
This script defines a custom stream cipher class named STREAM and demonstrates its functionality for encryption and decryption. Here's an explanation of how it works:
Key Components of the Code
- Class Initialization:
- __init__(self, seed, size): Initializes the cipher's internal state using a seed and size. The state is stored as a binary string of the specified size.
- Utility Methods:
- num2bits(self, num, size): Converts a decimal number into a binary string of the specified size, ensuring the length matches.
- bits2num(self, bits): Converts a binary string back into a decimal number.
- Shifting Mechanism:
- shift(self): Implements a cyclic shift of the internal state, moving the last bit to the front. This is the core of the stream generation.
- Stream Generation:
- getNbits(self, num): Generates a sequence of bits by repeatedly shifting the internal state and collecting the output bits.
- Encryption/Decryption:
- encrypt(self, plaintext): Encrypts the plaintext by XORing each byte with a corresponding byte from the generated bitstream.
- decrypt(self, ciphertext): Decrypts the ciphertext using the same process (because XOR is symmetric).
How the Cipher Works
- Initialization:
- Both the sender (Alice) and receiver (Bob) initialize their ciphers with the same seed and size to ensure synchronized states.
- Stream Generation:
- The getNbits() method provides the pseudorandom bitstream used for encryption and decryption. The cyclic shifting ensures that the state evolves deterministically and repeats eventually.
- Encryption:
- Each plaintext byte is XORed with an 8-bit chunk of the pseudorandom bitstream.
- Decryption:
- Since XOR is its own inverse, decryption uses the same process as encryption.
Example Run
- Alice and Bob share the same seed and size (16).
- A random 128-byte plaintext is generated using os.urandom(128).
- Alice encrypts the plaintext, producing ciphertext.
- Bob decrypts the ciphertext and verifies that the result matches the original plaintext using an assert statement.
Key Features and Observations
- Reproducibility:
- The cipher is deterministic. If Alice and Bob start with the same seed, their bitstreams will match exactly.
- Lightweight:
- The cipher implementation is simple and does not require external libraries beyond standard Python.
- Vulnerability:
- The stream cipher is only as strong as its pseudorandom bit generator. If an attacker can predict the state evolution (e.g., by observing enough ciphertext), they could reconstruct the stream and decrypt the message.
- Short Cycle:
- With a state size of 16 bits, the maximum cycle length of the stream is 2^16 bits (64 KB), which may be insufficient for encrypting large amounts of data securely.
Improvements and Suggestions
- Increase State Size:
- Use a larger state size (e.g., 128 or 256 bits) to improve security and reduce the likelihood of state repetition.
- Cryptographically Secure PRNG:
- Replace the simple cyclic shift with a more robust pseudorandom number generator to make the stream less predictable.
- State Integrity:
- Add mechanisms to detect if the internal state has been tampered with during communication.
- Testing:
- Include test cases for edge scenarios (e.g., encrypting and decrypting an empty string, very large data, etc.).
By addressing these improvements, the cipher could become more secure and better suited for practical applications.
This script encrypts a flag using the STREAM cipher from the provided cipher.py file and prints the encrypted flag in hexadecimal format. Here's a detailed breakdown:
How the Script Works
- Importing Necessary Modules:
- STREAM is imported from cipher.py (assumed to be the custom stream cipher implementation provided earlier).
- random is used to generate a random 16-bit seed for initializing the cipher.
- Loading the Flag:
- The script opens a file named flag in binary mode ("rb") and reads its contents.
- An assertion checks the flag format:
- It must start with DH{ and end with }.
- Initializing the Cipher:
- A 16-bit random seed is generated using random.getrandbits(16).
- A STREAM cipher object is created with this seed and a state size of 16 bits.
- Encrypting the Flag:
- The flag is encrypted using the cipher's encrypt method.
- The resulting ciphertext (in bytes) is converted to a hexadecimal string using .hex() for display.
- Output:
- The script prints the encrypted flag in hexadecimal format.
Output Example
When run, the script outputs something like:
encrypted flag > 4d5e6f7a8b9c...
The exact output depends on:
- The random seed.
- The contents of the flag file.
Key Observations
- Reproducibility:
- Since the seed is randomly generated, the output will differ with each execution. However, the decryption process will require the same seed to recover the plaintext.
- Security Concerns:
- The seed is not saved or transmitted in this script, meaning the encrypted flag cannot be decrypted without knowing the exact seed used.
- If an attacker observes multiple encrypted flags with the same seed, they might perform statistical attacks.
Potential Improvements
- Seed Management:
- Save the seed securely or transmit it alongside the ciphertext (e.g., using a separate secure channel).
- Alternatively, derive the seed from a password or key shared between the sender and receiver.
- Improved Randomness:
- Ensure the random number generator (random.getrandbits) is cryptographically secure by using secrets instead of random for critical applications:
import secrets seed = secrets.randbits(16)
- Ensure the random number generator (random.getrandbits) is cryptographically secure by using secrets instead of random for critical applications:
- State Size:
- A 16-bit state is relatively small and can lead to predictability or repetition. Consider increasing the state size to enhance security (e.g., 128 bits).
Decryption
If you have the encrypted flag and the seed, you can decrypt it using:
decrypted_flag = stream.decrypt(bytes.fromhex(encrypted_flag))
Make sure to reinitialize the STREAM object with the same seed used during encryption.
from cipher import STREAM
brute_force = 2**16
output_hex = "3cef03c64ac240c349971d9e4c951cc14ec4199f409249c21e964ac540c540944f901c934cc240934d96419f4b9e4d9f1cc41dc61dc34e9219c31bc11a914f9141c61ada"
output = bytes.fromhex(output_hex)
for i in range(brute_force):
seed = i
stream = STREAM(seed,16)
flag = stream.decrypt(output)
if flag[:3] == b'DH{' and flag[-1:] == b'}':
print(flag)
break
else:
print(i)
이 코드는 암호화된 데이터를 복호화하기 위해 브루트포스 공격을 수행하는 Python 스크립트입니다. 주요 목적은 암호화된 데이터에서 특정 조건을 만족하는 복호화 결과를 찾는 것입니다.
소스 코드 설명
1. 주요 변수와 설정
from cipher import STREAM
- STREAM은 외부에서 정의된 스트림 암호 알고리즘 클래스 또는 함수입니다.
- 이 클래스는 암호화/복호화 기능을 제공하며, 특정 seed 값을 사용하여 스트림 키를 생성합니다.
brute_force = 2**16
- 최대 브루트포스 시도 횟수를 설정합니다.
- 2**16 = 65536, 즉 0부터 65535까지 16비트(2바이트) 키 공간을 모두 시도합니다.
output_hex = "3cef03c64ac240c349971d9e4c951cc14ec4199f409249c21e964ac540c540944f901c934cc240934d96419f4b9e4d9f1cc41dc61dc34e9219c31bc11a914f9141c61ada"
output = bytes.fromhex(output_hex)
- 암호화된 데이터(output_hex)를 16진수 문자열로 저장하고, 이를 바이트 데이터로 변환해 output 변수에 저장합니다.
- bytes.fromhex()는 16진수 문자열을 바이트로 변환하는 함수입니다.
2. 브루트포스 루프
for i in range(brute_force):
seed = i
stream = STREAM(seed, 16)
flag = stream.decrypt(output)
- 0부터 65535까지 모든 seed 값을 시도하여 스트림 키를 생성합니다.
- STREAM(seed, 16):
- seed: 스트림 키 생성을 위한 초기값.
- 16: 스트림 암호가 사용하는 키 길이(추정).
- STREAM(seed, 16):
- stream.decrypt(output):
- output을 해당 seed를 사용해 복호화합니다.
- 결과를 flag 변수에 저장합니다.
3. 조건 확인
if flag[:3] == b'DH{' and flag[-1:] == b'}':
print(flag)
break
- 복호화 결과인 flag가 특정 형식을 만족하는지 확인합니다:
- flag[:3] == b'DH{': 복호화된 문자열의 시작이 DH{인지 확인.
- flag[-1:] == b'}': 복호화된 문자열의 끝이 }인지 확인.
- 조건을 만족하면:
- 복호화 결과(flag)를 출력.
- 반복문을 종료(break).
4. 디버깅용 출력
else:
print(i)
- 조건을 만족하지 않으면 현재 i 값(시도한 seed)을 출력하여 진행 상황을 표시합니다.
전체 코드의 실행 흐름
- 암호화된 데이터(output_hex)를 바이트로 변환.
- 브루트포스:
- 0부터 65535까지의 seed 값을 사용해 output 데이터를 복호화.
- 복호화 결과가 DH{...} 형식을 만족하면 성공.
- 조건을 만족하는 경우, 복호화된 플래그(flag)를 출력하고 종료.
예상 출력
- 복호화에 성공하면 flag 값 출력:
b'DH{your_decrypted_message}'
- 조건을 만족하지 않으면 계속 i 값 출력(진행 상황 표시).
이 코드의 활용
- CTF 문제 풀이: 주로 암호화된 데이터 복호화나 키 찾기 문제에서 사용.
- 브루트포스 공격: 제한된 키 공간(16비트 등)에서 가능한 모든 키를 시도.
- 스트림 암호 이해: 스트림 암호의 동작 원리와 취약점 탐구.
궁금한 부분이나 추가로 알고 싶은 점이 있으면 말씀해주세요! 😊
prob에서 16비트를 난수를 seed로 사용하는데, 16비트의 최대 값은 2^16 = 65,536 이므로,
브루트 포스가 충분히 가능하다고 생각해볼 수 있다. output으로 주어진 hex 값을 다시 bytes로 바꿔서
decrypt 함수에 65,536번 반복문을 돌려 모든 가능한 경우의 seed 값을 주면서
테스트하면 플래그를 획득할 수 있다.
출처 ㅣ https://velog.io/@ljyoung04/DreamHack-STREAMER-%ED%92%80%EC%9D%B4
https://velog.io/@ljyoung04/DreamHack-STREAMER-%ED%92%80%EC%9D%B4
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[134] IT 비전공자 [dreamhack]BISC Safe문제 풀기 (0) | 2025.01.22 |
---|---|
[133] IT 비전공자 [dreamhack]sane-env문제 풀기 (0) | 2025.01.22 |
[131] IT 비전공자 [dreamhack]Grand Theft Auto문제 풀기 (0) | 2025.01.19 |
[130] IT 비전공자 [dreamhack]산타 할아버지도 힘들어요문제 풀기 (0) | 2025.01.18 |
[129] IT 비전공자 [dreamhack]Replace Trick!문제 풀기 (0) | 2025.01.17 |