comibear
article thumbnail

☢️ Unintended Solution.. zzlol

이 또한 저번 포스팅에 이어 언인텐 풀이이다. 지금까지 봐왔던 언인텐 풀이와는 결이 다를 정도로 심각한 언인텐 풀이인데,, 이 방법을 어떻게 생각해낸건지 모르겠다. 개천재인듯;; (저 말고 어떤 선배입니다)

🖲️ Code Analysis

꽤나 나를 힘들게 했던 문제이다.. 언인텐 풀이만으로 너무 많은 힘을 써버려서 인텐 풀이를 알고싶게 하지도 않는다.. 먼저 코드를 살펴보자.

 

이 문제는 2가지 코드가 있다. 바로 Client 와 Server 이 서로 통신하면서 파일을 만들어내게 된다. 코드를 모두 설명하기에는 무리가 있기 때문에 문제를 푸는 데에 필요한 코드들만 살펴보도록 하자.

Server.py

#!/usr/bin/python3
# server.py

from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os

def aes_enc(pt : bytes, key : bytes):
    pt_pad = pad(pt, 16)
    cipher = AES.new(key, AES.MODE_ECB)
    return cipher.encrypt(pt_pad)

def aes_dec(ct : bytes, key : bytes):
    cipher = AES.new(key, AES.MODE_ECB)
    pt_pad = cipher.decrypt(ct)
    return unpad(pt_pad, 16)

def input_enc(session_key : bytes) -> str:
    dat_enc = bytes.fromhex(input())
    return aes_dec(dat_enc, session_key).decode()

def print_enc(session_key : bytes, data : bytes):
    data_enc = aes_enc(data.encode(), session_key)
    print(data_enc.hex())

def key_parser(client_id : str):
    try:
        f = open(f"{client_id}/key.txt")
        enc_p = f.readline()[:-1]
        enc_q = f.readline()[:-1]
        enc_d = f.readline()[:-1]
        enc_u = f.readline()[:-1]
        n = int(f.readline())
        f.close()
    except:
        print("[-] Error has been occured during key parsing. Please try later.")
        exit(-1)

    return enc_p, enc_q, enc_d, enc_u, n


def login():
    client_id = input()
    if not client_id.islower():
        print("[-] Only lowercase alphabet is allowed in ID")
        exit(-1)

    if os.path.exists(client_id): # Already registered
        enc_p, enc_q, enc_d, enc_u, n = key_parser(client_id)
        print(f"Hi {client_id}! Glad to see you again")
        print(enc_p)
        print(enc_q)
        print(enc_d)
        print(enc_u)

    else: # Not registered account
        print(f"Hi {client_id}! To create an account, please send your RSA private key encrypted with your pw")
        enc_p = input()
        enc_q = input()
        enc_d = input()
        enc_u = input()
        n = int(input())
        try:
            os.makedirs(client_id)
            os.makedirs(f"{client_id}/src")
            f = open(f"{client_id}/key.txt", 'w')
            f.write(enc_p + '\n')
            f.write(enc_q + '\n')
            f.write(enc_d + '\n')
            f.write(enc_u + '\n')
            f.write(str(n) + '\n')
            f.close()
        except:
            print("Error has been occured during key storing. Please try later.")
            exit(-1)
        print("Key is successfully saved")

    return client_id, n, 65537

def gen_and_send_session_key(n, e):
    session_key = os.urandom(32)
    while True:
        prefix_padding = os.urandom(1)
        if prefix_padding != b'\x00':
            break
    postfix_padding = os.urandom(256 - 32 - 1 - 1)
    rsa_plain = bytes_to_long(prefix_padding + session_key + postfix_padding)
    rsa_enc_session_key = long_to_bytes(pow(rsa_plain, e, n))
    print(rsa_enc_session_key.hex())
    return session_key

