DTrace based Rootkit Detection (System Call Hooking)

DTrace는 Sun Microsystems에서 개발한 동적 트레이싱 프레임워크이다. [fusion_builder_container hundred_percent="yes" overflow="visible"][fusion_builder_row][fusion_builder_column type="1_1" background_position="left top" background_color="" border_size="" border_color="" border_style="solid" spacing="yes" background_image="" background_repeat="no-repeat" padding="" margin_top="0px" margin_bottom="0px" class="" id="" animation_type="" animation_speed="0.3" animation_direction="left" hide_on_mobile="no" center_content="no" min_height="none"][link] DTrace는 동작 중인 시스템 환경에서 애플리케이션이나 커널의 트러블 슈팅을 위해 개발되었다. 이 프레임워크는 생산성을 높인 D 언어를 사용하며, 코드 몇 줄만으로도 시스템 정보 수집이나 프로세스를 디버깅할 수 있게해주는 강력한 기능을 제공한다. 개발자 입장에서 자신이 작성한 프로그램을 바이너리 레벨에서 디버깅하기에는 아주 훌륭한 프레임워크라 할 수 있지만 조금만 관점을 바꿔보면, 도구의 강력한 기능을 해킹 기술에도 사용할 수 있다.

Mac OS X도 DTrace 프레임워크를 제공하고 있다. 올해 4월, Immunity에서 주최하는 InfiltrateCon 2013에서는 DTrace를 이용하여 다양한 루트킷 기법을 스크립트 형태로 작성하는 방법을 소개하였다 [pdf]. 본 포스팅에서는 이 중 시스템 콜 후킹을 수행하는 특정 디렉터리 내의 파일 엔트리를 은닉하는 방법과 이를 탐지하는 방법을 설명하고자 한다. 코드의 동작 순서를 살펴보면 다음과 같다.

DTrace에서 제공하는 시스템 콜 후킹 기능을 사용하기 때문에, 메모리 분석도구인 volafox[tool]로 탐지할 수 있으나, 실제 테스트 결과 기존 후킹 탐지 방법과는 약간 다른 결과를 보였다. 우선 메모리 분석 도구를 이용하여 시스템 콜 변조를 탐지하기 위해 해당 도구를 Mountain Lion이 설치된 VMware에서 실행하고, 메모리 스냅샷을 분석하였다.

chainbreakers-MacBook-Pro:~ chainbreaker$ uname -a
Darwin chainbreakers-MacBook-Pro.local 12.0.0 Darwin Kernel Version 12.0.0: Sun Jun 24 23:00:16 PDT 2012; root:xnu-2050.7.9~1/RELEASE_X86_64 x86_64

커널 버전은 12.0.0인 Moutain Lion 초기버전이고 64비트 커널이 로드되어 있다.

chainbreakers-MacBook-Pro:~ chainbreaker$ file dirhide.d
dirhide.d: a /usr/sbin/dtrace -s script text executable

스크립트 파일임을 확인하고 현재 디렉터리 상태를 확인했다.

chainbreakers-MacBook-Pro:~ chainbreaker$ ls -al /private/tmp
total 0
drwxrwxrwt  9 root             wheel  306  6  2 11:44 .
drwxr-xr-x@ 6 root             wheel  204  3 18 15:05 ..
drwx------  3 chainbreaker     wheel  102  6  2 11:42 launch-2W8n1B
drwx------  3 chainbreaker     wheel  102  4 20 13:38 launch-YK6Mx4
drwx------  3 chainbreaker     wheel  102  4 20 13:38 launch-rDjEkQ
drwx------  3 _spotlight       wheel  102  4 20 13:12 launchd-129.TJC0L3
drwx------  3 chainbreaker     wheel  102  4 20 13:12 launchd-134.iboIMx
drwx------  3 _windowserver    wheel  102  4 20 13:38 launchd-191.YPTPFj
drwx------  3 _softwareupdate  wheel  102  6  2 11:44 launchd-473.dEMigI

dtrace의 로그를 생성하기 위해 -w 옵션을 주어서 스크립트를 실행한다.

