Problem Solving/Dreamhack

[Dreamhack] ROT128 문제풀이

LeeJaeJun 2023. 12. 23. 23:58
728x90
반응형

https://dreamhack.io/wargame/challenges/852/

 

ROT128

Description rot128.py는 flag.png 파일을 암호화하여 encfile로 저장하는 프로그램의 소스 코드입니다. (풀이자가 프로그램을 직접 실행할 수는 없습니다.) 주어진 encfile을 복호화하여 flag 파일 내용을 알

dreamhack.io

문제
문제파일
encfile

rot128.py 파일은 다음과 같습니다.

#!/usr/bin/env python3

hex_list = [(hex(i)[2:].zfill(2).upper()) for i in range(256)]

with open('flag.png', 'rb') as f:
    plain_s = f.read()

plain_list = [hex(i)[2:].zfill(2).upper() for i in plain_s]

enc_list = list(range(len(plain_list)))

for i in range(len(plain_list)):
    hex_b = plain_list[i]
    index = hex_list.index(hex_b)
    enc_list[i] = hex_list[(index + 128) % len(hex_list)]

enc_list = ''.join(enc_list)

with open('encfile', 'w', encoding='utf-8') as f:
    f.write(enc_list)

1. hex_list는 0 부터 255까지의 정수를 hex(i)와 upper()를 이용해 16진수로 대문자 형태로 변환한 후 [2:]으로 슬라이싱하여 '0x'부분을 제외한 문자열을 원소로 저장합니다.(원래 hex()를 통해 16진순로 변환하면 0x13과 같은 형태임) zfill(2)을 통해 길이가 2보다 짧은 문자열 앞에 '0'을 채워 모든 문자열의 길이를 2로 맞춥니다.

 

2. flag.png를 바이트 형식으로 읽어 이를 f라고 칭하고, 이것을 plain_s에 저장합니다.

 

3. plain_list는 plain_s의 각 바이트를 '0x'를 제외한 16진수 대문자 형태로 바꾸고, 길이가 2로 고정되도록 빈자리에는 '0'을 채운 결과값을 원소로 가집니다.

 

4. enc_list는 0부터 plain_list의 길이만큼 순차적으로 원소가 들어있는 리스트입니다. [0, 1, 2, ... , len(plain_list)]. 뒤에 코드를 보면 알겠지만 현재 enc_list 안의 원소들은 그냥 길이를 지정하기 위해 넣은 의미없는 값들입니다.

 

5. hex_b는 plain_list의 원소인데, hex_list에서 plain_list의 각 원소를 찾아서 Index를 가져옵니다. 그 다음에  index + 128을 한 뒤에 hex_list의 길이(256)로 나눈 나머지를 enc_list에 넣습니다. 이 과정을  plain_list 길이 만큼 반복합니다. 각 원소는 0~255의 값을 가지고,현재 index에 128만큼 이동한 위치라고 보면 됩니다.(255초과 시 0부터). 즉 plain_list의 각 원소를 128만큼 이동시켜 저장한 것이 hex_list입니다.-> 이 과정은 카이사르 암호의 한 종류인 ROT13 (Rotate by 13 places) 과 유사합니다. 카이사르 암호는 알파벳을 일정 거리만큼 밀어 다른 알파벳으로 치환하는 암호화 방식입니다.

 

6. enc_list의 모든 원소를 이어 붙인 뒤, 이 문자열을 encfile에 씁니다.

 

encfile은 평문에서 모든 바이트를 128만큼 이동시킨 값이므로 (전체 256개의 원소에서 각 원소를 128만큼 밀어서 암호화 한 값) 반대로 128만큼 밀면 복호화를 할 수 있습니다. 전체 원소가 256개로 고정되었으므로 128만큼 한 번 더 밀면 또한 같은 값을 나타내 복호화가 가능합니다.(%len(hex_list)에서 원소 범위를 0~255로 고정되었기 때문) (ex. 0x01 -> 128만큼 밀면 -> 0x81 -> 다시 또 128만큼 밀면 -> 0x01)

 

복호화 코드는 다음과 같습니다.

#!/usr/bin/env python3
 
hex_list = [(hex(i)[2:].zfill(2).upper()) for i in range(256)]

# 주어진 encfile 읽기
with open('encfile', 'r', encoding='utf-8') as f:
    enc_s = f.read()
enc_list = [enc_s[i:i+2] for i in range(0, len(enc_s), 2)]#읽어온 암호문을 2개 단위로 잘라서 list 저장

dec_list = list(range(len(enc_list)))

# 복호화
for i in range(len(enc_list)):
    hex_b = enc_list[i]
    index = hex_list.index(hex_b)
    dec_list[i] = hex_list[(index + 128) % len(hex_list)]#한 번 더 128만큼 씩 이동시켜 원래 평문으로 변환하여 list 저장

dec_list = ''.join(dec_list)#list의 각 원소 이어붙여서 문자열로 만들기

# 결과를 flag 파일에 저장
with open('flag', 'w', encoding='utf-8') as f:
    f.write(dec_list)

복호화 코드를 실행시켜 얻은 flag파일에는 평문이 저장되어 있습니다.

flag 파일

본래 flag.png파일을 16진수 데이터로 변환한 결과값이 바로 이 flag 파일입니다. 따라서 16진수 데이터 flag파일을 이진 데이터로 이루어진 png 파일로 다시 변환시켜주어야 합니다. Windows라면 Hxd(Hex Editor)와 같은 이진 데이터를 16진수로, 16진수를 이진 데이터로 변환하여 값을 보여주는 프로그램을 사용하시면 됩니다. Mac 유저라면 App store에서 Hex Fiend를 다운받아 사용하시기를 추천드립니다.

Hex Fiend로 데이터를 이진데이터로 변환하여 해독한 모습

오른쪽 부분에 .PNG로 시작하는 것이 Decoded text입니다. 

이것을 flag.png로 저장하면

flag.png

flag.png파일이 생성됩니다. PNG 이미지 파일에 flag 값이 적혀있습니다.

728x90
반응형

'Problem Solving > Dreamhack' 카테고리의 다른 글

[Dreamhack] Textbook-RSA 문제풀이  (1) 2023.12.23