def save_file(session_key : bytes, client_id : str):
    name = input_enc(session_key)
    if name == "BACK":
        return False
    if not name.islower():
        print_enc(session_key, "Invalid filename")
        return False
    print_enc(session_key, "OK")
    try:
        data_hex = input_enc(session_key)
        if data_hex == "BACK":
            return False
        data = bytes.fromhex(data_hex)
        if len(data) > 1000:
            print_enc(session_key, "File too large")
            return False
    except:
        print_enc(session_key, "Wrong hex data")
        return False

    try:
        filepath = f"{client_id}/src/{name}.enc"
        if os.path.exists(filepath):
            print_enc(session_key, "File already exists")
            return False
        f = open(filepath, "wb")
        f.write(data)
        f.close()

    except:
        print_enc(session_key, "Failed to save a file")
        return False

    print_enc(session_key, "OK")
    return True


def load_file(session_key : bytes, client_id : str):
    name = input_enc(session_key)
    if name == "BACK":
        return False
    if not name.islower():
        print_enc(session_key, "Invalid filename")
        return False

    try:
        filepath = f"{client_id}/src/{name}.enc"
        data_enc = open(filepath, "rb").read()
    except:
        print_enc(session_key, "File does not exist")
        return False

    print_enc(session_key, "OK")
    print_enc(session_key, data_enc.hex())
    return True

def menu(session_key : bytes, client_id : str):
    while True:
        c = input_enc(session_key)
        if c == 'save_file':
            save_file(session_key, client_id)
        elif c == 'load_file':
            load_file(session_key, client_id)
        elif c == 'logout':
            exit(-1)
        else:
            print("Wrong choice.")
            exit(-1)

def go():
    client_id, n, e = login()
    session_key = gen_and_send_session_key(n, e)
    menu(session_key, client_id)

go()

Client.py

#!/usr/bin/python3
# client.py

from Crypto.Util.number import *
import os
import socket
from Crypto.Util.Padding import pad, unpad
from hashlib import sha256
from Crypto.Cipher import AES

CREDENTIALS = {
    "codegate": "*******************"
}

def aes_enc(pt, key):
    pt_pad = pad(pt, 16)
    cipher = AES.new(key, AES.MODE_ECB)
    return cipher.encrypt(pt_pad)

def aes_dec(ct, key):
    cipher = AES.new(key, AES.MODE_ECB)
    pt_pad = cipher.decrypt(ct)
    #return pt_pad
    return unpad(pt_pad, 16)

def packet_recv_plain(sock):
    data = b''
    while True:
        c = sock.recv(1)
        if c == b'\n':
            break
        data += c
    return data

def packet_send_plain(sock, data):
    if type(data) != bytes or b'\n' in data:
        print("[-] Invalid packet")
        exit(-1)
    sock.sendall(data + b'\n')

def packet_recv_aes_enc(sock, session_key):
    data = b''
    while True:
        c = sock.recv(1)
        if c == b'\n':
            break
        data += c
    data = data.decode()
    data = bytes.fromhex(data)
    return aes_dec(data, session_key)

def packet_send_aes_enc(sock, data, session_key):
    if type(data) != bytes or b'\n' in data:
        print("[-] Invalid packet")
        exit(-1)
    data_enc = aes_enc(data, session_key).hex().encode()
    sock.sendall(data_enc + b'\n')

# Garner's formula
def rsa_crt_dec(p, q, d, u, c):
    mp = pow(c, d % (p-1), p)
    mq = pow(c, d % (q-1), q)
    m = ((mp - mq) * u % p) * q + mq
    return m

# Derive file encryption key from RSA prvate key
def file_encryption_key(p, q, d, u):
    rsa_priv_key = long_to_bytes(p) + long_to_bytes(q) + long_to_bytes(d) + long_to_bytes(u)
    return sha256(rsa_priv_key).digest()

def login(sock):
    client_id = input("id (Only lowercase) > ")
    if not client_id.islower():
        print("[-] Invalid ID")
        exit(-1)
    if client_id in CREDENTIALS:
        client_pw = CREDENTIALS[client_id]
    else:
        client_pw = input("pw > ")

    sock.send(client_id.encode())

def generate_rsa_private_key():
    while True:
        p = getPrime(1024)
        q = getPrime(1024)
        e = 65537
        phi = (p-1) * (q-1)
        if phi % e == 0: continue
        if p == q: continue
        d = inverse(e, phi)
        u = inverse(q, p)
        return p, q, d, u

def init_connection():
    ip = input("ip > ")
    port = int(input("port > "))
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect((ip, port))
    except:
        print("[-] Connection error")
        exit(-1)

    print("[+] Connection established")
    return sock

