이 글은 Qihoo360 블로그에 있는 글을 번역한 글입니다. 원문을 보고 싶으신 분은 아래 링크를 참고하시길 바랍니다.


http://blogs.360.cn/blog/venom-%E6%AF%92%E6%B6%B2%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%EF%BC%88qemu-kvm-cve%E2%80%902015%E2%80%903456%EF%BC%89/


혹시라도 잘못된 부분이 있으면 지적해 주시면 감사하겠습니다.

======================================================================================


VENOM 취약점

이번 취약점은 CrowdStrike 소속인 Jason Geggner가 가상 머신의 한 종류인 QEMU에 있는 가상 플로피 디스크 컨트롤러에서 발견한 취약점이다. CVE 번호는 CVE-2015-2356으로 VENOM라고 불린다. 이 취약점이 있는 환경에서 공격을 진행하면 공격자는 VM Escape를 할 수 있고 로컬환경에서 코드를 실행할 수 있는 권한을 얻을 수 있다. 


배경지식

이 취약점은 QEMU의 가상 플로피 디스크의 시뮬레이션 코드에서 발견되었기 때문에 생소하신 분들을 위해서 플로피에서 눈여겨보아야할 몇 가지 부분들에 대해서 설명하겠다.


  • 플로피 레지스터

플로피 레지스터는 총 9개의 레지스터로 이루어져 있으며 이 레지스터들은  0x3f6을 제외한 0x3f0부터 0x3f7까지의 포트들를 통해서 접근을 할 수 있다. 플로피 레지스터는 아래와 같이 이루어져 있다.

이 취약점에 사용되는 레지스터는 DATA_FIFO이다.


  • MSR

MSR의 marker bit는 그 당시 플로피 디스크 드라이버의 상태를 보여준다. 이 취약점과 관련된 MSR marker bit의 정의는 아래와 같다. 

 약칭 기호

 자릿수

 값

 의미

 RQM

 7

 0x80

 만약 FIFO IO 포트와 데이터를 주고 받아도 되는 경우에 사용.

 DIO 

 6 

 0x40

 만약 FIFO IO 포트를 통해 데이터 입력을 받고 싶을 경우에 사용.



  • FIFO 명령어

명령어는 DATA_FIFO에 써지는 32보다 작은 바이트 값이다. 각각의 명령을 진행한 후에는 무조건 지정된 길이의 파라미터가 따라와야 한다. 구성은 아래와 같다.



플로피 디스크 컨트롤러에 대해서 더 자세한 내용을 알고 싶다면 http://wiki.osdev.org/Floppy_Disk_Controller 를 참고하면 된다.



취약점 분석


POC가 실행이 안된다?!

일단 먼저 Marcus Meissner가 발표한 poc 코드를 보자.


이 코드를 보면 DATA_FIFO 포트로 데이터를 입력하는 것을 알 수 있다. 필자가 코드를 얻고나서 먼저 자신의 컴퓨터에서 실행을 해보았으나 익스플로잇에 성공하지 못했다. 그 이유가 무엇이든 일단 먼저 QEMU에서 FIFO 명령어를 어떻게 처리하는 지부터 분석해 보자.

 

FIFO 명령어의 처리 루틴

분석을 통해서 우리는 아래와 같은 결과를 도출해낼 수 있었다. 먼저 qemu는 FIFO의 함수와 명령어와 대응하는 파라미터의 개수 등 정보들을 하나에 테이블에 저장한다. 

이 테이블의 모든 항목은 그에 대응하는 명령어들의 정보를 정의하고 있다. 우리는 일단 이 부분을 Handler라고 얘기하기로 하자. qemu는 FIFO 명령어를 받은 후에 명령어의 ID를 통해 이 명령어의 Handler를 찾게 된다. 그리고 이 Handler에 저장되어 있는 파라미터의 개수를 근거로 계속 파라미터를 받게 되고 그 다음에는 명령어의 ID와 파라미터를 버퍼에 넣어주게 된다. 파라미터를 다 받은 후에는 그에 상응하는 처리 함수를 호출한다. FIFO의 쓰기 작업의 전체적인 과정은 모두 fdctrl_write_data함수에 있다.

만약 처리함수에서 리턴되는 값이 있다면 컨트롤러에서는 fdctrl_set_fifo함수를 콜해서 MSR의 상태를 FD_MSR_DIO로 설정함으로써 컨트롤러가 읽기 가능한 상태임을 표시하게 된다. 

주의: 설정을 완료한 후에도 컨르롤러의 상태가 일기 불가 상태이면 fdctrl_write_data가 시작할 때 하는 검사를 참고해라.

fdctrl_set_fifo 코드는 아래와 같다.

만약 리턴되는 데이터가 없거나 혹은 데이터가 클라이언트에 의해서 IN 명령을 통해 읽어들였다면 fdctrl_reset_fifo함수를 통해 FIFO를 리셋시킨다. FIFO는 쓰기 생태로 리셋된다.


