보안정보

전문화된 보안 관련 자료, 보안 트렌드를 엿볼 수 있는
차세대 통합보안관리 기업 이글루코퍼레이션 보안정보입니다.

Tomcat-AJP 취약점 분석 및 대응방안 : Ghostcat(CVE-2020-1938)

2020.03.31

43,841


 

 

 

 

01. 개요

 

Apache Software Foundation의 서블릿 컨테이너인 Apache Tomcat 모든 버전 (9.x/8.x/7.x/6.x)

에서 AJP 프로토콜 (PORT : 8009)을 이용해 /webapps/ROOT 디렉터리 하위 파일을 읽을 수 있는 ‘잠재적인(potential)’ 원격코드실행 취약점(Ghostcat, CVE-2020-1938)이 발견되었다. ‘잠재적’이라고 표현하는 이유는 웹 어플리케이션 프로그램 내에서 파일 업로드 및 저장이 허용되어야 하는 전제조건이 필요하기 때문이다.

 

2020년 1월 3일 최초 중국 보안 업체 차이틴 테크(Chaitin Tech)에서 발견하여 Ghostcat이라고 명명되었다. 취약점이 공개된 후 2020년 2월 EOS된 6.x 버전을 제외한 모든 버전의  보안패치가 이뤄졌으나, 2020년 3월 현재까지 GitHub에 20개의 POC가 공개되면서 공격빈도도 급격하게 증가하게 되었다. KISA에서는 2월 26일 취약점 보안 업데이트를 권고한 이후 추가로 3월 2일 취약점 보안조치 권고를 발표하였다.

 

AJP 프로토콜은 Tomcat 설치 시에 포트가 기본적으로 활성화되기 때문에 [그림5-1]과 같이 인터넷으로 연결된 장비 정보를 제공해주는 FOFA(FOFA.SO)에 따르면 한국 내에서도 기본포트로 서비스 중인 서버가 290,910건(2020년 3월 12일 기준)으로 취약점의 영향도에 노출되어 있다. 이에 따라 이번 호에서는 원격 코드 실행 가능성이 있어 공격위험도가 높은 Apache Tomcat-AJP 취약점 CVE-2020-1938에 대해 살펴보고자 한다.

 

 


[그림 5-1] 인터넷 상에 공개되어 있는 AJP 포트 사용수치 (출처 : FOFA)

 

02. 취약점 분석

 

1) 영향 받는 소프트웨어

 

 

 

[표 5-1] CVE-2020-1938에 영향 받는 버전 정보

 

2) CVE-2020-1938 공격 매커니즘

 

AJP(Apache Jserv Protocol)프로토콜은 Apache Server와 JAVA EE서버 간의 연결을 위한 프로토콜로, 여러 웹 서버로부터 여러 어플리케이션 서버로의 로드 밸런스를 구현하기 위한 목적으로 사용되게 된다. 세션들은 각각의 어플리케이션 서버 인스턴스 이름을 갖는 라우팅 매커니즘을 사용하여 현재 어플리케이션 서버로 리다이렉트되게 되고 이러한 경우 어플리케이션 서버를 위한 리버스 프록시로 웹 서버가 동작되게 된다.

 

HTTP Connector가 HTTP프로토콜의 기본포트인 8080으로 클라이언트에 노출되는 반면 AJP는 기본포트로 8009를 사용하여 외부에 노출되지 않은 채 Apache HTTP Server에서 mod_jk 또는mod_proxy_ajp 모듈로 구현되게 된다.

 

 


[그림 5-2] Apache Tomcat의 환경설정파일 기본 설정

 


 

 

AJP Request가 Apache에서 Tomcat으로 전송될 때 사용자 입력값 검증없이 실행되면서 임의로 파일 읽기나 실행이 가능하게 된다. 앞서 설명한 잠재적인 원격코드 실행 조건인 파일 업로드가 가능한 환경이라면 임의의 파일이 업로드된 후에 AJP취약점을 이용하여 공격코드를 실행할 수 있게 되는 구조라고 볼 수 있다.

 

CVE-2020-1938 취약점은 앞서 언급했듯, AJP Request 메시지가 Tomcat에 전달될 때 발생한다. AJP Request 메시지를 처리할 때 Tomcat에서는 org.apache.coyote.ajp.AjpProcessor.java를 호출하고 prepareRequest()를 통해 AJP메시지 내용을 추출하여 Request객체의 Attribute속성으로 설정하게 된다.

 