def login(sock):
    client_id = input("id > ")
    if client_id in CREDENTIALS:
        client_pw = CREDENTIALS[client_id]
    else:
        client_pw = input("pw > ")

    sock.sendall((client_id + '\n').encode())
    resp = packet_recv_plain(sock).decode()
    print("(From server)", resp)
    # Already registered
    if resp == f"Hi {client_id}! Glad to see you again":
        pw_hash = sha256(client_pw.encode()).digest()
        p = int(aes_dec(bytes.fromhex(packet_recv_plain(sock).decode()), pw_hash))
        q = int(aes_dec(bytes.fromhex(packet_recv_plain(sock).decode()), pw_hash))
        d = int(aes_dec(bytes.fromhex(packet_recv_plain(sock).decode()), pw_hash))
        u = int(aes_dec(bytes.fromhex(packet_recv_plain(sock).decode()), pw_hash))

    # Not registered
    elif resp == f"Hi {client_id}! To create an account, please send your RSA private key encrypted with your pw":
        pw_hash = sha256(client_pw.encode()).digest()
        p, q, d, u =  generate_rsa_private_key()
        n = p * q
        enc_p = aes_enc(str(p).encode(), pw_hash).hex().encode()
        enc_q = aes_enc(str(q).encode(), pw_hash).hex().encode()
        enc_d = aes_enc(str(d).encode(), pw_hash).hex().encode()
        enc_u = aes_enc(str(u).encode(), pw_hash).hex().encode()
        packet_send_plain(sock, enc_p)
        packet_send_plain(sock, enc_q)
        packet_send_plain(sock, enc_d)
        packet_send_plain(sock, enc_u)
        packet_send_plain(sock, str(n).encode())
        resp = packet_recv_plain(sock).decode()
        print("(From server)", resp)

    # Error has been occured
    else:
        exit(-1)

    return p,q,d,u

def recv_session_key(sock, p, q, d, u):
    rsa_enc_session_key = int(packet_recv_plain(sock).decode(), 16)
    rsa_plain = long_to_bytes(rsa_crt_dec(p, q, d, u, rsa_enc_session_key))
    session_key = rsa_plain[1:33]
    print("[+] Session key received")
    return session_key


def save_file(sock, session_key, file_enc_key):
    packet_send_aes_enc(sock, "save_file".encode(), session_key)
    name = input("filename(Only lowercase) > ")
    if not name.islower():
        print("[-] Invalid filename")
        packet_send_aes_enc(sock, "BACK".encode(), session_key)
        return False

    packet_send_aes_enc(sock, name.encode(), session_key)
    resp = packet_recv_aes_enc(sock, session_key).decode()
    if resp != "OK":
        print("(From server)", resp)
        return False

    data = bytes.fromhex(input("data(in hex) > "))
    if len(data) > 1000:
        print("[-] File too large")
        packet_send_aes_enc(sock, "BACK".encode(), session_key)
        return False

    data_enc = aes_enc(data, file_enc_key)
    packet_send_aes_enc(sock, data_enc.hex().encode(), session_key)
    resp = packet_recv_aes_enc(sock, session_key).decode()
    if resp != "OK":
        print("(From server)", resp)
        return False

    print(f"[+] File {name} successfully saved")
    return True

def load_file(sock, session_key, file_enc_key):
    packet_send_aes_enc(sock, "load_file".encode(), session_key)
    name = input("filename(Only lowercase) > ")
    if not name.islower():
        print("[-] Invalid filename")
        packet_send_aes_enc(sock, "BACK".encode(), session_key)
        return False

    packet_send_aes_enc(sock, name.encode(), session_key)
    resp = packet_recv_aes_enc(sock, session_key).decode()
    if resp != "OK":
        print("(From server)", resp)
        return False

    data_enc_hex = packet_recv_aes_enc(sock, session_key).decode()
    data_enc = bytes.fromhex(data_enc_hex)

    '''
    Sorry, I won't let you know a plain file content. But I will give you a encrypted one. You can easily decrypt this without my help because file_enc_key is derived from your own rsa private key. isn't it????
    '''
    # data = aes_dec(data_enc, file_enc_key)
    # print(f"[+] {name}(in hex) : {data.hex()}")

    print(f"[+] {name}.enc(in hex) : {data_enc.hex()}")
    return True

