
https://hackerone.com/reports/863553
/appsuite/api/oxodocumentfilter&action=addfile 처리 시 적용되는 URL 검증 로직에는 Time of Check Time of Use(ToCToU) SSRF 공격을 실행하는 데 사용될 수 있는 세 가지 결함이 있습니다. 이 문제는 악의적인 행위자가 내부 네트워크 서비스에 HTTP GET 요청을 실행할 수 있게 하며, 보호된 정보 유출로 이어질 수 있습니다.
URITools.getFinalURL 메서드 여기에 구현된 URL 검증 로직에는 ToCToU 취약점이 있습니다.
URL u = new URL(url);
if (validator.isPresent()) {
Optional<OXException> exception = validator.get().apply(u);
if (exception.isPresent()) {
throw exception.get();
}
}
URLConnection urlConnnection = u.openConnection();
위 코드에서는 입력 URL 호스트 이름의 DNS 확인이 검사 시점(validator.get().apply(u))과 사용 시점(u.openConnection()) 모두에서 InetAddress.getByName 메서드를 호출하여 수행됩니다. 따라서 공격자가 검사 시점 이후, 사용 시점 이전에 DNS 확인 결과를 변경할 수 있다면 검증기를 우회할 수 있습니다. 이 결함만으로는 다음 제한 사항 때문에 악용하기가 매우 어렵습니다.
InetAddress.getByName은 DNS 확인이 성공하면 30초, 실패하면 10초 동안 DNS 확인 결과를 캐시합니다.validator.get().apply(u)와 u.openConnection() 사이의 시간 간격은 매우 짧습니다(일반 URL의 경우 1밀리초 미만). 이 제한을 우회하는 데 결함 #2가 사용됩니다.AddFileAction.validator URL 검증기는 여기에 구현되어 있으며 입력 URL의 길이를 검증하지 않습니다. 공격자는 크기가 큰 URL을 제공하여 검사 시점과 사용 시점의 InetAddress.getByName 메서드 호출 사이의 시간 간격을 늘릴 수 있습니다. 시간 간격이 길어질수록 공격자는 결함 #1을 쉽게 악용할 수 있습니다.
HostList.contains 메서드는 여기에 구현되어 있으며, 입력 URL 호스트 이름의 DNS 확인이 실패하면 false를 반환합니다.
// Need to resolve as last resort
try {
return contains(InetAddress.getByName(toCheck), false);
} catch (UnknownHostException e) {
// Cannot be resolved
return false;
}
AddFileAction.validator는 blackImageUrlHostlist.contains 메서드를 호출하여 입력 URL 호스트 이름 또는 확인된 IP 주소가 블랙리스트에 있는지 확인합니다. 공격자가 호스트 이름의 DNS 확인을 실패하게 만들면 IP 주소 검사는 건너뛰고 URL이 허용됩니다.
공격자가 제어하는 권위 있는 네임서버를 가진 호스트 이름 randomid.dns.pointer.pw를 고려해 보십시오. 공격 전략은 검사 시점(validator.get().apply(u);)에는 randomid.dns.pointer.pw의 DNS 확인이 실패하고, 사용 시점(u.openConnection())에는 성공하도록 만드는 것입니다. 이를 통해 공격자는 IP 주소 블랙리스트 필터를 우회하고 블랙리스트에 등록된 모든 IP 주소에 HTTP GET 요청을 실행할 수 있습니다. 공격자는 이 공격을 실행하기 위해 다음 단계를 수행할 수 있습니다.
addfile 액션에 요청 데이터 {"add_imageurl": "http://randomid.dns.pointer.pw"}를 포함하여 실행함으로써 서버에서 InetAddress.getByName 캐시를 트리거합니다. 이때 권위 있는 DNS 서버는 SERVFAIL 상태를 반환하여 randomid.dns.pointer.pw의 DNS 확인을 실패하도록 설정됩니다. InetAddress.getByName은 이 실패 결과를 10초 동안 캐시합니다.pointer.pw의 권위 있는 네임서버에 호스트 이름 randomid.dns.pointer.pw에 대한 A 유형 DNS 요청에 대해 127.0.0.1을 반환하도록 지시합니다.InetAddress.getByName이 검사 시점에는 캐시된 응답(SERVFAIL)을 반환하고 사용 시점(url.openConnection())에는 127.0.0.1을 반환하도록 합니다.add_imageurl 값을 http://u{25MB}:[email protected]/path/to/internal/resource.png 형식으로 설정하여 SSRF 요청을 실행합니다. 여기서 u{25MB}는 사용자 이름 부분이 25 x 1024 x 1024 문자 길이의 문자열임을 의미합니다.https://golang.org/dl/ 에서 golang을 설치합니다.
가이드 https://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Installation_Guide_for_Debian_9.0 및 https://oxpedia.org/wiki/index.php?title=AppSuite:Documents_Installation_Guide#Debian_GNU.2FLinux_9.0_.28valid_from_v7.10.29 에 따라 최소 4GB RAM이 있는 가상 머신에 Open-Xchange와 Documents를 설치합니다.
poc.zip 파일을 다운로드하고 압축을 해제합니다.
터미널/명령줄을 열고 현재 디렉터리를 압축 해제된 poc.zip 폴더로 설정합니다.
다음 명령을 실행합니다.
go run . -serverRoot="http://172.16.66.130" -username="testuser" -password="secret" -targetPath="appsuite/v=7.10.3-9.20200409.083030/apps/themes/logo.png" -targetPort="80" -payloadSize=25 -startSleepDuration=6600
여기서 172.16.66.130은 VM의 IP 주소입니다.