2025. 07. 19 토요일 아주대학교에서 진행된 COSS CTF 본선에 참여하였다
동일하게 출전했고, 최종 결과 4위로 마무리 했다
솔직히 본선을 목표로 나간거였고, 수상은 생각도 못했는데, 수상하니깐 얼떨떨하고 너무 좋다
첫 본선에 첫 수상인 만큼 대회 하루의 일정을 정리해보려고 한다
08:10 - 기상
원래 8시에 일어나려다가 조금 늦었다
천둥 번개가 쳐서 늦게 잠들었더니, 알람을 못 들었다
그래도 늦지는 않았다
08:30 ~ 09:30 - 이동
대회장까지는 40분 정도 걸렸다
씻고 대충 빵 먹고 바로 출발해서 타이트하게 도착했다
401호였나? 거기서 인원 체크를 했는데, 아마 내가 가장 마지막으로 온 것 같았다
09:30 ~ 09:50 - 대회장 이동 및 준비
찐 대회장은 1층에 있었다
솔직히 비오고 그래서 그랬는지는 모르겠는데, 건물이 중후한 느낌이 있었다
근데 대회장은 엄청 깔끔하고 세련되어 있었다
그리고 인테리어가 이뻤다
가니깐 티셔츠도 나눠주셨다
또 다과도 있었는데, 간식들이 엄청 많았다
진짜 좋았다
대회 페이지 로그인 하고, 설명 간략하게 듣고, 와이파이 잡고, 딱 세팅해두니 시작할 시간이 되었다
10:00 ~ 15:30 - 본선 대회
시작하자마자 바로 misc로 달려갔다
mic check 같은거 있을 줄 알았는데 없었고, give me flag를 그리면 플래그를 주는 문제가 있었다
와우! 별거 아닌 문젠데 퍼블이다
꽤나 어려워서 자~알 그려야 한다
그리고 풀 문제를 보았다
팀원중 한 명이 크립토를 맡아서, 크립토 한 문제를 푸는동안에 나도 다른 크립토 문제를 보기 시작했다
Decrypt RSA 라는 문제로 누가 봐도 RSA 문제인걸 알 수 있다
Franklin-Reiter Related Message Attack 같이 생겨서 그냥 GPT한테 코드 던저주니깐 바로 알려줬다
from Crypto.Util.number import long_to_bytes
n = 65756182677698595176310503281797205396723871448656240837096474734817334229057
c1 = 46677618784900339556238162525204951591280608902940779073106839725667571575750
c2 = 46210449457768784989069436594183693519377189857113615576663015891132602693402
k = 1
f = [(-c1) % n, 0, 0, 1]
g = [((k**3 - c2) % n), (3*k**2) % n, (3*k) % n, 1]
def poly_trim(p):
while p and p[-1] == 0:
p.pop()
return p
def poly_divmod(a, b):
a = a.copy()
poly_trim(a); poly_trim(b)
deg_b = len(b) - 1
inv_lc_b = pow(b[-1], -1, n)
q = [0] * (len(a) - len(b) + 1) if len(a) >= len(b) else []
while len(a) >= len(b) and a:
d = len(a) - len(b)
coeff = (a[-1] * inv_lc_b) % n
q[d] = coeff
for i, bi in enumerate(b):
a[d + i] = (a[d + i] - coeff * bi) % n
poly_trim(a)
return poly_trim(q), poly_trim(a)
def poly_gcd(a, b):
poly_trim(a); poly_trim(b)
while b:
_, r = poly_divmod(a, b)
a, b = b, r
# normalize leading coefficient to 1
if not a:
return a
inv_lc = pow(a[-1], -1, n)
return [(coeff * inv_lc) % n for coeff in a]
d = poly_gcd(f, g)
if len(d) != 2:
raise ValueError(f"unexpected gcd degree: {len(d)-1}")
d0, d1 = d
m = (-d0 * pow(d1, -1, n)) % n
flag = long_to_bytes(m)
print(flag.decode())
# flag{fr4nkl1n-r3173r_4774ck}
이것도 퍼블이다
쉬운건 빨리 풀 수 있는데, 어려운 걸 못푼다 ㅜ
그 다음으로는 리버싱으로 갔다
1학년 리버싱 고트가 있어서, 쉬운 문제 하나 풀고 있는건 그 친구가 금방 풀 것 같았고, 나는 다른 문제를 보기 시작했다
딱 보니깐 풀만한게 Save the cat이라서 바로 시도해봤다
apk 파일 하나가 주어졌고, 마침!! Hackquest에 안드로이드 리버싱 내용이 있어서 참고하면서 jadx로 디컴파일해서 소스코드 추출했다
이 파일 4개를 GPT한테 던져주고 "flag를 복구하는 python 코드 짜줘" 하니깐 코드 짜줬다
expected = [
6, 102, 130, 179, 226, 146, 166, 20,
116, 112, 98, 210, 244, 166, 54, 0,
151, 66, 117, 7, 67, 36, 146, 51,
230, 87, 135, 85, 39, 50, 85, 182
]
# 1. Invert column rotations
arr = expected.copy()
for i in range(4):
for _ in range(i):
temp = arr[i]
arr[i] = arr[i + 12]
arr[i + 12] = arr[i + 8]
arr[i + 8] = arr[i + 4]
arr[i + 4] = temp
# 2. Invert row rotations
for i in range(4):
for _ in range(i):
base = i * 4
temp = arr[base]
arr[base] = arr[base + 3]
arr[base + 3] = arr[base + 2]
arr[base + 2] = arr[base + 1]
arr[base + 1] = temp
# 3. Invert XOR with "cat"
cat = [99, 97, 116]
for k in range(len(arr)):
arr[k] ^= cat[k % 3]
# 4. Invert nibble mixing to recover the two 16‑char strings
N = 16
orig1 = []
orig2 = []
for i in range(N):
lo1 = arr[i] >> 4
hi2 = arr[i] & 0xF
lo2 = arr[N + i] >> 4
hi1 = arr[N + i] & 0xF
orig1.append((hi1 << 4) | lo1)
orig2.append((hi2 << 4) | lo2)
flag1 = ''.join(chr(c) for c in orig1)
flag2 = ''.join(chr(c) for c in orig2)
print("var1:", flag1)
print("var2:", flag2)
# var1: flag{y0u_f1nally
# var2: _sav3d_th3_c4ts}
딱 이거 풀고 나니깐 1시간이 지나있었다
이때까지는 좀 빨리 풀었나? 싶어서 나쁘지 않았던 것 같다
그 이후로는 막혔고, 뭘 풀어야할지 몰랐었다
web 중에서 Download이런걸 봤었는데, 어떤식으로 해야할지는 대충 느낌이 왔는데, 잘 안되서 팀원이 바로 풀어줬다
포너블 문제를 보니 죄다 힙에다가 하나는 QEMU였고, 내가 풀 자신이 없었다
그래서 그때 살짝 방황을 했다
문제 슥슥 보다가 12시가 되고 점심을 주셨다
+) 12:00 - 점심
점심이 ㄹㅇ GOAT였다
걍 한솥 도시락정도 줄 것 같았는데, 뭔 도시락에 새우 튀김과 스테이크와 전복이 한 마리 통으로 있었다
후식으로 컵 과일도 주셨다
캬
저거 먹으면서 아라마크 퀄리티 뺨치는 점심이라고 찬양했다
선배팀에서도 아라마크이야기가 나왔던 것 같다 ㄷㄷ
점심도 여유롭지 않고, 걍 노트북 위에서 10분 컷 하고 밥 먹었다
이때쯤되니깐 살짝 몽롱해지면서 정신이 나갔다
그리고 다시 대회를 뛰었다
이 이후로는 포너블 한 문제와 리버싱 한 문제를 풀기 시작했다
포너블은 힙이였고, "힙"하지 않은 나는 풀지 못하였다
리버싱 base 문제를 우리 팀이 못 풀었는데, 다른 팀에서는 왜 많이 푼지는 모르겠다
이 텍스트 파일 3개 주었고, 도저히 모르겠어서 걍 GPT나 엄청 돌렸다
import base64
encode_data = (
"fniu3dQC3Wcimkeojn75nttmng3QQBxXMQ4cX9DkNMNbUCBhUY0e192zGHiFxwQYxVc7SPr0oPoW"
"DjXVTukyKWtJfW3t3kxqmn4KjdgTjgp3nBDnQQNHMtBUXMiPNCQ9NYc4U9e81M7aGwt+GV3gxbxl"
"SHRzojqFDunYDF67A+v="
)
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
cipher = base64.b64decode(encode_data)
cand = "".join(
alphabet[(cipher[i] - i) & 0x3F]
for i in range(len(cipher))
)
pad = "=" * ((4 - len(cand) % 4) % 4)
flag_raw = base64.b64decode(cand + pad)
print(flag_raw.decode())
아직도 도대체 왜 이렇게 푸는지는 모르겠는데 1시 40분쯤에 풀었다
이게 내가 푼 마지막 문제였다
이때쯤 7위쯤으로 추락했고, 좀 문제를 직감했다
2시에 웹 팀원이 퍼블을 땄고, 2등으로 올라섰다
바로 추격당했고, 마지막으로 3시쯤 후배가 리버싱 문제를 풀며 4위로 대회를 마무리 했다
3시부터는 포너블을 시도해봤지만, 실패했다
많이 풀었는데 솔직히 쉬운 문제는 아닌 것 같다
이렇게 대회가 끝이 났다
ㄹㅈㄷ인건 2위 TheFatCat에서 끝나기 5분전 QEMU 정신나간 문제를 푸셔서 1등을 했다
TheKing이 실시간으로 상금 60만원이 증발해버린걸 보았다
ㄷㄷ
GOAT들의 경쟁은 무섭다
15:40 ~ 16:30 - ENKI 강연
10분정도 참가자들과 이야기 하고, 출제자 엔키에서 롸업을 알려줬다
웹 2개 포너블 2 문제 솔브 해주셨고, 아직 풀기까지는 연습이 더 필요한 것 같다
그리고 엔키 부사장님께서 강연을 해주셨다
이게 그냥 보안윤리 또 설명하면 좀 지루해질 수도 있었는데, 실제 공공기관이 어떤 원리로 공격을 당했는데 생각보다 세세하고, 자세하게 설명을 해주셨다
HackQuest처럼 사고의 흐름이 어떻게 흘러가야하는지를 잘 짚어주시면서 설명해주셔서 이해가 잘 되었다
그리고 "50%의 확률로 애니 프사는 과학이다" 를 주장하셨다
뭐 이렇게 강연 해주시고 시상식을 했다
16:30 ~ 16:50 - 시상식 및 사진 촬영
드디어 시상식을 했다
팀원 단체 사진도 있는데, 모자이크 하니깐 너무 범죄자 같아서 그냥 부계에 올렸다
첫 대회 본선인데, 10등만 피하자였는데, 수상을 해서 너무 좋았다
17:30 ~ 19:30 - 회식 in 아웃닭
디미고 3팀이서 치킨집을 갔다
10명이서 갔다
5마리 시켜서 이야기도 하고, 먹으면서 놀았다
인당 15000원에 치킨이라 정말 좋았다
또 아주대에서 기념 선풍기와 티셔츠도 주셨다
정말 감사하다
여하튼 이렇게 아주대학교 CTF를 마치게 되었다
처음 예선을 나갈때도, 본선만본선만!을 외치며 나간 대회였고, 내 생애 처음으로 본선을 나갔다
그 상황에서 첫 수상을 해서 너무 좋다
포너블을 더 공부해야겠다
정말 좋은 경험이였다!
'CTF' 카테고리의 다른 글
[ 제 31회 해킹 캠프 후기 ] (7) | 2025.09.01 |
---|---|
[ 2025 제 1회 경기도 사이버 보안 캠프 후기 ] (2) | 2025.08.25 |
[2025 COSS 아주대 CTF - 예선 ] - Write Up & 후기 (0) | 2025.06.23 |
[2025 Codegate 예선] - Write Up & 후기 (0) | 2025.05.26 |
[2025 DIMI CTF Write Up] - Prob by pandas. with 후기 (3) | 2025.03.25 |