def menu(sock, session_key, file_enc_key):
    menu = '''1. Save a file
2. Load a file
3. Logout'''

    while True:
        print(menu)
        c = input("> ")
        if c == '1':
            save_file(sock, session_key, file_enc_key)
        elif c == '2':
            load_file(sock, session_key, file_enc_key)
        elif c == '3':
            packet_send_aes_enc(sock, "logout".encode(), session_key)
            print("[+] Bye...")
            break
        else:
            continue

def go():
    ### INIT
    sock = init_connection()
    p,q,d,u = login(sock)
    session_key = recv_session_key(sock, p, q, d, u)
    file_enc_key = file_encryption_key(p, q, d, u)
    ### MAIN ROUTINE
    menu(sock, session_key, file_enc_key)
    sock.close()

go()

코드가 많이 길지만 그래도 대충 감만 잡도록 하자.. ㅠㅠ 처음에 보고 기겁함

 

Client 와 Server 이 서로 통신을 하게 되는데, 여기서 통신에는 암호화 과정이 들어가게 된다. session key 를 생성함으로써, 통신할 때 메시지를 암호화해서 socket 을 보내게 되고, 수신자는 이를 다시 session key 로 복호화해서 정확한 메시지를 전달받는다.

 

그렇다면 이 session key 는 어떻게 생성될까??

—> 아마 인텐 풀이를 위해서는 필수적으로 봐야 할 요소이지만, python int error 이라는 미친 성능의 언인텐 풀이를 위해서는 있어봤자 독이 되는 요소이다… 여기서 삽질을 3시간 이상 한 것 같다…..

 

결국 우리는 flag.enc 를 읽어야 한다. 그런데 flag.enc 는 어떤 이상한 문자로 되어있고, 곧 암호화되어 저장됨을 인식할 수 있겠다. 그렇다면 어떻게 flag.enc 가 암호화되는 것일까?

def file_encryption_key(p, q, d, u):
    rsa_priv_key = long_to_bytes(p) + long_to_bytes(q) + long_to_bytes(d) + long_to_bytes(u)
    return sha256(rsa_priv_key).digest()

data_enc = aes_enc(data, file_enc_key)

 

바로 이 두 부분이다. p, q, d, u 를 이용해서 file_enc_key 를 생성해내고, 이를 aes 의 key 로 사용함으로써 기존의 FLAG 를 암호화하는 것 같다.

 

결국 우리가 알아야 할 것은 p, q, d, u 즉, RSA 암호의 모든 정보가 되겠다.

현재는 N만이 나와있는 상태이고, 우리는 p , q 중 하나만 구할 수 있다면 file_enc_key 도 구할 수 있을 것이다. ㅎㅎ

💡 Main Idea

바로 python 에서 int error 를 나타낼 때, 값까지 모두 나타내어주는 error 를 사용한다.. 어떻게 이런 생각을 했는지 신기할 정도로 신선한 방법이다. (다시봐도 미친 풀이..)

 

여튼 그래서 어떤 부분에서 에러를 발생시킬 수 있냐..?

def login(sock):
    client_id = input("id > ")
    if client_id in CREDENTIALS:
        client_pw = CREDENTIALS[client_id]
    else:
        client_pw = input("pw > ")

    sock.sendall((client_id + '\n').encode())
    resp = packet_recv_plain(sock).decode()
    print("(From server)", resp)
    # Already registered
    if resp == f"Hi {client_id}! Glad to see you again":
        pw_hash = sha256(client_pw.encode()).digest()
        p = int(aes_dec(bytes.fromhex(packet_recv_plain(sock).decode()), pw_hash))
        q = int(aes_dec(bytes.fromhex(packet_recv_plain(sock).decode()), pw_hash))
        d = int(aes_dec(bytes.fromhex(packet_recv_plain(sock).decode()), pw_hash))
        u = int(aes_dec(bytes.fromhex(packet_recv_plain(sock).decode()), pw_hash))

바로 이 부분이다. Server 에서는 암호화된 P를 전송하는데, Client 에서 이 암호화된 값을 통해서 기존의 P 를 구하는 코드가 있다. 그런데, 여기서 int 에서 오류를 발생시켜야 한다.

 

