카테고리 없음

순천향대학교 제 21회 정보보호 페스티벌 예선전 풀이 및 후기

Namchun 2023. 8. 28. 02:10

8월 11일부터 13일 동안 3일에 걸친 yisf 예선전을 처음으로 참가하였다.

점수는 4340점으로 11등을 하여 본선에 진출할 수 있었다. 그럼 바로 풀이를 시작하겠다.

 

Web

Magic shop

url에 접속을 하면

 

이렇게 여러 아이템을 살 수 있는 페이지가 나오고, 그 중에서 플래그를 선택해서 구매해보면

 

이런 페이지가 나온다

 

burp suite를 사용하여 요청을 잡아보면

이런 식으로 money를 전달하는 것을 볼 수 있는데 money에 값에 아무 것도 없다.

그래서 money의 값을 flag의 값보다 높게 전달을 해보았더니

flag를 얻을 수 있었다

YISF{W0w_Pa4mete4_Man1Pulat10n}

Customer Service

(해당 문제는 언인텐으로 풀린 문제이기에 해당 풀이는 의도한 바와 전혀 다르다)

문제 파일들 중에서 bot.js라는 파일을 살펴보니

 

ADMIN_ID와 ADMIN_PW가 있어서 로그인을 하였고, 그 후에 마이페이지를 접속해보면

 

이렇게 토큰 값이 있었다. 이것을 view.php에 넣으니

 

flag 파일을 읽을 수 있었고. flag를 제대로 읽어보고 나서 CSS Injection을 요구하는 문제였던 것으로 추측할 수 있었다

YISF{Cust0m3r_S3rv1ce_Rp0_C5S_1nj3ct1on}

Reversing

basic-rev

파일을 ida로 열어보면 

 

입력받은 v10의 길이가 29이어야 하고 아래의 연산값과 각각 비교하는 값이 달라야지만 else문으로 들어가 flag가 완성이 될 것으로 보인다.

 

파이썬의 z3 모듈을 이용해서 풀이를 진행하였다

from z3 import *

v10 = [BitVec('v10%i'%i, 8) for i in range(29)]

s = Solver()

s.add((v10[2] + v10[4] - v10[5] * v10[1] - v10[0] - v10[6] * v10[3]) == 0xFFFFDC99)
s.add((v10[7] - v10[2] + v10[6] * v10[4] * v10[3] - v10[5] - v10[1]) == 0xA6062)
s.add((v10[6] + v10[7] + v10[4] + v10[3] - v10[2] * v10[5] + v10[8]) == 0xFFFFF13F)
s.add((v10[5] + v10[7] + v10[6] - v10[3] * v10[4] - v10[9] - v10[8]) == 0xFFFFDE7F)
s.add((v10[8] + v10[7] + v10[5] - v10[4] * v10[10] + v10[9] + v10[6]) == 0xFFFFD682)
s.add((v10[9] + v10[5] + v10[6] - v10[11] + v10[10] * v10[7] + v10[8]) == 0x1244)
s.add((v10[9] + v10[7] + v10[12] + v10[11] + v10[9] - v10[10] - v10[6]) == 0xEC)
s.add((v10[8] + v10[7] - v10[9] + v10[10] * v10[11] - v10[12] - v10[13]) == 0x1036)
s.add((v10[11] + v10[14] + v10[8] + v10[9] * v10[13] - v10[12] + v10[10]) == 0x2683)
s.add((v10[14] + v10[11] - v10[12] + v10[9] * v10[15] - v10[10] + v10[13]) == 0x2A78)
s.add((v10[16] + v10[14] + v10[12] + v10[10] - v10[11] - v10[13] - v10[15]) == 0x52)
s.add((v10[14] - v10[11] - v10[12] + v10[17] - v10[15] * v10[13] + v10[16]) == 0xFFFFD2AF)
s.add((v10[17] + v10[12] * v10[13] - v10[14] + v10[15] * v10[16] - v10[18]) == 0x428F)
s.add((v10[13] + v10[14] - v10[15] * v10[16] + v10[17] * v10[18] - v10[19]) == 0xBC)
s.add((v10[17] * v10[18] + v10[16] + v10[14] - v10[15] - v10[20] * v10[19]) == 0xFFFFE8BF)
s.add((v10[18] + v10[16] + v10[15] - v10[17] - v10[19] + v10[20] * v10[21]) == 0x2CBF)
s.add((v10[17] * v10[18] + v10[16] + v10[19] * v10[20] - v10[21] + v10[22]) == 0x4306)
s.add((v10[17] - v10[18] - v10[19] * v10[20] - v10[21] * v10[22] - v10[23]) == 0xFFFFAC31)
s.add((v10[19] * v10[20] + v10[18] + v10[21] * v10[22] - v10[23] + v10[24]) == 0x53F6)
s.add((v10[19] + v10[20] - v10[25] * v10[24] * v10[23] * v10[22] * v10[21]) == 0xFF9393CB)
s.add((v10[23] + v10[20] - v10[21] * v10[22] - v10[24] * v10[25] + v10[26]) == 0xFFFFB9D1)
s.add((v10[21] * v10[26] - v10[24] - v10[25] + v10[22] * v10[23] * v10[27]) == 0x6942D)
s.add((v10[27] + v10[23] * v10[22] + v10[24] - v10[25] * v10[26] - v10[28]) == 0xFFFFECEC)