Request 메시지 헤더에서는 파일 확장자에 따라 *.JSP파일은 JspServlet.java에서 처리하고 그 외의 파일은 DefaultServlet.java에서 처리되게 된다. 임의의 파일이 실행되기 위해서는 DefaultServlet.java을 실행해야 하는 파일도 JspServlet.java을 수행하게 하는 것이 취약점의 핵심이라고 볼 수 있다.

 

3) CVE-2020-1938 취약점 상세분석

 

 


[그림 5-4] AJP request 전달 과정

 

[그림 5-4]와 같이 AJP Request에서 사용할 클래스 멤버인 Attribute속성을 살펴보면 사전에 정의되지 않은 속성을 사용하는 경우 ‘SC_A_REQ_ATTRIBUTE’로 설정되게 된다. ‘SC_A_REQ_ATTRIBUTE’를 처리하는 구문에서 취약점이 발생되기 때문에 공격 수행시에 AJP Request의 attribute값을 ‘SC_A_REQ_ATTRIBUTE’으로 설정해서 전송하면 된다.

 

 


[그림 5-5] AJP Request Attribute 정의

 

 


[그림 5-6] AjpProcessor.java 클래스에서의 취약점 발생 코드

 

[그림 5-7] 의 AJP Header 패킷에서 attribute_name(javax.sevlet.include.request_uri, javax.sevlet.include.servlet_path)과 attribute_value(index, /attack.txt) 값을 [그림 5-6]의 코드에서 각각 변수 n과 v에 저장해 attribute 요청인지 아닌지를 확인한 후 request 메시지로 캡슐화 하여 getadapter() 함수를 통해 servlet 과정으로 넘겨준다. 이 과정에서 입력 받은 n, v 값에 대한 별도의 검증 없이 진행되면서 공격이 가능하게 된다.

 

 

 

[그림 5-7] AJP Request 패킷 Header 정보 

 

일반 파일의 경우 위의 과정을 거쳐 만들어진 request값이 DefaultServlet.java 클래스로 전달되어 service() 함수를 호출해 GET Method로 전달한다. service() 함수는 doget()함수를 호출하고 doget() 함수는 serveResource() 함수를 통해 원하는 소스코드를 얻어낸다.

 

serveResource() 함수는 HttpServeltRequest의 값을 getRelativePath함수를 통해 RequestDispatcher.INCLUDE_PATH_INFO와 RequestDispatcher.INCLUDE_SERVLET_PATH을 pathInfo와 serveretPath에 저장하여 받은 return값을 통해 getResource()함수에 넣을 변수를 생성하게 된다. getResource()함수에서는 해당 경로의 파일의 실행결과를 가져오게 된다.

 

 

 

[그림 5-8] http servlet 진행 코드

 

 

 

[그림 5-9] jspServlet.java에서 request 처리

 

JSP파일의 경우 defaultServlet.java 클래스가 아닌 jspServlet.java클래스를 호출하여 request 메시지를 전송하게 된다.

메시지에 대한 처리 방식은 앞서 살펴봤던 defaultServlet.java 와 같이 request 메시지에 저장된 include_path_info attribute를 jspUri에 넣어 request 메시지에 추가 시킨 후 serviceJspFile에서 해당 경로의 리스소를 가져와 컴파일러에게 전송되어 서버에 전달된다. 이 과정에서 변형된 attribute라면 변조된 URI 경로 상의 파일이 실행될 것이다.

 

 

4) POC코드 분석

 

 


[표 5-2] 공격에 사용되는 Attribute 속성 값

 

CVE-2020-1938을 실제 공격 코드로서 작성하기 위해서는 앞에서 설명한 3개의 Attribute 설정 중 javax.servlet.include.path_info를 제외한 2개의 속성값을 변조하여 전송해야 한다. Github에 공개된 POC 중 1개를 분석하여 Tomcat-AJP 취약점을 보다 상세하게 알아보고자 한다

 

 


[그림 10] POC 코드내에서 ‘SC_A_REQ_ATTRIBUTE’값을 설정한 모습

 

POC 코드를 확인해보면 취약점이 존재하는 ‘SC_A_REQ_ATTRIBUTE’구문 실행을 위해서 ‘SC_A_REQ_ATTRIBUTE’값에 10(0A)를 입력해서 전송하고 있다.

 

POC코드의 기능은 크게 2가지로 분류될 수 있다. JSP파일 이외의 파일확장자를 txt파일로 해석하여 DefaultServlet.java으로 실행하게 하는 방법과 JSP파일로 해석하여 JspServlet.java으로 실행하게 하는 방법이 있다. POC코드 상에서는 shoot()함수에서 사용자에게 입력받은 javax.servlet.include.request_uri와 javax.servlet.include.servlet_path을 이용하여 DefaultServlet.java이나 JspServlet.java여부를 결정하게 된다.

 

 

 