첨부되어있는 ket.txt 를 살펴보면, 이미 enc_p 의 값이 정의되어 있다는 것을 확인할 수 있다. 그렇다면 우리는 우리가 서버인척을 하고 client 에 enc_p 를 살짝 변형시킨 값을 반환시켜 오류를 발생시킬 수 있을 것이다.

 

우리가 주목해야 할 점은 2가지이다.


1. Padding 문제를 피할 것
aes_dec 부분에서 unpad 함수가 쓰인다. 하지만, 만약 enc_p 를 변형시켰을 때, 변형된 데이터를 복호화 시에 unpad 함수에서 오류가 발생한다면 우리는 int error 를 발생시킬 수 없게 된다.

2. int error를 발생시킬 것
당연하게도, int error 를 발생시켜야 한다. enc_p 를 그대로 넣어주게 되면, int error 가 발생하지 않아서 우리가 p 에 대한 정보를 얻을 수 없게 된다. 따라서 enc_p 중간에 이상한 값을 첨가하여 int string 으로 변형될 수 없게 해야 한다.
  • Padding 문제를 피하기 위해서는 enc_p 의 마지막 블럭이 항상 포함되어야 한다. Pad 에 영향을 끼치는 것은 마지막 블럭인데, 이 블럭이 있어야 정상적으로 unpad 를 진행할 수 있게 된다.
  • int error 문제는 중간에 00 이 16번 반복되는 하나의 블럭을 넣어줌으로써 해결할 수 있다.

📖 Exploit Code

이제 내가 Server 인 척을 하고, 거짓된 데이터를 넘겨주어야 한다.

선배님이 주신 서버를 이용해서 ssh 에서 서버를 열고, 다음과 같은 코드를 작성해주자.

import socket

HOST = '0.0.0.0'
PORT = 18425

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen()
client_socket, addr = server_socket.accept()
print('Connected by', addr)

client_id = client_socket.recv(1024)
enc_p = "5901d4d62364a649837b4638748fd17beb3942388cce66f7595ea589820b7ca86fa1c33d92435f6820c06e09752b4bcf1f0a3936d173d482fb414cabe446ed00fbcc7de15b79670e8cf0ee478be647b6bf637e4a69eac25b2d8567fd400b01b3bbf9ec3a6cee1718d43cb8c5d16b0ebd35e3cffc2bc58662338e56f3a7a9478d958d8abac38a84d801e11cd209510782b5e9f5bcc277b5e7518c38571c923771c2a22c1dc11abf5a82d32c1c4e3ae84300a11fd09a978304cf65c6e31c8286685020c63c1ed465a1f0e2423bafff2cf1b951266cacfd99580693c0cdd80d8224a41e56bfc9ae430fc219a6a4d8570d487de2dc67ec5610734181e72bc5cf8691f04ffb9997d259d757e4779966d1fd0c911c50928345a5c8fedfd81f56c07340051d97c41c6529a5593b854af0f10f832c158b049a53325d13e68b6d34b297be"
client_socket.sendall(f"Hi codegate! Glad to see you again\n".encode())

client_socket.sendall((enc_p[:-32] + "00" * 16 + enc_p[-32:] + "\n").encode())

client_socket.close()
server_socket.close()

이 코드를 실행시켜서 서버를 열고, Client 서버에서 이 서버에 접속을 해주도록 해보자.

그러면 다음과 같이 int error 를 발견할 수 있다.

 

 

아마 00 으로 구성된 블럭이 복호화 후에 int string 이 아닌 다른 이상한 바이트로 변질되기에 오류가 발생하는 것 같다. 하지만 이유는 모르겠지만 끝까지 오류가 나오지 않고 중간에서 끊겨 모두 확인할 수는 없었다.

 

그래도 이 과정을 통해 P 의 앞쪽 바이트를 모두 leak 해볼 수 있었다.

그렇다면 반대로 client_socket.sendall((enc_p[:-32] + "00" * 16 + enc_p[-32:] + "\\n").encode()) 이 부분을 다르게 변질시켜서 이번에는 뒤의 int 를 leak 해보도록 하자. 앞부분을 없애고 뒷부분을 매우 늘려주면 된다. 

 