왜 익스플로잇이 안되는 것일까?

위에서 분석한 것을 기반으로 다시 한번 Marcus Meissener가 발표한 poc 코드를 보자. 

먼저 id가 0xa인 제어 명령어를 보낸다. 우리는 id가 0xa인 명령이 FD_CMD_READ_ID인 것을 알 수 있다. 그리고 이에 대응하는 처리 함수가 fdctrl_dandle_readid이고 파라미터는 1개인 것을 알 수 있다.

  1. { FD_CMD_READ_ID, 0xbf, “READ ID”, 1, fdctrl_handle_readid },

그리고는 RESD_ID의 파라미터로 0x42를 넣는다. 그리고는 fdctrl_dandle_readid 함수 안으로 들어가게 된다. 

필자가 fdctrl_handle_readid 함수로 타이머를 실행시켜 보았다. 타이머가 실행되는 순간 프로세스에서는 fdctrl_set_fifo함수를 콜해서 데이터를 리턴시켰다. 

그렇기 때문에 다음 줄에서 0x42를 FIFO에 입력하는 작업은 fdctrl_write_data에서 진행하는 fdctrl -> msr & FD_MSR_DIO에서 조건을 만족시킬 수 없기 때문에 완전히 소용 없는 일이다. 

그렇기 때문에 필자의 환경에서 POC가 작동하지 않았던 것이다.


새로운 익스플로잇 방식

아래는 우리가 패치한 코드이다.

코드를 보면 알 수 있듯이 대부분 fdctrl->fifo에 쓰이는 버퍼가 오버플로우 되지 않도록 하는 패치이다. 그렇기 때문에 이는 분명한 오버플로우 취약점인것을 알 수 있다. 이 사고대로 우리는 모든 명령어의 처리함수를 분석해 보았고 FD_CMD_DRIVE_SPECIFICATION_COMMAND의 처리함수에서 문제가 있는 것을 발견할 수 있었다. 먼저 FD_CMD_DRIVE_SPECIFICATION_COMMAND 명령의 Hanlder는 다음과 같다.

  1. { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, “DRIVE SPECIFICATION COMMAND”, 5, fdctrl_handle_drive_specification_command }

명령 처리 함수의 이름은 fdctrl_handle_drive_specification_command이고 파라미터의 개수는 5개이다. 자 그러면 이제 fdctrl_handle_drive_specification_command 함수의 코드를 보자,

우리는 fdctrl->data_len > 7 이 조건문에서 문제가 있는 것을 발견하였다. 우리는 fdctrl_write_data 이 함수를 시작으로 먼저 FD_CMD_DRIVE_SPECIFICATION_COMMAND 명령어 문자열을 전달하고 이어서 5개의 파라미터를 전달하였다. fdctrl_write_data의 흐름상 처리함수인 fdctrl_handle_drive_specification_command 함수에 들어 갔을 때 fdctrl->data_len은 6이어야 한다. 자 그러면 fdctrl_handle_drive_specification_command에서의 첫번째 조건문 fdctrl->fifo[fdctrl->data_pos - 1]은 우리가 제어 가능한 부분이고 아래의  fdctrl->data_len > 7 이 부분의 조건에도 만족시키지 않을 수 있기 때문에 fdcrtl_set_fifo와 fdctrl_reset_fifo함수를 호출하는 부분들을 우회할 수 있게 되고 버퍼가 리셋되지 않고 컨트롤러의 상태도 쓰기 가능인 상태에서 접근을 할 수 있게 된다. 그러면 우리는 무한대로 fdctrl->fifo로 데이터를 입력할 수 있고 결국은 오버플로우를 일으킬 수 있다. fdctrl->fifo의 초기화는 fdctrl_realize_common함수에 있다.


다시 작성한 POC


익스플로잇 성공!!

linux guest:


windows guest:


결론

이 취약점은 전형적인 힙 오버 플로우 취약점으로 오버플로우 이후에서 쓰기 작업이 가능하다는 점을 노렸다. 이 취약점의 사용 가능성은 매우 높다. 심지어 가상 플로피 디스크를 사용하지 않더라도 여전히 이 취약점을 피해갈 수 없다. 이 취약점은 매우 위험한 취약점에 속하며 최대한 빨리 QEMU를 패치할 것을 권장한다. 


[1] 이 취약점의 발견자의 블로그: http://venom.crowdstrike.com/

[2] IO포트 0x3F6은 ATA(하드디스크)에서 사용하는 상태 레지스터이며 플로피 디스크 컨트롤러를 사용하지 않는다.



출처: http://fandu.tistory.com/53   by Fandu

'기타' 카테고리의 다른 글

SSG 커리큘럼  (0) 2016.09.05
Smart Touch 사칭 안드로이드 악성코드 분석 및 제거 도구  (0) 2015.06.08
KISBIC 수상작  (0) 2015.02.27

WRITTEN BY
sweetchip
세종대학교 해킹/보안 기술 연구 동아리 SSG입니다.

,