보안동향

최신 보안정보를 신속하게 전해드립니다.

[보안취약점] CVE-2019-0211 Apache Root Privilege Escalation

2019-05-28

1. 개요

다중처리 모듈(이하 MPM)로 동작하는Apache Http Server 에서 Out-Of_Bound(이하 OOB) Array Access로 인해 메인 서버 프로세스인 root 권한으로 임의의 함수를 실행 가능합니다. 표준 Linux에서 logrotate는 로그 파일 핸들을 재설정 하기 위해 오전 6:25분 apache2ctl graceful 명령어를 실행하여 apache를 재 시작 합니다. Apache가 정상적으로 재 시작 되었을 때 임의의 함수가 root권한으로 실행됩니다.

 

2. 취약한 버전

 - Apache 2.4.17 ~ 2.4.38

 - PHP 5.x, 7.2.x

 

3. 취약점 설명 및 분석

MPM prefork에서 실행되는 메인 서버 프로세스는 root로 동작합니다. 이 프로세스는 http 요청을 처리하기 위한 단일 스레드, 낮은 권한을 가진 www-data(작업자)들을 관리합니다. 이 작업자들의 피드백을 받기 위해 apache scoreboard는 작업자 pid 및 처리한 요청 등 다양한 정보가 포함 된 공유 메모리 영역(SHM)을 유지 및 관리합니다.

 

각 작업자들은 process_score pid와 관련된 구조를 유지하고 SHM 영역에 대해 rwx 권한을 갖습니다.

각 작업자의 구조는 다음과 같고 bucket 멤버는 메인 서버 프로세스의 all_buckets 배열에 access할 때 사용됩니다.

 

 

All_buckets의 중요한 구조들은 다음과 같습니다. all_buckets 배열에 access할 때 bound check가 존재하지 않기 때문에 process_score 구조의 bucket 멤버를 조작하여 apache가 재 시작 될 때 임의의 함수를 호출할 수 있습니다.

 

 

정상적인 재 시작 프로세스 (prefork.c)

 

1. Logrotate가 apache에 재 시작을 요청합니다.

2. Apache의 메인 서버 프로세서가 오래된 작업자를 죽이고 새로운 작업자를 생성하기 위해 prefork_run() 함수를 호출합니다.

3. ap_wait_or_timeout() 함수에 의해 작업자의 pid를 child_slot 변수에 반환합니다.

4. 해당 작업자의 종료가 정상적이라면 make_child() 함수가 호출되고 해당 함수의 인자로 ap_server_conf, child_slot(pid), ap_get_scoreboard_process(child_slot)->bucket을 사용합니다.

5. Make_child() 함수를 호출해 메인 서버 프로세스가 fork()로 새로운 child를 생성합니다.

6. 그 다음 child_main() 함수가 호출 되고 SAFE_ACCEPT code가 실행 됩니다. SAFE_ACCEPT code는 apache가 2개 이상의 port를 수신하고 있을 때 실행 되며 일반적으로 80, 443 port를 사용하기 때문에 해당 code가 실행됩니다.

 

SAFE_ACCEPT code가 실행되면 apr_proc_mutex_child_init()이 호출되며, 최종적으로 *(mutex)->meth->child_init(mutex,pool,fname)이 호출됩니다.

 

 

4. Exploit

Mod_prefork는 Mod_php와 함께 사용되기 때문에 PHP UAF 취약점을 이용해 OOB read/write로 작업자의 bucket(index of all_buckets)을 조작할 수 있고 공유 메모리 영역(SHM)에 fake prefork_child_bucket 구조를 작성할 수 있습니다.

아래 소스코드로 인해 조작된 bucket(index of all_buckets)으로 my_bucket을 제어할 수 있습니다.

 

 

임의의 함수 호출 프로세스는 다음과 같습니다.

 

 

bucket_id = ap_scoreboard_image->parent[id]->bucket

my_bucket = all_buckets[bucket_id]

mutex = &my_bucket->mutex

apr_proc_mutex_child_init(mutex)

(*mutex)->meth->child_init(mutex, pool, fname)

 

 

적절한 함수 호출을 위해 (*mutex)->meth->child_init() 대신 zend_object_std_dtor(zend_object *object)이 호출될 수 있도록 합니다.

 

 

mutex = &my_bucket->mutex

[object = mutex]

zend_object_std_dtor(object)

ht = object->properties

zend_array_destroy(ht)

zend_hash_destroy(ht)

val = &ht->arData[0]->val

ht->pDestructor(val)

 

 

해당 프로세스는 아래 그림과 같습니다.

Fake prefork_child_bucket 구조에서 pDestructor는 libc_system으로 설정하고 &ht->arData[0]->val은 실행시킬 명령어를 가리키도록 합니다. 추가적으로apache가 재시작 될 때 all_buckets의 주소가 변경되므로 남아있는SHM영역에 fake prefork_child_bucket의 pointer를 spraying시킵니다.

 

최종적인 함수 호출 프로세스와 변조된 메모리 구조는 아래 그림과 같습니다.

 

 

 

burn6@ubuntu1804:~/CVE-2019-0211$ curl localhost/exp.php

burn6@ubuntu1804:~/CVE-2019-0211$ sudo apache2ctl graceful

 

 

PoC 코드를 요청하고 apache2ctl graceful 명령어로 apache가 재 시작되면 root 권한으로 리버스 쉘을 획득할 수 있습니다.

 

 

해당 취약점에 대한 공격 시나리오는 다음과 같습니다.

 

 

5. 위협요소

공격자가 PHP 웹쉘 업로드 취약점 및 XXE 취약점을 이용해 root 권한으로 서버를 장악할 수 있습니다.

 

6. 대응방안

Apache 최신 버전 업데이트

 

──────────────────────────

컨설팅사업본부 모의해킹팀

목록