후후,, 결국 Python 의 Int error 을 통해서 이렇게 p의 원하는 자리수를 leak 할 수 있었다. 

 

따라서 이런 식으로 P 의 모든 수를 확인할 수 있고, P 를 최종적으로 leak 한 결과는 다음과 같다.

from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from hashlib import sha256
p = 139167972701556923691719729095826674624022800081683439292056773540293879230416702328728312842627704836270110246831665146105905423916568797355302669673976390440372528742403601639760049783384774945975256639743140994000549663310264767368556146118016837819793263414602490398883213775195758088528334919695125670223
N = 12659214462730739290777710676401716129364537461971321037157877540193780746910540896819650182970880505880808257029478576966922788012182813161567264480789412487933555048594859387373262444873139956649127172509365160527593844518913325580532608517683946927197373934501869789520791006507064633048240282420120292353010385218838558491852148859208850993839899492722657498287727598894181537635087547896216039380309309759245366183056244723928135316860412330991393425352005295678522825817645977696006156843729550866483053456177362041805254686689400982564781219502910693511366956648759975359689060253588108513703355540146694238377
q = N // p
e = 0x10001
d = inverse(e,(p-1)*(q-1))
u = inverse(q,p)

이렇게 RSA 에 대한 모든 정보를 구할 수 있게 된다.

 

결국 앞서 말했던 file_enc_key 를 모두 구할 수 있게 되었고, 이 file_enc_key 를 구해서 aes key로써 사용하게 되면, flag 를 복호화할 수 있을 것이다. 최종적인 exploit 코드는 다음과 같다.

 

 

이 데이터를 코드에 추가해주면, 최종적으로 다음과 같은 코드가 완성된다. 

from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from hashlib import sha256
p = 139167972701556923691719729095826674624022800081683439292056773540293879230416702328728312842627704836270110246831665146105905423916568797355302669673976390440372528742403601639760049783384774945975256639743140994000549663310264767368556146118016837819793263414602490398883213775195758088528334919695125670223
N = 12659214462730739290777710676401716129364537461971321037157877540193780746910540896819650182970880505880808257029478576966922788012182813161567264480789412487933555048594859387373262444873139956649127172509365160527593844518913325580532608517683946927197373934501869789520791006507064633048240282420120292353010385218838558491852148859208850993839899492722657498287727598894181537635087547896216039380309309759245366183056244723928135316860412330991393425352005295678522825817645977696006156843729550866483053456177362041805254686689400982564781219502910693511366956648759975359689060253588108513703355540146694238377
q = N // p
e = 0x10001
d = inverse(e,(p-1)*(q-1))
u = inverse(q,p)

def aes_dec(ct, key):
    cipher = AES.new(key, AES.MODE_ECB)
    pt_pad = cipher.decrypt(ct)
    return unpad(pt_pad, 16)
def file_encryption_key(p, q, d, u):
    rsa_priv_key = long_to_bytes(p) + long_to_bytes(q) + long_to_bytes(d) + long_to_bytes(u)
    return sha256(rsa_priv_key).digest()
    
flag = bytes.fromhex('05317e8e878cf267b924c9aebca52bfa9169813439997054db6a5522e488a0b34594104098ae60ed147a465d12d797af')
file_enc_key = file_encryption_key(p,q,d,u)
print(aes_dec(flag,file_enc_key).decode())

 

Flag : codegate2022{977afe81a5fabee1ff0f7437cea81a50}

 

이번 문제를 통해서 Python int error 뿐만 아니라 server 과 client 가 소통하는 방식, 즉 socket 에 대해서 더욱 자세히 살펴볼 수 있었다. 다시 한번 선배님께 감사...

'Cryptography > CTF' 카테고리의 다른 글

[Kaist-Postech 2020] - fixed point revenge  (0) 2023.04.19
[Kaist-Postech 2020] - Baby Bubmi  (0) 2023.04.18
[CodeGate 2022] - Hidden Command Service  (0) 2023.04.17
[HITCON 2022] - SuperPrime  (0) 2023.04.17
[HITCON 2022] - Babysss  (0) 2023.04.17
profile

comibear

@comibear

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그