s.add(v10[0] == 0x59)
s.add(v10[1] == 0x49)
s.add(v10[2] == 0x53)
s.add(v10[3] == 0x46)
s.add(v10[4] == 0x7b)
s.add(v10[28] == 0x7d)

if str(s.check()) == 'sat':
    print(s.model())

# YISF{2O23_Y1sf_r00ting_4_???}

하지만 해당 코드에서는 문제가 있었는데 24번 인덱스까지 잘 출력이 되다가 25, 26, 27번 인덱스에서 ascii의 범위를 넘어서는 숫자들이 나왔다.

그 이유는 많은 경우의 수들 중에서 저런 큰 숫자들로도 solver가 문제를 해결했다는 것이기 때문에, 이러한 경우의 수를 줄이기 위해 각 인덱스마다 33 ~ 126까지 범위를 지정해주는 코드를 추가하였다.

for i in v10:
    s.add(i >= 33); s.add(i <= 126)

 

최종 코드

from z3 import *

v10 = [BitVec('v10%i'%i, 8) for i in range(29)]

s = Solver()

for i in v10:
    s.add(i >= 33); s.add(i <= 126)

s.add((v10[2] + v10[4] - v10[5] * v10[1] - v10[0] - v10[6] * v10[3]) == 0xFFFFDC99)
s.add((v10[7] - v10[2] + v10[6] * v10[4] * v10[3] - v10[5] - v10[1]) == 0xA6062)
s.add((v10[6] + v10[7] + v10[4] + v10[3] - v10[2] * v10[5] + v10[8]) == 0xFFFFF13F)
s.add((v10[5] + v10[7] + v10[6] - v10[3] * v10[4] - v10[9] - v10[8]) == 0xFFFFDE7F)
s.add((v10[8] + v10[7] + v10[5] - v10[4] * v10[10] + v10[9] + v10[6]) == 0xFFFFD682)
s.add((v10[9] + v10[5] + v10[6] - v10[11] + v10[10] * v10[7] + v10[8]) == 0x1244)
s.add((v10[9] + v10[7] + v10[12] + v10[11] + v10[9] - v10[10] - v10[6]) == 0xEC)
s.add((v10[8] + v10[7] - v10[9] + v10[10] * v10[11] - v10[12] - v10[13]) == 0x1036)
s.add((v10[11] + v10[14] + v10[8] + v10[9] * v10[13] - v10[12] + v10[10]) == 0x2683)
s.add((v10[14] + v10[11] - v10[12] + v10[9] * v10[15] - v10[10] + v10[13]) == 0x2A78)
s.add((v10[16] + v10[14] + v10[12] + v10[10] - v10[11] - v10[13] - v10[15]) == 0x52)
s.add((v10[14] - v10[11] - v10[12] + v10[17] - v10[15] * v10[13] + v10[16]) == 0xFFFFD2AF)
s.add((v10[17] + v10[12] * v10[13] - v10[14] + v10[15] * v10[16] - v10[18]) == 0x428F)
s.add((v10[13] + v10[14] - v10[15] * v10[16] + v10[17] * v10[18] - v10[19]) == 0xBC)
s.add((v10[17] * v10[18] + v10[16] + v10[14] - v10[15] - v10[20] * v10[19]) == 0xFFFFE8BF)
s.add((v10[18] + v10[16] + v10[15] - v10[17] - v10[19] + v10[20] * v10[21]) == 0x2CBF)
s.add((v10[17] * v10[18] + v10[16] + v10[19] * v10[20] - v10[21] + v10[22]) == 0x4306)
s.add((v10[17] - v10[18] - v10[19] * v10[20] - v10[21] * v10[22] - v10[23]) == 0xFFFFAC31)
s.add((v10[19] * v10[20] + v10[18] + v10[21] * v10[22] - v10[23] + v10[24]) == 0x53F6)
s.add((v10[19] + v10[20] - v10[25] * v10[24] * v10[23] * v10[22] * v10[21]) == 0xFF9393CB)
s.add((v10[23] + v10[20] - v10[21] * v10[22] - v10[24] * v10[25] + v10[26]) == 0xFFFFB9D1)
s.add((v10[21] * v10[26] - v10[24] - v10[25] + v10[22] * v10[23] * v10[27]) == 0x6942D)
s.add((v10[27] + v10[23] * v10[22] + v10[24] - v10[25] * v10[26] - v10[28]) == 0xFFFFECEC)

