소학회/기술스터디

[Theori]CoSoSys Endpoint Protector에서 발견한 0-day 원격 코드 실행 취약점

haerim9.9 2025. 5. 12. 16:44

APT* 모의침투 중, CoSoSys의 EPP 분석으로 4건의 RCE* 취약점 발견. 해당 취약점으로 EPP 서버와 클라이언트 장악으로 민감 정보 탈취 가능.

 

CoSoSys EPP(Endpoint Protector): 조직에서 다루는 매체가 외부로 유출되는 것 방지 및 유출 경로 추적 위한 DLP* 솔루션의 일종.

EPP 사용 시, 서버와 연결된 클라이언트 검열 가능. 정책 솔루션 관리자가 직접 생성해 클라이언트에 배포 가능. 관리자는 민갑 정보 중 어떤 자산의 이동을 추적할 건지 정의 가능.

 

 

발견한 취약점 CVE* 번호

  • CVE-2024-36072(CVSSv4 10) : 서버 애플리케이션 로그 관련 구성 요소에 존재하는 원격 코드 실행 취약점
  • CVE-2024-36073(CVSSv4 8.5) : 에이전트의 Shadow 기능 관련 구성 요소에 존재하는 원격 코드 실행 취약점
  • CVE-2024-36074(CVSSv4 7.3) : 에이전트의 third-party 리소스(EasyLock) 업데이트 방식에 존재하는 원격 코드 실행 취약점
  • CVE-2024-36075(CVSSv4 7.2) : 에이전트가 서버 설정을 적용하는 과정에 존재하는 원격 코드 실행 취약점

 

분석 배경 : 고객사가 CoSoSys의 EPP 솔루션 사용. 회사 전체에 적용되는 솔루션은 중앙 소프트웨어를 이용하여 임직원의 기기를 제어할 수 있기 때문에 측면 이동 공격*에 유용. -> 취약점 분석 수행. 정적/동적 분석을 활용한 리버싱.

 


1. CVE-2024-36072

인증되지 않은 이용자가 임의 경로에 파일을 업로드해 서버의 루트 쉘(Shell)을 획득하는 RCE 취약점.

*루트=최고 권한 관리자 계정 / 쉘=명령어 입력해서 시스템 조작하는 터미널 환경

 

1) 솔루션 구조 분석 진행. -> EPP 솔루션 주요 로직 : 인증된 이용자 대상으로 제공.

 

2) 이용자 인증 우회 혹은 인증 없이 공격 가능한 취약점을 찾기 위해, 인증 여부 확인 로직 분석. -> isSoapAuth() 함수.

public function isSoapAuth( )
{
    $this->machine_id = $_SERVER['SSL_CLIENT_S_DN_CN'];
    $machine_in_db = $this->machineInDB( );
    $machine_valid_license = $this->machineHasValidLicense( );
    if ( $machine_in_db && $machine_valid_license )
    {
        return TRUE;
    }
    ...
}

설명 : SSL 통신을 시도하는 클라이언트 서버의 인증서 정보인 서버 변수의 SSL_CLIENT_S_DN_CN 필드 가져옴. 인증서의 서버 DB 등록 여부 및 라이센스 유무 판단으로 인증된 이용자 식별.

 

3) 인증 우회 목적으로 인증 없이 진행되는 메시지 처리 과정 확인. -> SOAP 프로토콜 기반의 여러 핸들러* 발견. 그중, 이용자가 전달한 파라미터 기반으로 인증서를 발급하는 Reigster 핸들러 존재.

 

설명 : PC 정보(IP, MAC 주소 등) 및 기본적인 가입을 위한 정보(부서 코드 등)를 파라미터로 전달. -> 서버는 인증서와 패스워드 반환.

이 인증서 정보를 이용하면 인증 상태를 검증하는 모든 로직 이용 가능. -> 인증 문제 해결.

 

5) 서버 장악을 위한 취약점 탐색. -> 인증서를 이용해 접근 가능한 기능 중, 파일 업로드 기능 존재. -> UploadLogPutPacket.

