源码
'''
Hints: Flag在环境变量中
'''
from typing import Optional
import pydash
import bottle
//由此可得这是⽤ Python 的 bottle 框架构建的⼀个 Web 应⽤
__forbidden_path__=['__annotations__', '__call__', '__class__', '__closure__',
'__code__', '__defaults__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__get__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__init_subclass__',
'__kwdefaults__', '__le__', '__lt__', '__module__',
'__name__', '__ne__', '__new__', '__qualname__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__sizeof__', '__str__', '__subclasshook__', '__wrapped__',
"Optional","func","render",
]
__forbidden_name__=[
"bottle"
]
__forbidden_name__.extend(dir(globals()["__builtins__"]))
//__forbidden__函数,黑名单
def setval(name:str, path:str, value:str)-> Optional[bool]:
if name.find("__")>=0: return False
for word in __forbidden_name__:
if name==word:
return False
for word in __forbidden_path__:
if path.find(word)>=0: return False
obj=globals()[name]
try:
pydash.set_(obj,path,value)
except:
return False
return True
def set_value():
name = bottle.request.query.get('name')
path=bottle.request.json.get('path')
if not isinstance(path,str):
return "no"
if len(name)>6 or len(path)>32:
return "no"
value=bottle.request.json.get('value')
return "yes" if setval(name, path, value) else "no"
def render_template():
path=bottle.request.query.get('path')
if path.find("{")>=0 or path.find("}")>=0 or path.find(".")>=0:
return "Hacker"
return bottle.template(path)
bottle.run(host='0.0.0.0', port=8000)
一、Bottle 框架的双接口
# 核心路由与污染函数
'/setValue') .post(
def set_value():
name = bottle.request.query.get('name')
path = bottle.request.json.get('path')
if len(name) > 6 or len(path) > 32: return "no"
value = bottle.request.json.get('value')
return "yes" if setval(name, path, value) else "no"
get('/render') .
def render_template():
path = bottle.request.query.get('path')
if "{".encode() in path.encode() or ".".encode() in path.encode():
return "Hacker"
return bottle.template(path)
- /setValue 接口
用于处理POST请求、接收名称、路径和值,调用了setval函数,在此路由中,获取了名称name、路径path,检查path是否为字符串类型,检查name的长度是否超过6,path长度是否超过32,从请求的JSON数据中获取值value - /render 接口
用于处理GET请求,接收模板⽂件路径,并对其进⾏渲染,在此路由中,从请求查询的参数中获取模板⽂件路径path,并检查路径中是否包含 { 、 } 或 . ,最后调⽤bottle.template函数对模板⽂件进⾏渲染并返回结果
setval(name,path,value)
name----要污染的对象
path----被污染对象的功能点路径
value---想让他成为的值
name=pydash
path:helpers.RESTRICTED_KEYS
value:[]
解析:寻找pydash库中helpers.RESTRICTED_KEYS的地址,然后设置为空
name=setval
path:_globals_.bottle.TEMPLATE_PATH
value:[../../../../proc/self/]
解析:寻找setval函数,将该函数往上查询globals.(__globals__
会返回一个包含该函数全局命名空间的字典,可通过setval
函数的作用域链向上访问)
再调用bottle框架中的TEMPLATE_PATH(指定模板文件所在的路径),将路径设置为/proc/self/这样访问path时会自动跳转到这个路径下,当path=environ时,直接渲染环境变量文件
二、原型链污染
1. 绕过 pydash 的安全限制pydash 库的helpers.py
中定义了禁止修改的关键属性:
RESTRICTED_KEYS = ("__globals__", "__builtins__")
RESTRICTED_KEYS 通常是一个集合(像列表、元组或者集合对象),其作用是存储受限的键名。这些键名往往是出于安全、性能或者避免冲突等原因,在程序里不允许被使用或者访问的。
这里有两个元素,分别是__globals __和__builtins__,到此思路有了,先污染key函数,使它允许我们使用globals,借此污染bottle.TEMPLATE_PATH,使它路径指向../../../../proc/self/,最后在 /render 路由下 GET 传参 path 为 environ ,对其进行渲染,就可以获取环境变量了
name=pydash # 污染对象是 pydash
path="helpers.RESTRICTED_KEYS" # 路径就是 helpers ⽂件中的 RESTRICTED_KEYS
value=[] # 清空受限键列表,修改成为的值就是空列表
解除对__globals__
和__builtins__
的修改限制
2. 篡改模板路径指向环境变量
bottle.TEMPLATE_PATH
,将模板路径指向存储环境变量的系统文件name=setval # 污染对象是 setval
path:"__globals__.bottle.TEMPLATE_PATH" # 路径是模板⽂件路径
value:[../../../../proc/self] # 修改为的值是存储环境变量⽂件路径,因为题⽬提
示flag在环境变量中
三、实战
通过两步污染操作突破框架限制,最终利用模板引擎读取环境变量。该案例揭示了原型链污染在框架级漏洞中的典型利用方式,开发中需严格控制对象属性的修改权限。
原文始发于微信公众号(零漏安全):Bottle 框架漏洞攻防实录:从原型链污染到 Flag 获取的完整渗透路径
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论