s.add(v10[0] == 0x59)
s.add(v10[1] == 0x49)
s.add(v10[2] == 0x53)
s.add(v10[3] == 0x46)
s.add(v10[4] == 0x7b)
s.add(v10[28] == 0x7d)

if str(s.check()) == 'sat':
    print(s.model())

# YISF{2O23_Y1sf_r00ting_4_YoU}

YISF{2O23_Y1sf_r00ting_4_YoU}

Time Capsule

우선 파일이 upx로 언패킹되어있어 upx -d CHALL.EXE로 풀어주었다.

그 후에 파일을 실행해보았는데

 

"현재 PC에서는 이 앱을 실행할 수 없습니다"라는 문구가 뜨며 실행이 되지 않았다.

문제를 확인하기 위해 hxd로 열어보니 

 

MZ로 시작하긴 하지만 평소에 보던 다른 pe 파일들과는 다르게 "This program cannot be run in DOS mode"라는 문자열이 없었다.

이는 윈도우 실행 파일이 아닌 dos 실행 파일이라는 것이었기에 dosbox를 통해 실행을 해보았다

실행을 하였더니 flag가 출력되었다

YISF{Wh47_15_7H12?84ck_70_7he_d02?}

MISC

yisf2023 start

늘 먹던 flag

YISF{welc0m2_t0_the_YISF}

Where is dobby

jpg 파일을 열어보면

 

이런 대화를 나누는 채팅을 볼 수 있는데 dobby는 '붉은 바다거북이'라고 한다

그래서 뭐 어쩌라는 건지.. 하며 스테가노그래피, binwalk 등을 사용해보았지만 아무것도 나오지 않았다

 

그러던 중 구글에 sea turtle "dobby"라고 검색을 해보았더니

https://conserveturtles.org/stctrackingmap/?id=326

이러한 링크를 발견하였고 실제로 dobby라는 붉은 바다거북이의 위치를 추적해주는 사이트가 나왔다

 

(dobby 이외에도 여러 바다거북이의 위치를 하루마다 추적하여 알려준다)

 

문제에서는 도비의 위치가 마지막으로 확인된 날짜를 찾고 싶다고 하였으니 flag 형식에 맞추어

flag{YYYY_MM_DD_HH_MM_(AM or PM)} -> 도비의 위치가 하루마다 바뀌기 때문에 대회 3일 동안 flag의 날짜값이 여러 번 변경되었다

sherlock

jpg 파일을 열어보면

 

