deayzl's blog

[2022 Fall GoN Open Qual CTF] Private Storage writeup 본문

CTF writeup/GoN Open Qual CTF

[2022 Fall GoN Open Qual CTF] Private Storage writeup

deayzl 2022. 8. 31. 21:00

#!/usr/bin/env python3
from Crypto.Cipher import ARC4
from base64 import b64encode
import zlib
from secret import flag, key

file = {"flag.txt" : flag, "key" : key}

def print_menu():
    print("1. List file")
    print("2. Download file")
    print("3. Add file")
    print("4. Copy file")
    print("5. Remove File")
    print("6. Logout")

def download_file():
    filename = input("File name >> ").strip()
    if filename not in file:
        print("No such file")
        pass
    content = file[filename]
    rc4 = ARC4.new(key.encode())
    enc = rc4.encrypt(zlib.compress(file[filename].encode()))
    print("Content :", b64encode(enc).decode())

def get_filename():
    filename = input("File name >> ").strip()
    if filename == "key":
        print("No!")
        exit(0)
    return filename

def add_content(filename, content):
    if filename not in file:
        file[filename] = ""
    file[filename] += content

def remove_file(filename):
    if filename not in file:
        print("No such file")
    elif filename == "key":
        print("No!")
        exit(0)
    else:
        del file[filename]

print("Welcome to safe storage! Make sure to bring your key!")
while True:
    print_menu()
    ipt = input(">> ").strip()
    if ipt == "1":
        print(" ".join(file.keys()))
    elif ipt == "2":
        download_file()
    elif ipt == "3":
        filename = get_filename()
        add_content(filename, input("Content >>").strip())
    elif ipt == "4":
        origin = input("Which file to copy >> ").strip()
        filename = get_filename()
        add_content(filename, file[origin])
    elif ipt == "5":
        filename = input("Which file to remove >> ").strip()
        remove_file(filename)
    elif ipt == "6":
        break

 

제공된 server.py 파일의 소스코드다.

 

flag 를 rc4 방식으로 암호화하고 저장하는 프로그램으로 보인다.

Rc4 암호화 알고리듬을 모른다면 어떻게 풀어야하는 지 감이 안올거다.

 

 

RC4 CIPHER 단순화 - YouTube

RC4 ALGORITHM - YouTube

RC4 security algorithm - YouTube

 

 

 

 

 

(문제를 풀 당시, 바로 위에 있는 세번째 영상이 크게 도움이 되었다)

 

문제 풀기 전, 휘갈겨놓은 노트를 보면

 

 

크게 2가지의 함수가 있는데, 이 함수 모두 Key 배열을 기반으로 KeyStream 을 만들어내는 과정의 일부이다.

중요한 것은 직접 plain text 와 xor 하는 KeyStream 은 K 와 S 배열에 종속적이고,

K 와 S 배열은 Key 만 같다면 항상 같을 수 밖에 없는 구조로 되어 있다는 점이다.

(암호화를 할 당시의 시간을 구해서 반영한다던가, 평문에 따라서 달라진다거나 하는 점이 아예 없다)

 

즉, KeyStream 이 항상 같으니 임의의 문자열을 암호화해서 암호화된 값과 평문을 xor 해주면

KeyStream 을 구할 수 있고 이 KeyStream 은 flag 문자열을 암호화했을 때 쓴 키이니

그대로 암호화된 flag 값에 구한 KeyStream 문자열을 xor 해주면 flag 를 구할 수 있다는 것이다.

(KeyStream 을 평문에 xor 할 때, 평문이 길다면 KeyStream 을 반복시킨 후 xor 하기에

암호화 된 값의 길이가 암호화된 flag 값의 길이가 같도록 해주고 KeyStream 을 구해줬다)

 

 

from pwn import *
import zlib
import base64

tmptext = b'asdfghjklqwertyuiopzxcv'

p = remote('host3.dreamhack.games', 23382)

def Download_file(filename):
    p.recvuntil(b'>> ')
    p.send_raw(b'2\n')
    p.recvuntil(b'>> ')
    p.send_raw(filename + b'\n')
    p.recvuntil(b': ')
    return p.recvline()[:-1]

def Add_file(filename, content):
    p.recvuntil(b'>> ')
    p.send_raw(b'3\n')
    p.recvuntil(b'>> ')
    p.send_raw(filename + b'\n')
    p.recvuntil(b'>>')
    p.send_raw(content + b'\n')


plain_text = zlib.compress(tmptext)
print(b'plain text : ' + plain_text + b'(' + str(len(plain_text)).encode() + b')')

Add_file(b'a.txt', tmptext)
cypher_text = base64.b64decode(Download_file(b'a.txt'))
print(b'cypher text : ' + cypher_text + b'(' + str(len(cypher_text)).encode() + b')')

keystream = list()
for i in range(len(plain_text)):
    keystream.append(plain_text[i] ^ cypher_text[i])
keystream = bytes(keystream)
print(b'keystream : ' + keystream + b'(' + str(len(keystream)).encode() + b')')

flag_encrypted = base64.b64decode(Download_file(b'flag.txt'))
print(b'flag encrypted : ' + flag_encrypted + b'(' + str(len(flag_encrypted)).encode() + b')')

flag_compressed = list()
for i in range(len(keystream)):
    flag_compressed.append(flag_encrypted[i] ^ keystream[i])
flag_compressed = bytes(flag_compressed)
print(b'flag compressed : ' + flag_compressed + b'(' + str(len(flag_compressed)).encode() + b')')

print(zlib.decompress(flag_compressed))

p.close()

 

 

 

Comments