지난 글에 이어서
ProxyLogon 공격 과정을
분석한 내용입니다.
# Exchange 구조
Exchange의 웹 사이트는
IIS를 기반으로 하며,
Front-End site(Default Web site)와
Back-End site(Exchange Backend)로
구성되어 있습니다.
Front-End site(Default Web site)
- 사용자 UI
- Port 80 & 443 사용
Back-End site(Exchange Backend)
- 비즈니스 로직
- Port 81 & 444 사용
취약점은 이 두 Site 간
전달되는 데이터에에 대한
보안 검토가 미흡하여 발생합니다.
# CVE-2021-26855(SSRF)
SSRF 취약점은
공개된 PoC 코드의 공격 위치에 따라
연관된 모듈이 다르기 때문에
각각 작성했습니다.
- 타 사이트 접속
Front-End site로 들어온 사용자 요청은
내부 정보 추가 등의 과정을 거친 후
1:1로 연결된 Back-End site의 모듈로
전달됩니다.
사용자가 접속한 페이지에 따라
이 과정을 처리하는 모듈이 다르며,
각 모듈은 동일한 하나의 모듈을
상속받아 각 페이지 특성에 맞게
추가 구현되어 있습니다.
사용자 접속 Page | 사용자 요청 처리 Module | 최상위 Module |
/owa | OwaProxyRequestHandler | ProxyRequestHandler |
/ews | EwsProxyRequestHandler | |
/ecp | EcpProxyRequestHandler |
FrontEndHttpProxy.dll - HttpProxy.ProxyRequestHandler.cs
Front-End site에서 Back-End로 사용자 데이터를
전달하는 모듈들의 기본 모듈입니다.
이 모듈의 GetTargetBackEndServerUrl() 메서드는
Front-End Site에서 전달된 사용자 요청을 처리할
Back-End Site의 주소(URI)를 정의합니다.
코드를 자세히 살펴보면
urlAnchorMailbox의 값이 Null일 때,
Back-End site의 Host 값을
this.AnchoredRoutingTarget.BackEndServer.Fqdn에서
가져오는 것을 확인할 수 있습니다.
UrlAnchorMailbox urlAnchorMailbox = this.AnchoredRoutingTarget.AnchorMailbox as UrlAnchorMailbox;
if (urlAnchorMailbox != null) {
result = urlAnchorMailbox.Url;
} else {
UriBuilder clientUrlForProxy = this.GetClientUrlForProxy();
clientUrlForProxy.Scheme = Uri.UriSchemeHttps;
clientUrlForProxy.Host = this.AnchoredRoutingTarget.BackEndServer.Fqdn;
clientUrlForProxy.Port = 444;
if (this.AnchoredRoutingTarget.BackEndServer.Version < Server.E15MinVersion) {
this.ProxyToDownLevel = true;
RequestDetailsLoggerBase<RequestDetailsLogger>.SafeAppendGenericInfo(this.Logger, "ProxyToDownLevel", true);
clientUrlForProxy.Port = 443;
}
result = clientUrlForProxy.Uri;
}
FrontEndHttpProxy.dll - HttpProxy.OwaResourceProxyRequestHandler.cs
만약, 사용자가 /owa 페이지로 접속한다면,
this.AnchoredRoutingTarget.BackEndServer에 대한 정보는
OwaResourceProxyRequestHandler 모듈의
ResolveAnchorMailbox() 메소드에서 정해지며,
사용자 브라우저의
"X-AnonResource-Backend" 쿠키의 값을
보안 필터링 없이 그대로 사용합니다.
취약점 Point.
ResolveAnchorMailbox() 메소드에서
"X-AnonResource-Backend" 쿠키의 값을
사용하여 BackEndServer를 정할 때
보안 검토를 거치지 않기 때문에,
변조된 쿠키의 값을 통해
BackEndServer의 값을 조작하여
확인되지 않은 서버로 접속하도록
유도할 수 있습니다.
수동 테스트
Prxoy Tool인 Burp Suite를 통해
직접 취약점을 테스트한 결과입니다.
공격에 성공하여,
공격자의 의도대로
Exchange 서버가 Naver에 접속하여
그 결과를 공격자에게 반환합니다.
쿠키의 값을 Exchange가 있는 네트워크의
다른 사이트나 서버로 하면,
외부에서 접근이 불가능한 내부 사이트로도
접근하여 공격이 가능합니다.
- 인증 우회
동일한 CVE 취약점으로
Exchange 서버의 인증을
우회할 수 있는 취약점입니다.
위에서는 /owa 페이지 접속 시 사용하는
"X-AnonResource-Backend" 쿠키를 대상으로 했으나,
이번에는 /ecp 페이지 접속 시 사용하는
"X-BEResource" 쿠키를 대상으로 합니다.
FrontEndHttpProxy.dll - HttpProxy.BEResourceRequestHandler.cs
만약, 사용자가 /ecp 페이지로 접속한다면,
this.AnchoredRoutingTarget.BackEndServer에 대한 정보는
BEResourceRequestHandler 모듈의
ResolveAnchorMailbox() 메소드에서 정해지며,
사용자 브라우저의
"X-BEResource" 쿠키의 값을
보안 필터링 없이 그대로 사용합니다.
FrontEndHttpProxy.dll - HttpProxy.ProxyRequestHandler.cs
이후, ProxyRequestHandler Class의
GetTargetBackEndServerUrl() 메소드에서
Front-End Site에서 전달된 사용자 요청을 처리할
Back-End Site의 주소(URI)를 정의합니다.
여기서 서버의 버전에 따라 접속 Port가 정해지며,
취약점이 발생하기 위해서는
버전을 E15MinVersion 값보다 크게 설정하여
정보를 내부 서버(Back-End Site)에서 찾도록
해야합니다.
ApplicationLogic.dll - BackEndServer.FromString
위의 BEResourceRequestHandler.ResolveAnchorMailbox() 메소드에서
입력받은 쿠키 값은
BackEndServer.FromString() 메소드를 거치게 됩니다.
이 메소드로 전달된 쿠키 값은
Split() 메소드에 의해
앞 부분의 접속 위치와
뒷 부분의 버전 정보로 나뉘게 됩니다.
따라서, '~' 뒷 부분의 값을
E15MinVersion 보다 크게 설정하여
공격에 필요한 정보를
내부의 서버에서 가져오도록
조작할 수 있게 됩니다.
취약점 Point.
취약점이 발생하는 코드에서
버전에 따른 서버 포트 차이를
설명했으나,
실제로 인증 우회가 가능한 이유는
Back-End site에서
별도로 사용자 검증을 하지 않기 때문입니다.
수동 테스트
Proxy Tool인 Burp Suite를 통해
직접 취약점을 테스트한 결과입니다.
인증 우회를 위해
여러 단계로 이루어져 있습니다.
Step 1) Server의 Hostname을 획득하기 위해,
아무 HTTP Request를 보냅니다.
HTTP Response의 Header에서
서버의 Hostname을 획득할 수 있습니다.
Step 2) User 계정의 LegacyDN 정보를 얻기 위해
autodiscover.xml 로 접속을 시도합니다.
접속할 때는 위에 분석한 내용대로
버전을 E15MinVersion 보다 크게 설정했으며,
사용자 데이터를 검색하기 위한 정보를 포함하여
접속을 시도합니다.
참고로, autodiscover.xml 파일은 사용자 계정 정보를
내부 서버에서 찾아주는 파일입니다.
Step 3) User 계정의 SID를 얻기 위해
mapi의 emsmdb 모듈로 데이터를 전송합니다.
불완전한 데이터를 전송하여
Back-End site에서 강제로 에러를 발생시키며,
HTTP Response에 포함된
서버의 에러 메시지에서
User SID를 획득할 수 있습니다.
Step 4) 이제 ProxyLogon.ecp로 접속하여
System 계정으로 인증된 세션을 획득할 수 있습니다.
획득한 세션을 사용하여
다음 취약점을 활용해 추가 공격이 가능합니다.
# CVE-2021-26855
- Arbitrary File Write(임의의 파일 작성)
공격자가 원하는 위치에
서버에서 실행이 가능한 .aspx 파일을
생성할 수 있는 취약점 입니다.
생성된 파일에 웹쉘이 포함된 것은 아니며,
생성된 파일에 접근할 때, 웹쉘 코드를 포함시켜
RCE를 발생시킬 수 있는 취약점 입니다.
OAB 가상 디렉토리 재설정
이 취약점은 Exchange 관리 센터에서
OAB 가상 디렉토리의 위치를
재설정할 때 발생합니다.
ControlPanel.dll - DDIService.WriteFileActivity.Run
OAB 가상 디렉터리 재설정 시
현재 설정을 저장하게 되며,
WriteFileActivity Class의 Run 메소드에서
그 코드를 확인할 수 있습니다.
파일 생성 위치는
OutputFileNameVariable 값에서 가져오지만,
모든 과정에서
경로 및 확장자를 검토하는 코드가 없어서
공격자가 원하는 위치에
원하는 확장자로 파일을 생성할 수 있습니다.
취약점 Point.
파일이 생성되는 위치를
사용자로 부터 받아서
파일을 생성하는 과정에서
그 위치나 확장자 등을
검토하는 코드가 없어서
공격자가 원하는 위치에
원하는 확장자로 생성이 가능합니다.
수동 테스트
Proxy Tool인 Burp Suite를 통해
직접 취약점을 테스트한 결과입니다.
Step 1) 우선, 생성된 파일로 접근할 때
웹쉘 코드를 실행시킬 수 있도록
외부 URL의 값을
공격 코드로 설정합니다.
Step 2) OAB 가상 디렉터리의 위치를 재설정합니다.
재설정 시 파일이 생성되는 위치를
웹 브라우저로 접속 가능한
Front-End site의 웹루트 폴더로 지정합니다.
Step 3) Proxy Tool을 사용하여 패킷을 가로챈 후,
FilePathName 파라미터의 내용을
Front-End site의 웹루트 경로와
실행 가능한 확장자(.aspx)로
수정하고 재전송합니다.
Step 4) 성공적으로 RCE 취약점이 가능한
파일이 생성됩니다.
웹쉘을 생성한 것이 아니기 때문에
파일의 내용은 평범합니다.
Step 5) 생성된 파일로 접근 시
웹쉘 코드를 포함하여 전송합니다.
서버에서는 해당 코드를 실행한 후,
그 결과를 HTTP Response에 포함하여
공격자에게 반환합니다.
# 보안 패치는 다음 글에서...
보안 패치에 관한 내용은
다음 포스팅에서 이어서 하겠습니다.
관련 글
- 참고 사이트
http://cn-sec.com/archives/531520.html
https://devco.re/blog/2021/08/06/a-new-attack-surface-on-MS-exchange-part-1-ProxyLogon/
https://githubmemory.com/repo/alt3kx/CVE-2021-26855_PoC