01. TeamCity 인증 우회 취약점 개요
인증 우회 취약점은 악의적인 공격자가 보안 메커니즘을 우회하고 시스템의 인증 절차를 회피하여 권한을 획득할 수 있는 경로를 제공하는 취약점이다. 이러한 취약점은 API 오용, 인증 및 접근 제어 결함, 입력 값 검증 미흡, 보안 설정 미흡 등의 결함으로 인해 발생한다. 공격자는 이를 악용해 초기 액세스 권한을 얻거나 민감 정보 탈취 등의 공격을 수행할 수 있다.
API 오용은 애플리케이션에서 제공되는 기능이나 외부 인터페이스를 부적절하게 사용하는 경우 발생한다. 인증 및 접근 제어 메커니즘 결함은 권한이 부여되지 않은 사용자가 시스템에 접근할 수 있는 결함을 의미하며, 입력 값 검증 미흡은 외부 요청이나 입력 값에 대한 검증 절차 미흡으로 악의적인 데이터 삽입이 가능한 결함을 의미한다. 이러한 유형의 결함들이 취약점으로 이어지면 시스템에 심각한 위험이 야기되게 된다.
JetBrains가 개발한 CI/CD(지속적 통합 및 지속적 배포) 서버 솔루션인 TeamCity는 2023년에 이어 최근까지도 인증 우회 취약점이 발견되어 다수의 공격 그룹에서 공격벡터로 악용하였다. 공격자들은 취약점을 악용해 관리자 권한 획득이나 원격 코드 실행(RCE)을 통해 시스템을 제어하는 공격을 수행했다. 이에 따라 이번 호에서는 2023년에 발견된 CVE-2023-42793 공격 사례와 2024년 발견된 TeamCity 취약점(CVE-2024-27198)을 비교 분석하여 인증 우회 취약점의 발생 원리 및 대응방안에 대해서 살펴보고자 한다.
02. CVE-2023-42793 공격 사례
CVE-2024-27198을 살펴보기에 앞서 2023년 TeamCity 취약점으로 발견된 CVE-2023-42793의 공격사례에 대해서 살펴보고자 한다. 2023년에 발견된 CVE-2023-42793은 HTTP POST 요청을 통해 유효한 액세스 토큰을 획득할 수 있는 취약점으로 이를 악용해 초기 액세스 권한을 얻고 백도어를 설치하거나 민감 정보를 탈취할 수 있다. 해당 취약점은 북한발 공격그룹인 히든 코브라(Hidden Cobra)와 안다리엘(Andariel)에서 악용한 것을 확인할 수 있다.
1) Lazarus 계열 APT 그룹 : Hidden Cobra
북한발 APT 공격그룹 Hidden Cobra(aka. Diamond Sleet)는 2023년 10월 초 CVE-2023-42793 취약점으로 TeamCity 서버에 접근한 후 Powershell을 통해 Forest64.exe와 4800-84DC-063A6A41C5C 페이로드를 다운로드 하고 라자루스(Lazarus)에서 수행하는 DeathNote campaign의 ForestTiger 백도어를 사용해 LSASS 프로세스에 대한 메모리 자격 증명을 덤프하여 데이터를 유출하였다. 이외에도 동일 취약점을 이용해 DLL Hijacking 공격을 통해서 RAT 악성코드를 통해 메모리 덤프를 수행하기도 하였다.
2) Lazarus 계열 APT 그룹 : Andariel
북한발 APT 공격그룹 Andariel(aka. Onyx Sleet)은 일반 기업 및 금융기관이나 국내 정부기관을 표적으로 공격한다. 이들은 CVE-2023-42793을 이용해 서버에 접근한 후 Kerberos 티켓을 부여하는 Windows 계정 이름인 KRBTGT을 이용하여 krtbgt라는 새로운 사용자 계정을 생성하고 tasklist, whoami, netstat, systeminfo 등 다수의 시스템 검색 명령어를 입력하여 시스템 정보를 확인했다. 이후 Powershell을 통해 temp.exe와 inetmgr.exe를 다운로드 하였으며, 다운로드 된 악성코드는 메모리에 로드되는 형태로 HazyLoad 악성코드라 명명된다.
이후 공격자는 원격 데스크톱 프로토콜(RDP)을 통해 타 시스템에 접근하거나 다른 공격 그룹의 추가 공격을 저지하기 위해서 TeamCity 서비스 중단, LSASS 메모리를 통한 자격 증명 덤프, 브라우저 내 저장된 자격증명 정보 및 데이터 검색 도구 배포 등을 통해 추가 정보를 확보하기 위한 공격을 수행하였다.
03. CVE-2024-27198 취약점 분석
’02. CVE-2023-42793 공격 사례’에서 확인한 바와 같이 TeamCity 환경에서 발생하는 인증 우회 취약점은 TeamCity의 시스템 인증을 우회하고 계정 생성 및 TeamCity의 프로젝트, 빌드, 에이전트, 아티팩트 등의 구성요소를 제어할 수 있어서 공급망 공격으로 이어질 수 있다. TeamCity 온프레미스 환경의 모든 버전에서 발현되고 시스템 리소스에 제한없이 접근이 가능한 RCE 공격이 가능하기 때문에 고위험 취약점에 속한다.
2023년에 발견된 CVE-2023-42793 이후 2024년에 발견된 CVE-2024-27198은 해킹그룹에서 악용된 사례는 확인되지 않았으나, 자스민(Jasmin) 랜섬웨어 변종, XMRig, SparkRAT 백도어 등에서 악용한 사례가 확인됨에 따라 향후 다수의 해킹그룹에서 악용할 가능성이 존재한다.
CVE-2024-27198은 CVSS 3.0 기준 9.8에 해당하며, CVE와 다르게 취약점 악용 가능성을 예측하는 지표인 EPSS 수치를 확인해 보면 [표 1]과 같이 취약점의 악용 가능성이 상당히 높다는 것을 알 수 있다.
CVE-2024-27198은 대체 경로 문제로 인해 발생하는 웹 구성요소 인증을 우회하는 취약점이다. 코드 결함으로 인해 발생하며 특정 요청 전송 후 updateViewIfRequestHasJspParameter 메서드를 호출하여 모든 인증에 대한 우회가 가능하게 된다. CVE-2024-27198 취약점 원인에 대해서는 PoC와 코드 디버깅을 통해 인증 우회 과정에 대해서 설명하고자 한다.
1) CVE-2024-27198 : PoC 시연
PoC 시연은 다음과 같은 환경에서 진행되며 구성도는 [그림 4]과 같다.
아래 [그림 5]의 PoC 실행 결과 화면에서 임의의 값으로 지정한 관리자 계정이 생성되고 웹 쉘이 포함된 악성 플러그인이 업로드 되어 RCE 공격까지 가능한 것을 확인할 수 있다. 이와 같은 결과가 어떻게 발생할 수 있는지 PoC 코드 분석과 디버깅을 통해 더 자세히 알아보도록 하자.
2) CVE-2024-27198 : PoC 분석
[그림 4]의 PoC 구성도에 따라 관리자 계정 생성, 악성 플러그인 제작, 악성 플러그인 업로드, RCE 발현 과정에 대해 나누어 살펴보고자 한다.
먼저, 인증 우회 취약점 발현 여부를 확인하기 위해 인증을 필요로 하는 임의의 경로에 접근을 시도한다.
로그인하지 않은 상태에서 서버 버전 정보가 노출되는 페이지인 /app/rest/server 로 접근 요청을 했을 때를 웹 프록시 도구(Burp Suite)로 확인해보면 응답 코드 302(Found)를 반환하고 로그인 페이지로 리다이렉트 되는 것을 알 수 있다.
PoC 코드를 확인해보면 GetTeamCityVersion() 함수에서 요청 url에 존재하지 않는 임의의 경로(/hax)를 사용하여 /app/rest/server 로 접근을 시도하고 있다.
요청 라인의 URL을 PoC 코드와 같이 존재하지 않는 경로(/hax)로 변경하게 되면 [그림 8]의 Response 결과에서 확인할 수 있듯이 /app/rest/server 페이지에 접근이 가능하여 서버의 버전 정보가 노출된다.
앞의 과정을 흐름도로 표현하면 [그림 9]과 같으며, 인증이 필요한 페이지에 존재하지 않는 임의의 경로를 요청할 경우 인증 우회가 가능하게 된다.
PoC 코드에서 /hax?jsp=/app/rest/server;.jsp 로 요청 URL을 변경하여 서버 버전 정보가 노출되는 페이지의 접근 유무로 인증 우회 취약점의 발현 여부를 파악하고 있다. 이와 같은 구문이 어떤 방식으로 인증을 우회할 수 있는지 디버깅을 통해 알아보고자 한다.
[그림 10]은 디버깅 시 호출되는 클래스와 메서드를 간단히 요약한 흐름도이다. 정상적인 요청일 경우는 생략하며 단계별 디버깅 과정에 따라 자세하게 알아보도록 하자.
먼저, 살펴볼 PageNotFoundController 클래스는 존재하지 않는 경로를 요청하는 경우 활용되며 doHandle 메서드에서 ModelAndView에 “404.jsp” 가 반환되고 servletPath(서블릿 경로)는 “/404.html”이 되는 것을 확인할 수 있다.
[그림 11]에서 PageNotFoundController는 BaseController를 상속하고 있으며, BaseController 클래스가 요청을 처리하는 방식에서 해당 취약점이 발생된다.
아래의 [그림 12]에서 BaseController 클래스의 handleRequestInternal 메서드가 요청을 처리할 때, 요청이 리다이렉션되지 않는다면 (즉, 핸들러가 응답 코드 302(Found) 리다이렉션을 실행하지 않는 경우) updateViewIfRequestHasJspParameter 메서드를 호출하게 된다.
[그림 13]에서 updateViewIfRequestHasJspParameter 메서드를 확인하면 modelAndView가 null이 아니고 현재 요청의 servletPath(서블릿 경로)가 .jsp로 끝나지 않을 경우 isControllerRequestWithViewName 변수가 true로 설정된다.
이를 만족하기 위해서 HTTP 404(not found) 응답을 생성하도록 요청하면 [그림 11]에서 확인하듯이 modelAndView가 404.jsp가 되고 서블릿 경로는 /404.html이 생성된다. 이 때 경로가 .html로 끝나기 때문에 True로 설정된다.
그 다음 getJspFromRequest 메서드가 호출되고, 그 결과가 Spring Framework의 modelAndView.setViewName 메서드로 전달된다. 이 때 jspFromRequest 변수의 내용을 공격자가 변조하여 제어할 수 있게 된다면 DispatcherServlet에서 처리되는 URL을 변경하여 임의의 엔드 포인트를 호출할 수 있게 된다.
[그림 14]에서 getJspFromRequest 메서드를 살펴보면 밑줄 친 부분에서 여러 판단을 진행하며 그 내용은 다음과 같다.
1) jsp 파라미터가 존재하는 경우
2) jsp 파라미터 값이 null 또는 .jsp 종단 문자일 경우
3) jsp 파라미터 값에 “admin/” 문자열이 포함되지 않을 경우
[그림 15] PoC 코드에서 확인한 요청 URL을 보면 위의 조건들을 만족하고 있다. 그 결과 jspFromRequest가 True를 반환하므로 updateViewIfRequestHasJspParameter 메서드에 jspFromRequest 값이 전달된다.
다시, updateViewIfRequestHasJspParameter 메서드에 돌아와서 확인해보면 jspFromRequest 변수에 인증된 엔드 포인트인 “/app/rest/server;.jsp”값이 전달되고 modelAndView.setViewName 호출이 발생하는 것을 볼 수 있다.
이후 Java Servlet API에서 사용되는 getRequestDispatcher 메서드를 이용해 서블릿 컨테이너의 내부 동작을 추상화하고 클라이언트의 요청을 처리하게 된다.
[그림 17]에서는 URI를 매핑하고 서블릿 컨테이너가 서블릿 요청을 적절하게 처리할 수 있도록 도와주는 메서드인 getRequestDispatcher는 뷰 경로를 얻는 과정을 나타내고, 파라미터 경로를 파싱하는 stripPathParams 메서드를 호출한다.
stripPathParams 메서드를 확인해보면 세미콜론(;) 문자 전까지의 내용만 추가하고 나머지 부분을 제거하고 있다. “/app/rest/server;.jsp” 값이 전달될 때 세미콜론(;) 전까지의 문자열을 추가하므로 [그림 18]에서 uriNoParams 값이 /app/rest/server만 남게 된다.
org.apache.Catalina.core.ApplicationContext#getRequestDispatcher에서 접근이 불가능한 경로였던 /app/rest/server;.jsp가 /app/rest/server로 변경되어 값을 전달하여 해당 경로에 접근이 가능하게 되면서 서버의 버전 정보를 확인할 수 있게 된다.
공격자는 해당 과정으로 발현되는 인증 우회 취약점을 악용하여 시스템에 접근하기 위한 2차 공격을 수행할 위험성이 존재한다. PoC 코드에서는 2차 공격으로 관리자 계정 생성, 토큰 생성 및 부여, 악성 플러그인 업로드 및 RCE 공격을 수행하고 있어 아래의 STEP1,2,3,4 과정을 통해 살펴보고자 한다.
STEP1 : 관리자 계정 생성
인증 우회 취약점 발현 시 디버깅 과정에서 확인한 바와 같이 존재하지 않는 경로로 접근했을 때 인증이 우회된다는 점을 악용하여 REST API 엔드 포인트를 대상으로 공격자의 제어가 가능한 관리자 계정을 생성할 수 있게 된다.
TeamCity에서 사용자를 생성할 때 /app/rest/users 경로를 통해 접근하는 것을 확인한다.
PoC 코드에서 AddUser() 함수의 add_user_url 변수에 존재하지 않는 경로(/hax)를 통한 요청 URL을 사용하였으며 add_user_data 변수에 계정명, 패스워드, 계정의 권한을 나타내는 파라미터 roleId를 “SYSTEM_ADMIN”값으로 지정하여 관리자 계정 생성을 요청한다.
/app/rest/users 경로에 접근이 가능하게 되며 관리자 권한을 가진 계정 생성이 이루어진다. 다음은, 웹 프록시 도구를 활용해 실제로 계정이 생성되는 과정을 확인해보자.
해당 요청으로 관리자 권한을 가진 계정이 생성되고, 로그인까지 가능한 것을 확인할 수 있다.
계정 생성 후 GetToken() 함수를 이용해 사용자의 토큰을 생성하고 권한이 존재하는 페이지에 요청 시 사용된다. [그림 21]에서 관리자 계정 생성 함수인 AddUser()로 생성된 관리자 계정에 대한 토큰 값을 생성하여 관리자 권한을 필요로 하는 페이지에 접근 시 해당 토큰을 사용하여 2차 공격을 하기 위함이다.
PoC 코드를 확인해보면 /app/rest/users/id:{user_id}/tokens/{token_name} 경로로 접근하여 응답값의 name, value, creationTime을 포함하는 토큰(value)값을 생성하게 되는 것을 알 수 있다.
토큰을 생성하는 흐름을 간단히 표현하면 [그림 25]와 같이 나타낼 수 있다. 토큰을 생성하는 페이지에 접근하게 되면 임의의 토큰 값을 반환 받게 되며 관리자 권한을 요구하는 페이지에 접근할 때 해당 토큰을 사용하게 된다.
STEP2 : 악성 플러그인 제작
공격자는 관리자 계정 생성, 토큰 생성으로 관리자 권한을 필요로 하는 페이지에 접근이 가능한 점을 악용하여 서버를 제어하기 위해 파일이나 플러그인을 업로드하는 시도를 진행할 수 있으며, PoC 코드에서는 웹 쉘을 포함한 .jar 파일을 생성하고 있다. GetEvilPluginZipFile() 함수는 Faker 라이브러리를 사용하여 가짜 정보를 생성하기 위한 객체를 초기화하고 cmd 파라미터를 갖는 jsp 웹 쉘 코드를 생성하여 임의의 .jar 파일을 제작한다.
[그림 27]에서는 플러그인의 메타 정보가 담긴 XML 파일을 생성한다. 이후 [그림 26]에서 생성한 jsp를 buildServerResources/ 디렉터리로 지정 후 jar 파일과 xml 파일을 zip 파일에 추가한다.
STEP3 : 악성 플러그인 업로드
관리자만 접근 가능한 플러그인 업로드 페이지에 관리자 계정의 토큰 값을 이용하여 접근함으로써 웹 쉘이 포함된 악성 플러그인 업로드가 가능해진다.
LoadEvilPlugin()에서 플러그인 존재 시 활성화를 위해 /admin/plugins.html로 POST 요청을 전송 한다.
STEP4 : 원격 코드 실행(RCE) 발현
[그림 28] 악성 플러그인 업로드 흐름도에서 관리자 계정의 토큰을 이용해 플러그인 업로드 후 플러그인의 경로에 접근함으로써 원격 코드 실행(RCE)이 가능하게 된다.
PoC 코드에서 확인해보면 ExecuteCommandByEvilPlugin()에서는 메인 함수에 있는 shell_url과 command를 이용해 POST 요청을 생성하고 cmd 파라미터에 명령어를 인코딩하여 전달함으로써 원격 명령어 실행이 가능하게 된다.
04. 대응 방안
TeamCity에서 발생한 인증 우회 취약점은 온프레미스(On-Premises) 설치 모든 버전에서 발현되므로 이를 해결하기 위해서는 다음과 같은 조치가 필요하다.
1) 최신 버전 업데이트(23.11.4)
TeamCity 서버, 에이전트 및 클라이언트를 포함한 모든 소프트웨어에 대한 최신 버전 및 보안 업데이트를 적용하는 것이 가장 중요하며 업데이트를 통해 취약점 자체에 대한 해결이 가능하므로 최신 버전인 2023.11.4 로 업데이트 진행을 권고한다.
2) 보안 패치 플러그인 적용
JetBrains에서는 취약점만 패치가 가능한 보안 패치 플러그인을 지원하고 있어 업데이트가 어려운 환경일 경우 플러그인을 통해 패치가 적용될 수 있도록 설치를 권고한다. 플러그인 수동 설치 방법은 ZIP 플러그인 패키지를 [TeamCity 설치 디렉터리]/plugins 경로에 복사하여 설치 후 로드를 선택하여 활성화될 수 있도록 한다.
3) 악성 플러그인 업로드 시 비활성화
PoC 분석에서 확인한 악성 플러그인이 업로드 될 경우 disabled-plugins.xml 파일에서 태그를 이용해 비활성화가 가능하므로 [표 3]의 OS 별로 위치한 경로에서 적용이 가능하다.
4) 로그 파일 정규 표현식 적용
로그 파일에서 정규표현식을 통해 특정 문자열 패턴을 매칭하여 해당 패턴이 포함된 로그 메시지를 식별함으로써 특정 유형의 활동 탐지가 가능하다. OS 별 로그 파일이 존재하는 경로는 [표 4]와 같으며 logs 디렉터리 내에 존재하는 teamcity-*.log 파일에서 인증 우회를 위한 경로 접근 및 업로드 된 플러그인 정보 등의 확인이 가능하다.
teamcity-javaLogging.log 파일에는 ModelAndView.setViewName 호출 후에 처리된 URL을 설명하는 내용이 포함되어 새로운 액세스 토큰을 생성했을 때를 예시로 하여 [그림 34]과 같은 로그를 확인할 수 있다.
인증 우회 취약점이 발생될 때 로그 내용을 확인해보면 jsp= 파라미터와 ;.jsp 문자열이 포함되어 있다. 이를 탐지하기 위해 아래의 [그림 35]에 있는 정규표현식을 적용하여 악용 시도에 대한 탐지가 가능하다.
05.마무리
JetBrains의 TeamCity에서 발생한 CVE 취약점을 예시로 인증 우회 취약점에 대한 분석을 통해 취약점의 위험성과 발생 원인에 대해 확인할 수 있었다. 인증 우회 취약점은 공격자가 초기 액세스를 위해 보안 메커니즘을 우회하여 시스템에 접근할 수 있는 경로를 제공한다. 접근 권한을 획득하게 되면 토큰을 생성하여 웹 쉘을 업로드하거나 원격 코드 실행과 같은 2차 공격으로 이어질 수 있는 가능성이 발생하게 된다. 이를 대응하기 위해 보다 강력한 인증 및 접근 제어 메커니즘을 도입하고 입력 값 검증과 인증 프로세스 강화, 패치 업데이트 등을 통해 시스템의 보안성을 강화하는 것이 필요하다.
06.참고 자료
1) Vulnerability Details : CVE-2024-27198 :
https://www.cvedetails.com/cve/CVE-2024-27198/?q=cve-2024-27198
2) Additional Critical Security Issues Affecting TeamCity On-Premises (CVE-2024-27198 and CVE-2024-27199) – Update to 2023.11.4 Now : https://blog.jetbrains.com/teamcity/2024/03/additional-critical-security-issues-affecting-teamcity-on-premises-cve-2024-27198-and-cve-2024-27199-update-to-2023-11-4-now/
3) Proof of Concept for Authentication Bypass in JetBrains TeamCity Pre-2023.11.4 : https://github.com/Chocapikk/CVE-2024-27198
4) CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities :
https://www.rapid7.com/blog/post/2024/03/04/etr-cve-2024-27198-and-cve-2024-27199-jetbrains-teamcity-multiple-authentication-bypass-vulnerabilities-fixed/