[그림 11] POC 코드(ajpshooter.py)의 shoot() 함수

 

5) 공격시연

 

파일업로드가 가능한 환경에서 jspServlet.java을 통해 원격코드실행 취약점이 실제 가능한지 POC코드를 이용하여 [표 3]환경에서 테스트를 진행해 보았다.

 

 

 

[표 3] 공격 시연 환경

 

① Wget명령어를 이용하여 웹쉘파일을 다운로드하는 공격코드가 포함되어 있는 파일생성

 

 


[그림 12] attack.txt 파일 내용

 

② 생성한 공격코드를 /webapps/ROOT 경로에 업로드

 

 

 

[그림 13] 서버에 업로드된 attack.txt 파일

 

③ attack.txt파일이 jsp파일처럼 실행되어 wget명령어를 통해 cmd.jsp파일 다운로드 명령어 실행

 

 


[그림 14] CVE-2020-1938 POC코드를 이용하여 attack.txt 실행 화면

 

 

 

[그림 15] attack.txt 실행 결과 (1)

 

 


[그림 16] attack.txt 실행 결과 (2)

 

03. 대응방안


1) Tomcat 보안패치 적용

 

 


[표 4] CVE-2020-1938 취약점이 패치된 버전 정보

 

Apache Tomcat의 버전을 확인할 수 있는 방법이 2가지 존재한다

① vesion.sh으로 확인하는 방법 : [설치 디렉터리]/bin/version.sh 실행

 

 


[그림 17] ./version.sh 실행 결과

 

② ServerInfo 클래스로 확인하는 방법

  : [설치 디렉터리]/lib에서  ‘java –cp catalina.jar org.apache.catalina.util.ServerInfo’ 명령어 실행

 

 


[그림 18] org.apache.catalina.util.ServerInfo 클래스 내용

 

보안 패치결과 java/org/apache/coyote/ajp/AjpProcessor.java의 prepareRequest()에서 기존에 정의되지 않은 attribute 코드에 대해서 req_attribut에서 별도의 검증없이 파일을 실행하던 취약점은 getAllowedArbitraryRequestAttributesPattern()을 통해 검증하는 기능이 추가되었다.

 

 

 

[그림 19] 취약점 보안패치 결과

 

2) AJP 프로토콜 포트 비활성화 (임시방안)

 

 


[표 5] AJP 프로토콜 비활성화 설정 방법

 

3) AJP 프로토콜 자격 증명 설정 


AJP 프로토콜이 반드시 사용되어야 하고, 업데이트하기가 힘든 상황인 경우 requiredSecret  옵션을 사용하여 자격 증명을 설정한다.

 

 


[표 6] AJP 프로토콜 자격 증명 설정 방법

 

④ SNORT적용

 

 


[표 7] CVE-2020-1938 Snort

 

 

04. 출처

 

[1] Apache Tomcat 취약점 보안 업데이트 권고

   - https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=35279 (1차)

   - https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=35292 (2차)

[2] 전 세계 AJP프로토콜 사용 통계 : fofa pro

   - https://fofa.so/result?q=port%3D%228009%22&qbase64=cG9ydD0iODAwOSI%3D

[3] CVE-2020-1938 POC

   - https://github.com/00theway/Ghostcat-CNVD-2020-10487

[4] CVE-2020-1938:Apache Tomcat 服务器任意文件读取/包含漏洞通告

   - https://www.tuicool.com/articles/J77VNzM

[5] 协议 1938 AJP request 漏洞 2020 SC CVE Constants

   - https://www.icode9.com/content-4-648068.html 

[6] 모든 톰캣에 되는 웹 공격이라고!?! Ghostcat: Tomcat Ajp 프로토콜 취약점 cve-2020-1938

   - https://www.youtube.com/watch?v=y-xtEk7NciE

[7] CVE-2020-1938: Ghostcat - Apache Tomcat AJP File Read/Inclusion Vulnerability

   -https://www.tenable.com/blog/cve-2020-1938-ghostcat-apache-tomcat-ajp-file-readinclusion-

     vulnerability-cnvd-2020-10487

[8] (CVE-2020-1938)Apache Tomcat 文件包含漏洞

   - http://www.0-sec.org/0day/Tomcat/CVE-2020-1938.html

[9] req_Attribute 헤더 정의

   - https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html