public function UploadLogPutPacket( $uploadId, $packet = "", $packetCRC = "" )
{
    if ( $this->isSoapAuth( ) )
    {
        $this->logThis( "FileUpload Error: not authenticated" );
        return array( "resultCode" => ALLOK );
    }
    $shadowDir = "/var/eppfiles/shadows";
    $shadowTempDir = $shadowDir."/temp";
    $uploadFile = $shadowTempDir."/".$uploadId;
    if ( isset( $_FILES['upshadow'] ) )
    {
        $fuploaded = fopen( $_FILES['upshadow']['tmp_name'], "rb" );
        $fhandle = fopen( $uploadFile, "a" ); // [1]
        $size = 0;
        while ( !feof( $fuploaded ) )
        {
            $data = fread( $fuploaded, 81920 );
            $writeOk = TRUE;
            $i = 1;
            do
            {
                for ( ; $i <= 3; ++$i )
                {
                    $writed = fwrite( $fhandle, $data );

설명 : 업로드할 파일(uploadId)을 /var/eppfiles/shadows/temp 디렉터리에 저장. 파일 저장 과정에서 uploadId 검사 X. -> Path Traversal*을 통한 임의 경로에 원하는 파일 쓰기 가능. 그러나 권한이 낮아 명령어 실행에 제약 존재.

 

6) 권한 상승 목적으로 루트 데몬 및 서비스를 확인. -> 로그 파일을 관리하기 위한 Worker가 동작하는 것을 발견. 루트 권한으로 동작하는 run_workers.php 코드는 ventilator.php 실행.

$path = $config['logs_path'];  // [1]
$tmpFile = uniqid( "list-" );
$cmd = "find ".$path." -type f | grep -v '\\.env'> /tmp/".$tmpFile.".pre";
system( $cmd );
$cmd = "sort /tmp/".$tmpFile.".pre > /tmp/".$tmpFile; // [2]
system( $cmd );
shell_exec( "rm -rf /tmp/".$tmpFile.".pre" );
...
$fr = fopen( "/tmp/".$tmpFile, "r" ); //[3]
while ( $file = fgets( $fr ) )
{
    if ( trim( $file ) == "" )
    {
        if ( 1000000 <= $count )
        {
            break;
        }
        else
        {
            $file = trim( $file );
            $dstPath = $workersPath."/".$pos."/".$hourFolder;
            $ret = preg_match( "/[0-9a-f]+-(\\d+)/", pathinfo( $file, PATHINFO_BASENAME ), $matches );
        }
        if ( $ret )
        {
        }
        else
        {
            $timePath = ceil( time( ) / 30 ) * 30;
            $dstPath = $dstPath."/".$timePath;
            logthis( "Destination path" );
            logthis( $dstPath );
            if ( is_dir( $dstPath ) )
            {
                mkdir( $dstPath );
            }
            $dstFile = $dstPath."/".pathinfo( $file, PATHINFO_BASENAME );
            logthis( "Destination File" );
            logthis( $dstFile );
            shell_exec( "mv -f ".$file." {$dstFile}" ); // [4]
            shell_exec( "mv -f ".$file.".env ".$dstFile.".env" ); // [4]

 설명 : 파일 시스템에 존재하는 로그 파일을 관리하기 위한 로직 포함.

동작 : logs_path /var/eppfiles/quicklogs/logs 경로 초기화. -> 해당 경로에 존재하는 모든 로그 파일에 대한 정보를 담은 임시 파일 생성. -> 로그 파일에 대한 이동 작업을 위해 임시 파일을 엶. -> 로그 파일을 순차적으로 가져와 shell_exec 함수를 통해 파일 이동 작업 수행.

 

이때 로그 경로 위치에 존재하는 파일이, 이름에 쉘 메타 문자를 포함하는 경우(../ 와 같은) 이동 과정에서 Command Injection 발생. -> 루트 권한으로 동작하는 로직 활용해 권한 획득, 명령어 실행 시 EPP 서버 장악 가능.

 

 

 

2. CVE-2024-36073

macOS 클라이언트의 File shadowing 기능에서 발생하는 Command Injection 취약점. 관리자 권한을 획득한 공격자가 macOS용 EPP 클라이언트가 설치된 모든 기기 장악 가능.

*File shadowing : 탐지된 파일을 저장소에 업로드 하는 행위.

 

1) File shadowing 시 EPP 서버는 다양한 위치에 파일 저장하는 옵션 제공. -> shadows.server.protocol에 명시된 값에 따라 업로드 방식 결정, 방식에 따른 설정 값 활용. 예시로 Azure 방식이라면 그에 따른 FTP URL, 포트, 계정 정보 가져옴.

 

2) Azure은 SMB*와 NFS*를 통해 파일 업로드 지원, EPP에서는 SMB 이용. -> mount_smbfs 명령어로 파일 업로드.

아래는 명령어 생성 코드.

std::operator+(&command, "mount_smbfs //", p_ftp_cred);
v25 = std::string::append(&command, "@");
cmd = *v25;
std::string::append(&cmd, ftp_url, ftp_url_len);
std::string::append(&cmd, " ");
strcpy(command.__r_.__value_.__l.__data_, "/private/var/tmp/epp/mount");
epp::file_util::createDirectory(&command, 3LL, 493LL);
cmd_str = cmd.__r_.__value_.__s.__data_;
if ( (cmd.__r_.__value_.__s.__size_ & 1) != 0 )
  cmd_str = cmd.__r_.__value_.__l.__data_;
if ( epp::runSynchronousShellCommand(cmd_str) )

생성되는 명령어 : mount_smbfs //{username} : {password}@{IP} {PATH} /private/var/tmp/epp/mount

 

epp::runSynchrocnousShellCommand 함수는 command injection을 방지할 수 있는 execve 시스템 콜이 아닌 popen을 사용하고 있기 때문에 command injection이 가능. -> 해당 취약점으로 임의의 명령어 실행할 수 있는 권한 획득 가능.

 

 

3. CVE-2024-36074

Windows 클라이언트의 third-party 리소스인 EasyLock 업데이트 과정에서 발생하는 Zip Slip 취약점. -> 서버의 관리자 권한 획등한 공격자가 Windows용 EPP 클라이언트가 설치된 모든 기기 장악 가능.

*Zip Slip : Path Traversal 구문이 포함된 파일이 담긴 압축 파일을 해제할 때, 공격자가 의도한 경로에 임의 파일을 생성하거나 덮어쓰는 공격 기법.

 

1) EPP의 EasyLock 기능 : 정보 유출 방지를 목적으로 지정한 매체를 암호화해 보호. -> EasyLock.exe 실행 파일 이용, EPP 에이전트 업데이트와 별도로 수행.

EasyLock 업데이트는 서버에서 /var/www/EPPServer/sieratool/web/easylock/ 경로에 있는 압축 파일 업데이트. 클라이언트가 해당 압축 파일 해제하는 방식.

 

2) 업데이트 수행 코드 확인. -> 업데이트 파일을 다운할 URL 설정값인 el.url과 EPP 에이전트 설정 파일을 담고 있는 settings_dir 디렉터리가 파일 시스템에 존재하는지 검증, 설정 파일 접근 가능 여부 확인. -> 만족한 경우 process_update 함수 실행해 업데이트.

 

설명 : options.ini 설정 파일의 settings_dir 값 뒤에 EasyLocks.zip을 붙여 다운 경로 생성.

 

3) 다운 경로와 URL 기반으로 epp:OptionStore2::downloadFile 함수를 통해 압축 파일 다운. epp::extractZipFile 함수로 압축 파일 해제.

if ( epp::OptionsStore2::downloadFile(option, download_path, download_url) ) {
    ...
    epp::extractZipFile(path, sett_dir);
    ...
}

설명 : 이 과정에서 libzip 오픈소스 사용. -> libzip 소스 코드가 압축 해제 과정에서 파일 이름에 대한 검사를 수행X. -> Zip Slip에 취약.

 

 

4. CVE-2024-36075

macOS/Windows 클라이언트가 서버에서 내린 결정을 적용하는 과정에서 발생하는 Injection 취약점. -> 서버 관리자 권한 획득한 공격자는 EPP 클라이언트가 설치된 모든 기기 장악 가능.

 

1) EPP 서버는 클라이언트 관리하기 위한 설정 값을 내려주고, 클라이언트는 해당 값을 options.ini 설정 파일에 key=value 형태로 저장. 명시된 설정값 일부.

 

2) 클라이언트는 epp::OptionsStore::getOptionString과 epp::OpptionsStore::setOptionString 함수를 통해 설정 파일 값 불러오거나 저장. 아래는 epp::OptionsStore2::setOptionString 함수 코드, key=value\n 형태로 설정 파일에 값을 작성.