이런 편지가 있길래 위의 문제처럼 스테가노그래피, binwalk 등을 하였지만 문제의 태그가 osint로 되어있었기에 다른 방법을 찾아보았다.

그러던 중 디스코드에 mnme3이라는 운영진 분이 계신 것을 확인하였고, 보자마자 '아 이거다!'하면서 바로 디엠을 보냈다

티켓으로 연락해달라는 말에 바로 티켓을 열어 다시 보내보았다

 

;;; 이름도 바꿔가며 티켓에 부탁을 해보았지만 이 방법이 아니었다..

(이걸 본 운영진분들은 무슨 생각이 들으셨을까..) 정말 부끄러웠다.

 

무튼 osint 문제였기에 이번에는 'ctf sherlock'이라는 키워드로 검색을 해보았다.

그러던 중 'sherlock'이라는 파이썬 도구가 있다는 것을 알게되었다

 

https://ctftime.org/writeup/28008

이 sherlock이라는 도구는 인자로 건네준 닉네임을 토대로 온라인 상에서 존재하는 페이지 링크를 몽땅 가져오는 역할을 수행한다. 

바로 mnme3를 인자로 주고 실행을 해보았더니

 

위와 같이 21개의 결과가 나오게 되었고 첫 번째 링크에 들어가보니 flag를 얻을 수 있었다

YISF{jeW3LrY_WaS_OR16!na1lY}

Forensic

PDF Decrypt

확장자가 없는 zip이라는 이름의 파일을 받을 수 있는데, 이것을 hxd로 열어보면

 

50 4B 03 04로 시작하는 것으로 보아 .zip 확장자 파일임을 알 수 있다.

 

.zip 파일로 변경 후 압축 해제를 해주면 '문서.pdf'가 나오게 된다

 

이 문서에는 비밀번호가 설정되어 있었는데 pdfcrack이라는 도구를 통해 비밀번호를 알아낼 수 있었다

password : 182353

 

비밀번호를 입력하고 pdf를 열면 한 여성의 사진이 나오는데 

그 사진을 따로 pdf 전용 viewer를 통해 삭제하면 그 뒤에 숨겨져 있던 flag를 얻을 수 있었다

 

YISF{PDF_RC4_1S_E4SY_T0_CR4CK}

Broken png

zip 파일 속의 flag.png를 열어보면

 

지원되지 않는 형식이라고 나온다

혹시나 헤더가 손상되지는 않았는지 검사해보려 TweakPNG 도구를 사용해보았다

 

생각했던 IHDR이 아닌, 실질적으로 이미지가 표현이 되는 청크인 IDAT의 Length가 0으로 되어있었다.

다시 hxd로 살펴보면

IDAT가 굉장히 많이 검색되는데 왜 그런 것일까?

 

PNG의 청크 중 IDAT라는 청크의 구조를 살펴보면

"IDAT"라고 검색되는 Chunk Type 이전에 4바이트 값으로 각 IDAT의 Length가 저장되어 있다.

 

현재 png 파일에는 모든 IDAT의 Length가 위의 그림과 같이 00 00 00 00으로 변경되어 있고 이를 제대로 된 Length로 수정해주면 될 것이다.

그렇다면 이 부분에는 어떤 값을 넣어야 하는가? 바로 현재 IDAT 시작부분부터 각각 바로 다음에 있는 IDAT 직전까지의 offset을 계산해서 넣어주면 될 것이다.

 

여러 시도 끝에 첫 IDAT에는 FF A5를 넣어주어 다음 IDAT의 직전까지를 현재 IDAT의 데이터로 해석할 수 있도록 해주었고

두 번째 IDAT 부터는 다음 IDAT와의 간격이 같았기에 모두 FF F4로 넣어주어 png 파일을 제대로 해석할 수 있도록 만들어주었다.

참고 : https://mineeeee.tistory.com/entry/PNG-파일구조


바로 위에 있는 Broken PNG 문제는 퍼블을 딴 문제라 정말 기분이 좋았었고

where is dobby, sherlock 이 문제들은 재밌다고 해야할 지는 모르겠지만 osint 수집 능력이 부족하다는 것을 깨닫게 해준 문제였다고 생각한다.