浅学SSTI漏洞原理(基于flask)
本文最后更新于 5 天前,其中的信息可能已经有所发展或是发生改变。

Flask

flask是一个使用Python编写的轻量级Web应用框架

特点:良好的文档,丰富的插件,包含开发服务器和调试器,集成支持单元测试,RESTful请求调度,支持安全cooies,基于Unicode。

Python可直接使用flask启动一个Web服务页面。

Flask基本架构

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
return "helloworld"

if __name__=='__main__':
app.run()

成功运行,helloworld,可加上debug功能

(debug=True,host="0.0.0.0",port=??)//在创建时定位错误,改配置会自动重启,也可改端口,但是得手动重启(注意端口冲突问题)并且避免不安全端口存在

Flask变量及方法

Flask变量规则

通过向规则参数添加部分,可动态构建URL

格式化字符串

from flask import Flask
app = Flask(__name__)

@app.route('/hello/<name>')
def hello(name):
return "hello %s"%name
@app.route('/int/<int:postid>')
def id(postid):
return " %d " %postid
if __name__=='__main__':
app.run()

变量传参方法

HTTP方法

注:要放到同级目录下的templates文件夹下,这是flask内置定义好的,不然会找不到文件

from flask import Flask

app = Flask(__name__)

@app.route('/login',methods=['POST','GET'])
def login():
if request.methods == 'POST':
  print(1)
  user = request.form['ben']
  return redirect(url_for('success',name = user))
else:
  print(2)
  user = request.args.get('ben')
  return redirect(url_for('success',name = user))

if __name__ == '__main__':
  app.run(debug=True)
//redirect重定向/success/user
<html>
<body>
<from action = "http://localhost:5000/login" method = "post">
  <p>Enter Name:</p>
  <p><input type = "text" name = "ben" /></p>
  <p><input type = "submint" value = "submit" /></p>
</from>
</body>
</html>

Flask模版

视图函数的主要作用是生成请求的响应

1.处理业务逻辑

2.返回响应内容

把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本

使用模版:使用静态的页面html展示动态的内容

render_template

加载html文件,默认文件路径在templates

这部分先不一一赘述了,后面会单开一篇文章学习

模版注入漏洞介绍

SSTI漏洞是什么?

服务器端模板注入(Server – Side Template Injection,SSTI)漏洞,是指攻击者能够利用应用程序对模板引擎的使用,将恶意的模板代码注入到服务器端的模板渲染过程中,使得这些恶意代码在服务器端被执行,从而获取敏感信息、执行系统命令或控制服务器等恶意操作的一种安全漏洞。

Flask漏洞

flask代码不严谨,可能造成任意文件读取和RCE远程控制后台系统

漏洞成因:

渲染模版时,没有严格控制对用户的输入;

使用了危险的模版,导致用户可以和flask程序进行交互

(flask是基于python开发的一种web服务器,那么也就意味着如果用户可以和flask进行交互的话,就可以执行python代码,比如eval,system,file等等之类的函数)

实例演示:

from flask import Flask, request, render_template_string

app = Flask(__name__)


@app.route('/', methods=['GET'])
def index():
  str = request.args.get('sun')
  html_str = """
  <html>
      <head></head>
      <body>{{str}}</body>
  </html>
  """
  return render_template_string(html_str, str=str)


if __name__ == '__main__':
  app.debug = True
  app.run('127.0.0.1', 8082)

以GET的方式获取sun的值。并赋值给str

str值通过render_template_string加载到body中间,其中{{str}},会被预先渲染转义,然后才会输出,不会被渲染执行,而是直接当文本输出

漏洞演示

format()函数解释:

python中用于格式化字符串的内置函数,能让字符串更符合特定需求。

基本使用

format() 函数通过花括号 {} 作为占位符,将指定的值填充到字符串中

name = "Tom"
age = 20
print("My name is {} and I'm {} years old.".format(name, age))
进阶用法
  • 使用关键字参数:通过关键字参数使代码可读性更强,如:
city = "London"
country = "UK"
print("I live in {city}, {country}.".format(city=city, country=country))

输出 I live in London, UK.

  • 嵌套花括号转义:若字符串中本身需包含花括号,可通过双花括号 {{}} 进行转义,例如:
print("The price is {{{}}} dollars.".format(100))

输出 The price is {100} dollars.

演示:

from importlib.resources import contents
import time
from flask import Flask, request, render_template_string

app = Flask(__name__)


@app.route('/', methods=['GET'])
def index():
  str = request.args.get('sun')
  html_str = """
  <html>
      <head></head>
      <body>{0}</body>
  </html>
  """.format(str)
  return render_template_string(html_str)


if __name__ == '__main__':
  app.debug = True
  app.run('127.0.0.1', 8082)

通过format()函数先直接向{}中写入字符串,然后才会经rander_template_string解析,所以可以顺序颠倒,输入的字符串可以被rander_template_string当做命令执行

判断模版类型

实际也是一种注入漏洞,判断上什么模版就很重要了

根据图中有无响应,可对常见模版进行判断(其余模版还有很多)

此部分会后续持续更新~

继承关系和魔术方法

漏洞利用的关键(逻辑和利用方法)

继承关系(父类和子类)

子类调用父类下的其他子类

Python flask 脚本没有办法直接执行Python指令

object是父子关系的顶端,所有的数据类型最终的父类都是object

利用逻辑如图:

演示:

class A:pass
class B(A):pass
class C(B):pass
class D(B):pass
c = C()
print(c.__class__.__base__)
//<class '__main__.B'>
print(c.__class__.__base__.__base__.__base__)
//<class 'object'>
print(c.__class__.__mro__)
//(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
//罗列了各个类
print(c.__class__.__mro__[1].__subclasses__())
//[<class '__main__.C'>, <class '__main__.D'>]特定查看B类下的所有子类

魔术方法

__class__查找当前类型所属的对象
__base__沿着父子类的关系往上走一个
__mro__查找当前类对象的所有的继承类
__init__查找类是否重载,重载是指程序在运行时就已经加载好了这个模块到内存中,如果出现wrapper字眼,说明没有重载(类的初始化方法)返回的类型是function
__globals__函数会以字典的形式返回当前对象的全部全局变量
__builtins__提供对Python的所有“内置”标识符的直接访问,即先加载内嵌函数再调用
__import__   : 动态加载类和函数,用于导入模块,经常用于导入os模块(例如__import__('os').popen('ls').read())
url_for :flask的方法,可以用于得到__builtins__
lipsum :flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{{lipsum.__globals__['os'].popen('ls').read()}}
config:当前application的所有配置。
popen():执行一个 shell 以运行命令来开启一个进程
request.args.key:获取get传入的key的值

检查漏洞

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