if ( (long_flag & 1) != 0 )
{
    key = obj->key;
    len = obj->len;
}
else
{
    len = long_flag >> 1;
    key = obj->gap21;
}
v12 = std::__put_character_sequence>(&file, key, len); 
LOBYTE(chr_eq) = '=';
v13 = std::__put_character_sequence>(v12, &chr_eq, 1LL);
long_flag2 = obj->long_flag2;
v15 = obj->short_value;
if ( (long_flag2 & 1) != 0 )
{
    value = obj->value;
    value_len = obj->value_len;
}
else
{
    value_len = long_flag2 >> 1;
    value = obj->short_value;
}
v18 = std::__put_character_sequence>(v13, value, value_len);
LOBYTE(chr_) = '\n';
std::__put_character_sequence>(v18, &chr_, 1LL);

설명 : 서버에서 내려받은 설정 값에 대한 검사가 존재하지X. -> 특수문자 입력 가능. -> 이를 이용하면 임의의 Key에 임의 Value를 추가하고 적용하는 것이 가능. (\n 같은 거 사용해서) 

 

Key보다 앞선 위치에 있는 Key에 Value를 Injection하면 조작된 Key를 참조. -> command injection과 같은 추가적인 공격으로 클라이언트 장악 가능.

 


모르는 용어 정리