chainbreakers-MacBook-Pro:~ chainbreaker$ sudo dtrace -w -s dirhide.d

스크립트를 실행하고 난 후, 다시 'ls' 명령을 실행하였다.

chainbreakers-MacBook-Pro:~ chainbreaker$ ls -al /private/tmp/
total 0
drwxrwxrwt  9 root             wheel  306  6  2 11:44 .
drwxr-xr-x@ 6 root             wheel  204  3 18 15:05 ..
drwx------  3 chainbreaker     wheel  102  4 20 13:38 launch-YK6Mx4
drwx------  3 chainbreaker     wheel  102  4 20 13:38 launch-rDjEkQ
drwx------  3 _spotlight       wheel  102  4 20 13:12 launchd-129.TJC0L3
drwx------  3 chainbreaker     wheel  102  4 20 13:12 launchd-134.iboIMx
drwx------  3 _windowserver    wheel  102  4 20 13:38 launchd-191.YPTPFj
drwx------  3 _softwareupdate  wheel  102  6  2 11:44 launchd-473.dEMigI
chainbreakers-MacBook-Pro:~ chainbreaker$

확인 결과 3번째 엔트리인 'launch-2W8n1B' 가 숨겨진 걸 확인할 수 있다.

이제 현재 메모리 상태를 스냅샷(snapshot)을 volafox로 분석하였다. 도구는 SVN Repository의 최신 버전을 이용하였다. [Revision 102]

n0fate@n0fate-ui-MacBook-Pro: ~/volafox$ python vol.py -i ~/Documents/Parallels/Mac OS X.pvm/{8ffc8dfd-9a5b-481a-b88d-5653bee07b6b}.mem -o system_profiler
[+] Mac OS X Basic Information
 [-] Darwin kernel Build Number: 12A269
 [-] Darwin Kernel Major Version: 12
 [-] Darwin Kernel Minor Version: 0
 [-] Number of Physical CPUs: 1
 [-] Size of memory in bytes: 2147483648 bytes
 [-] Size of physical memory: 2147483648 bytes
 [-] Number of physical CPUs now available: 1
 [-] Max number of physical CPUs now possible: 1
 [-] Number of logical CPUs now available: 1
 [-] Max number of logical CPUs now possible: 1
 [-] Last Hibernated Sleep Time: Thu Jan 01 00:00:00 1970 (GMT +0)
 [-] Last Hibernated Wake Time: Thu Jan 01 00:00:00 1970 (GMT +0)

버전 정보를 확인하고 systab 명령어로 시스템 콜 테이블 정보 중 후킹된 엔트리를 확인한다.

n0fate@n0fate-ui-MacBook-Pro: ~/volafox$ python vol.py -i ~/Documents/Parallels/Mac OS X.pvm/{8ffc8dfd-9a5b-481a-b88d-5653bee07b6b}.mem -o systab | grep hooked
427         4    0     0              0xFFFFFF8000304CA0 0xFFFFFF8000304CA0 0xFFFFFF80005E3640      0x00000000        6        20 Maybe hooked
n0fate@n0fate-ui-MacBook-Pro: ~/volafox$

코드를 확인해보면, volafox에서 버그로 나타나는 427번 시스템 콜을 제외하곤 후킹을 판단되지 않는다.

DTrace로 인해 후킹된 시스템 콜은 해당 스크립트의 메모리 영역을 점프하는 것이 아니라 커널의 'dtrace_systrace_syscall' 함수로 점프한 후, 사용자가 만든 trace 코드를 실행한다. [ref] OS가 지원하는 정상적인 호출 흐름이기 때문에, 메모리 분석 도구에서 악성 후킹으로 판단하지 않는다. 이러한 후킹을 판단하려면, grep 인자로 'dtrace'를 주어야 한다.