더보기

APT(지능형 지속 공격, Advanced Persistent Threat) : 민감한 데이터를 훔치거나 사이버 스파이 활동 수행하거나 중요 시스템을 장기간에 걸쳐 방해하도록 설계된 탐지되지 않는 사이버 공격. 대상 네트워크에 침투해 존재를 확장하는 동안 눈에 띄지 않는 것이 목적.

 

CVE(일반적인 취약성 및 노출, Common Vulnerabilities and Exposures) : MITRE Corporation에서 설정하고 관리하는 공개 정보 보안 취약점 카탈로그인 CVE 목록. 취약성 또는 노출에 대해 하나의 이름과 하나의 설명 제공. (자세한 설명 확인)

 

RCE(원격 코드 실행, Remote Code Execution) 공격 : 공격자가 조직의 컴퓨터나 네트워크에서 악성코드를 실행할 수 있는 공격. 일반적으로 웹 애플리케이션과 네트워크 인프라의 취약점을 통해 발생.

 

측면 이동(Lateral Movement) : 위협 행위자가 손상된 네트워크나 시스템을 탐색하여 한 호스트에서 다른 호스트로 은밀하게 이동하는 데 사용하는 기술. APT 공격의 중요한 단계.

 

DLP(데이터 손실 방지, Data Loss Prevention) : 회사 네트워크 외부로 민감하거나 중요한 정보를 보내지 않도록 함.

 

핸들러(handler) : 어떤 요청이나 이벤트, 처리하는 자 또는 기능. -> 웹 애플리케이션이 클라이언트로부터 받은 HTTP 요청을 처리할 수 있는 모든 객체.

 

Path Traversal(경로 탐색 취약점) : 웹 루트 디렉터리 외부에 저장된 파일 및 디렉터리에 접근하는 기법. "../"을 사용해 접근.

 

NFS(네트워크 파일 시스템, Network File System) : 네트워크 상 효율적인 파일 공유를 위한 파일 액세스 스토리지 프로토콜.

클라이언트가 원격 프로시저 호출을 사용해 서버에 파일 또는 디렉터리 요청. -> 서버는 파일/디렉토리의 가용성, 액세스 권한 유무 확인. -> 파일 또는 디렉터리를 클라이언트에 원격 탑재. 가상 연결로 액세스 공유.

 

SMB(서버 메시지 블록, Server Message Block) : 네트워크 상 효율적인 파일 공유를 위한 파일 액세스 스토리지 프로토콜. NFS와 유사, 세부 사항과 운영 메커니즘의 차이 존재.


본 게시글은 아래의 링크 속 콘텐츠를 기반으로 작성.

https://theori.io/ko/blog/a-deep-dive-into-cososys-endpoint-protector-exploit-remote-code-execution-kor

 

CoSoSys Endpoint Protector에서 발견한 0-day 원격 코드 실행 취약점 - Theori 블로그

Theori Security Assessment 팀이 APT 모의침투 프로젝트에서 CoSoSys Endpoint Protector(EPP)의 4가지 RCE 취약점을 발견했습니다. 해당 취약점을 분석하고 활용하여 서버 및 클라이언트를 장악한 과정, CVE-2024–3

theori.io