n0fate@n0fate-ui-MacBook-Pro: ~/volafox$ python vol.py -i ~/Documents/Parallels/Mac OS X.pvm/{8ffc8dfd-9a5b-481a-b88d-5653bee07b6b}.mem -o systab | grep dtrace
344         4    0     0        _dtrace_systrace_syscall 0xFFFFFF80005DC5E0 0xFFFFFF80005E34B0      0x00000000        6        16 True
n0fate@n0fate-ui-MacBook-Pro: ~/volafox$

344번 시스템 콜인 'getdirentries64' 함수가 변경된 것을 알 수 있다. 스크립트 실행 정보는 'ps' 명령어로 확인할 수 있다.

n0fate@n0fate-ui-MacBook-Pro: ~/volafox$ python vol.py -i ~/Documents/Parallels/Mac OS X.pvm/{8ffc8dfd-9a5b-481a-b88d-5653bee07b6b}.mem -o ps
[+] Process List
OFFSET(P)  PID PPID PRIORITY NICE     PROCESS_NAME        USERNAME(UID,GID)  CRED(UID,GID)      CREATE_TIME (UTC+0)
0x008DA2E0   0    0        0    0      kernel_task                    (0,0)          (0,0) Sat Apr 20 04:10:56 2013
0x020B9A60   1    0      128    0          launchd     _softwareupdate(0,0)          (0,0) Sat Apr 20 04:10:56 2013
0x020B91A0  11    1      128    0   UserEventAgent                root(0,0)          (0,0) Sat Apr 20 04:11:00 2013
0x020BA8E0  12    1      128    0            kextd                root(0,0)          (0,0) Sat Apr 20 04:11:00 2013
...[SNIP]...
0x3F471760 464    1      128    0         installd     _softwareupdate(0,0)          (0,0) Sun Jun 02 02:43:25 2013
0x1D9D3BC0 473    1      128    0          launchd _softwareupdate(200,200)      (200,200) Sun Jun 02 02:44:24 2013
0x363258C0 475  473      128    0         cfprefsd _softwareupdate(200,200)      (200,200) Sun Jun 02 02:44:24 2013
0x01C31D40 492  448      128    0             sudo       chainbreaker(0,20)         (0,20) Sun Jun 02 02:45:41 2013
0x4026FA60 494  492      128    0           dtrace        chainbreaker(0,0)          (0,0) Sun Jun 02 02:45:45 2013
0x3F8F0480 495    1      128    0 coresymbolicatio     _softwareupdate(0,0)          (0,0) Sun Jun 02 02:45:47 2013
0x446E8300 497    1      128    0             sshd        chainbreaker(0,0)          (0,0) Sun Jun 02 02:46:23 2013
0x43CA7D40 500  497      128    0             sshd     chainbreaker(501,20)       (501,20) Sun Jun 02 02:46:27 2013
0x3ED72EA0 501  500      128    0             bash     chainbreaker(501,20)       (501,20) Sun Jun 02 02:46:27 2013
0x35664180 507  464      128    0 update_dyld_shar     _softwareupdate(0,0)          (0,0) Sun Jun 02 02:51:57 2013

그간 루트킷은 KEXT 기반으로 동작하였기 때문에, 메모리 분석 도구의 후킹 탐지 로직으로 함수 테이블 변조 여부를 확인할 수 있었다. dtrace 기반 루트킷은 기존 KEXT 기반의 루트킷과 다르게 OS에서 제공하는 후킹이기 때문에 기존 메모리 분석 도구의 후킹 탐지 로직을 우회할 수 있다.

KEXT 기반의 루트킷은 커널 메모리를 비공식적으로 조작하는 방법을 사용하기 때문에, 조금의 실수가 발생하더라도 커널 패닉을 유발할 수 있는 문제점을 가지고 있다. 또한, 많은 포렌식 조사관이 'kextstat' 명령을 이용하여 루트킷을 식별하기 때문에 탐지 위험이 높았다. dtrace를 이용한 트릭은 코드 작성이 간단하면서 손쉽게 함수 테이블을 후킹할 수 있는 장점을 가지고 있다.

이러한 상황을 고려하여 앞으로 Mac OS X 시스템을 분석할 경우에는 dtrace를 고려한 메모리 분석을 진행해야 할 것이다.