PDF로 된문서인데 txt포멧으로 변경후 올립니다.

출처 : http://www.securitymap.net/

--------------------------------------------------------

LKM 루트킷 탐지
- Detecting Loadable Kernel Module(LKM) Rootkit -

Version 2.0
2002. 5.

*이 계 찬, <cagers96 [at] hanmail [dot] net>
**이 현 우, <lotus [at] securitymap [dot] net>
*아주대 정보통신대학원,
**SecurityMap

http://www.securitymap.net

[Document History]

2002. 05. : LKM 루트킷 탐지방법 v2.0
     - Linux Kernel 2.4.x 버전에서의 탐지 도구 설명 추가
     - Solaris 시스템에서의 탐지 방법 설명 추가
2001. 11. : LKM 루트킷 탐지방법 v1.0

[목 차]

I. 개요

II. LKM 루트킷의 이해
 1. LKM(loadable kernel module)의 기본 개념
 2. LKM 루트킷

III. LKM 루트킷 탐지
 1. chkrootkit
 2. Linux LKM 루트킷 탐지
 3. Solaris LKM 루트킷 탐지
 4. Kernel 루트킷 제거

IV. 결론

I. 개요

공격자는 보통 시스템 침입에 성공한 뒤에 자신의 흔적을 남기지 않고 그리고 손쉽게 다시 시스템에 접근할 수 있도록

백도어 및 트로이잔 프로그램을 만들어 설치한다. 백도어의주요 목적은 다음과 같다.

- 관리자가 패스워드 교체, 보안패치 등의 보안조치를 한 뒤에도 다시 시스템에 들어올 수 있도록 한다.
- 시스템 로그파일이나 모니터링 명령에서 탐지되지 않도록 한다.
- 최단시간에 손쉽게 시스템에 접속할 수 있도록 한다.

하지만 공격기술이 일반화된 것처럼 보안기술도 수준이 높아져 루트킷(rootkit)과 같은 사용자 모드에서 실행되는

일반적인 백도어/트로이잔 프로그램은 관리자들이 쉽게 탐지할 수있다. 이에 공격자는 자신의 흔적을 보다

완벽히 감추기 위해서 커널 기반의 루트킷을 사용하기에 이르렀다. 이미 "네트워크 공격기법의 패러다임 변화와

대응방안”에서 커널기반 루트킷에 대하여 언급한 바가 있었다. 그리고 지금은 이러한 커널기반 루트킷이 공격자에게

일반적인 도구로 사용되고 있다. 심지어 커널기반 루트킷을 사용하는 인터넷 웜(W0rmbreed)도 일부 유포된 적이 있었다.

커널기반의 루트킷(이하 LKM 루트킷)은 현재 실행되고 있는 커널에 공격자가 만든 커널모듈

(LKM, Loadable Kernel Module)을 적재해서 시스템 함수의 정상적인 실행을 바꾸는 방법을 사용한다.

이럴 경우 응용 프로그램 레벨의 명령을 사용하는 시스템 분석 방법으로는LKM 루트킷을 탐지하기 힘들게 된다.

LKM 루트킷은 공격자가 만든 프로세스와 파일을 완벽하게 감추어 주고, 관련된 로그를 통제하며, 공격자에게

Root 권한으로 특정 명령을 실행할 수 있도록 해준다.

본 문서는 LKM 루트킷의 작동원리, 탐지방법에 대해 설명한다. 시스템 프로그래밍과 Unix Kernel에 대한 약간의 지식을

갖춘 시스템/네트워크 관리자를 대상으로 쓰여졌다. 필자자신도 약간의 지식만으로 쓴 문서이므로 미약한 부분은

양해를 바란다. 만약 본 문서가 어렵다고 생각한다면, “Reference”에 있는 문서들을 참고 하기 바란다

(더어려울 수도 있음 ^^).

아무튼 본 문서를 통하여 시스템/네트워크 관리자들이 해킹이 의심되는 시스템을 보다 더잘 분석 할 수 있으면 한다.

또한 본 문서는 Securitymap 사이트에서 진행하고 있는 “Honeynet Project”를 위한 것으로,

실제 침해사고를 분석하는데 도움을 주기 위함이다. 그리고 더 나아가 이러한 공격 분석 정보를 서로 공유함으로써

해킹사고가 국내에 확산되는 것을 방지하기 위한 목적도 갖고 있다. 따라서 실제 인터넷 상에서 일어나는

공격에 대해서 분석을 한 경우, 그 결과를Securitymap 사이트를 통하여 많은 사람과 공유했으면 한다.

보다 자세한 내용은 다음 페이지를 참고하기 바란다.

http://www.securitymap.net/sp-index.html

II. LKM 루트킷의 이해

1. LKM(loadable kernel module)의 기본 개념

 운영체제는 일반적으로 Micro Kernel 과 Monolithic kernel 디자인으로 분류된다. Micro kernel디자인은 다수의

완전히 분리된 모듈(또는 프로세스)로 구성되며, 각 프로세스간에는 메시지교환방식을 이용하여 통신하게 된다.

반면, Monolithic 디자인은 내부적으로 여러 하위 모듈로 나뉘어진 하나의 커다란 프로세스가 커널을 이루게 된다.

즉, 커널이 하나의 커다란 실행 이미지로 이루어진다.

 Micro kernel 디자인의 장점은 메모리의 효율적인 사용을 제공한다. 각각의 모듈은 필요할때만 메모리상에 로드되어

사용되며, 사용되지 않는 모듈은 메모리에 로드되지 않는다.

LKM은 커널에 바로 이러한 동적인 기능을 제공하는 메커니즘으로, 시스템 콜 또는 커널의자원 요청에 따라 동적으로

필요한 모듈만을 로드하거나 언로드하는 기능을 제공한다. 또한커널 코드를 수정하고, 다시 컴파일하고,

또는 재 부팅할 필요 없이 커널 모듈을 개발할 수있는 장점을 제공한다. 일반적으로 디바이스 드라이버를 로드하는데

사용되는데, 이는 새로운 디바이스를 시스템에 연결할 때마다 커널을 다시 컴파일하고 재 부팅해야 하는 불편함을

없애기 위한 것이다. 즉, 윈도우 시스템에서의 Plug and Play 개념으로 이해하면 된다. 

Monolithic kernel의 장점은 커널을 변조하기 힘들다는 것이다. 즉, 커널을 변조하기 위해서는 커널을 수정해서

다시 컴파일해야 하고 시스템을 재부팅 시켜야 한다. 이는 보안 측면에서 장점이기는 하지만, 사용측면에서는

비효율적인 면이 있다. 그리고 Monolithic Kernel 디자인에 취약점이 없는 것은 아니다. 단지 Micro Kernel에 비해 좀더

안전하다는 뜻이다.

 Solaris는 Micro Kernel 기반의 Unix 운영체제이며 근본적으로 monolithic 커널을 만들 수없다.

반면 Linux와 BSD 운영체제는 원래 monolithic 커널 디자인이며, 사용상의 편리를 위해 동적으로 커널 모듈을

로드/언로드 할 수 있는 기능이 추가 되었다. 따라서 Linux와 BSD는 이러한 동적 기능을 제거할 수 있으며

완전히 Monolithic 커널로 구성할 수 있는 장점이있다.

1.1. 리눅스 LKM

우선, 리눅스 시스템에서 커널 모듈과 관련된 파일 및 디렉토리에 대해서 살펴보자.

/lib/modules."kernel_version" 디렉토리에서 LKM과 관련된 파일과 디렉토리를 볼 수 있다.

[root@cagers96 2.2.16-21hl]# ls -1
block
build
cdrom
fs
ipv4
misc
modules.dep
modules.pcimap
net
pcmcia
...


이중에서 net 디렉토리를 예로 살펴보면 파일명이 "*.o" 로 표시되는 여러 가지 커널 모듈
을 발견할 수 있는데, 이는 오브젝트 파일임을 의미한다.

[root@cagers96 net]# ls
3c501.o
cs89x0.o
ewrk3.o
3c503.o
de4x5.o
fmv18x.o
eexpress.o
...

 /dev/ksyms 파일에 있는 내용은 Kernel Symbol을 나타낸다. 각 각의 LKM에서 사용하는 변수나 함수 이름,

그리고 가상 주소를 확인 할 수 있다. 이 파일을 조사하여 LKM 루트킷을탐지할 수도 있겠지만, 대부분의 경우,

이를 감추는 경우가 많다.
 
 /boot/System.map 파일은 Linux kernel을 컴파일할 때 생성되는 것으로, 모든 kernel symbol과,
그와 관련된 고정된 주소 정보를 가지고 있다. 이 파일은 컴파일할 때 만 필요한 것이다.
따라서 본 파일을 이용하면, 원래의 symbol 주소를 확인할 수 있다. 단 System.map 파일이변조 되지 않아야 한다.

 리눅스는 커널 모듈을 커널에 로딩하거나, 현재 로딩된 커널 모듈 목록을 열람하고, 그리고 로딩된 커널 모듈을

제거할 수 있는 다음과 같은 명령을 제공한다.

o lsmod : 현재 커널에 로드되어 있는 모든 모듈 목록을 보여준다. 이 리스트는/proc/modules 파일에서도 확인할 수 있다.

이 명령을 이용해 현재 어떤 커널 모듈이 실행되고 있는지 확인 할 수는 있으나, 공격자는 이러한 명령을

우회할 수 있는 방법으로 커널 모듈을 만들어 사용한다. 따라서 커널 루트킷을 탐지하기 위한 명령으로 사용할 수는 없다.

o insmod : 커널 모듈을 커널에 로딩하는 명령이다. LKM 루트킷을 커널에 로드하기 위해사용한다.

o rmmod : 커널에서 특정 커널 모듈을 제거하는데 사용한다. 하지만, 실제로 LKM 루트킷을 제거할 수는 없다.

1.2 Solaris LKM

(1) Solaris 부팅 과정

 Solaris 시스템에 대한 커널 루트킷 공격을 이해하고 탐지하기 위해서는 Solaris 운영체제의 부팅과정을

이해하는 것이 중요하다. 다음은 Solaris 시스템의 부팅 과정을 간단히 설명한다.

 1단계 : 시스템의 Open Boot PROM을 이용해 Bootblock 프로그램을 메모리에 로딩한다.

2단계 : bootblock 프로그램이 ufsboot(로컬 디스크 부팅) 또는 inetboot(네트워크 부팅) 라는 두 번째

boot 프로그램을 메모리에 로드한다.

3단계 : ufsboot 프로그램이 core kernel 실행파일(/platform/<arch>/kernel/unix)과 커널링커

프로그램(/kernel/misc/krtld)을 로드 한다.

4단계 : krtld 프로그램은 core kernel 실행 파일인 “unix” 프로그램의 ELF 헤더 정보를 분석하고, 연관된 다른 실행

이미지를 찾아내서 로드시킨다. 이후 “unix” 프로그램으로 제어를넘긴다.

5단계 : unix 프로그램이 kernel 초기화를 위하여 프로세서 리지스터 초기화, mlsetup(),main(), startup()

커널 함수 호출 등을 한다. 이후 kernel 설정 파일인 “/etc/system”을 참고하여 커널 메모리에 필요한

시스템 패러미터 데이터구조를 생성한다.

 여기서 “/etc/system” 파일은 LKM을 통제하는데 매우 유용한 역할을 하므로 반드시 기억해 두어야 한다.

주로 kernel 모듈을 강제적으로 로딩하거나, kernel 모듈의 로딩을 제한하거나, 그리고 kernel 모듈의 검색 경로

정보를 설정할 수 있다.

6단계 : main() 함수가 newproc() 커널 함수를 호출하여, 최초의 사용자 프로세스인 init 프로세스를 생성한다.

커널은 init 프로세스에 사용자 주소 공간을 할당하고, init 은 모든 유닉스 프로세스의 조상이 된다.

이후의 부팅 과정은 사용자 주소공간 내에서 init 프로세스에 의해 진행된다.

그리고 이때 “/etc/inittab” 파일을 참조하게 된다. 이 파일에는 시스템상태, /etc/rc*.d” 디렉토리의 실행 스크립트

등에 대한 설정 정보가 있다.

(2) Solaris LKM

 Solaris 시스템에서는 LKM을 device driver, system calls, file systems, misc, streams modules,
scheduling classes, exec file 의 7개 유형으로 구분하고 있다. 이는 “/usr/include/sys/modctl.h” 파일에 정의되어 있다.

 LKM을 로드하기 위해서는 커널 함수인 modload()나 또는 사용자 프로그램인 modload(1)
를 사용하면 된다. 로드될 커널모듈은 “/etc/system” 파일에 설정된 커널모듈 검색 경로에 존재하여야 한다.

 커널은 로드된 모든 모듈에 대한 자료구조를 Linked list로 유지하고 있다(관련 자료구조는
“/usr/include/sys/modctl.h”와 “/usr/include/sys/kobj.h”에서 참고 할 수 있다). Modload()가 호출되면,

먼저 모듈 자료구조의 linked list를 검색해서 이미 해당 모듈이 존재하는지를 검사한다.
만약 존재하지 않으면 새로운 자료구조를 생성하고, 이를 Linked list에 추가한다. 여기서 주목할 것은 모듈이

언로드될 경우 자료구조의 mod_loaded 라는 요소만 설정해지되고, 모듈의자료구조는 그대로 linked list에

남아있게 된다는 것이다. 이는 시스템이 실행된 이후에 로드되었던 모든 커널 모듈 정보를 알 수 있다는 뜻이다.

 현재 로드된 모듈에 대한 심볼(symbol) 테이블은 “/dev/ksyms” 에 저장되며, 다음 명령으로 이의 내용을 볼 수 있다.

    nm ?x /dev/ksyms

 여기서 나온 결과는 모듈에서 사용된 변수나 함수의 이름, 그리고 연관된 가상주소를 보여준다.

 현재 로드된 모듈 정보를 보여주는 명령으로는 modinfo(1M)라는 명령을 사용할 수 있다.

2. LKM 루트킷

 대부분의 LKM 루트킷은 정상적인 시스템 콜을 가로채서(System Call Interception 또는 hooking)

공격자가 만든 시스템 콜 함수가 실행되도록 하는 방법을 사용한다. 즉, 커널은 전역변수인

"sys_call_table"에 정의된 시스템 콜 함수의 위치(주소)를 참조하여 원하는 기능을수행하는데,

LKM 루트킷은 원래의 정상적인 system call 함수 위치를 공격자가 만든 시스템콜의 주소로 바꿈으로서 공격자의

시스템 콜 루틴이 실행되도록 만드는 것이다. 다음은LKM 루트킷 모듈의 일부로 어떻게 시스템 콜을 가로채서

공격자의 프로세스나, 파일, 그리고 특정 문자열 등을 감추는지에 대하여 몇 가지 예를 들어 설명한다.
 
 참고 자료 : 커널 기반 루트킷 분석 보고서, 정현철
    
    http://www.certcc.or.kr/paper/incident_note/in2000004.html

2.1 자기자신 감추기

LKM 루트킷은 “lsmod”(Linux) 또는 “modinfo”(Solaris) 명령에 의해 표시되지 않도록 하는 은닉 기능을 가지고 있다.

일반적으로 module 이름을 NULL 값으로 지정함으로써 가능하다.

또한 Linux의 경우, LKM 루트킷이 사용하는 symbol을 등록되지 않게 함으로써 symboltable(/dev/ksyms)을

분석하더라도 LKM 루트킷의 존재 여부를 확인할 수 없도록 한다. Solaris시스템의 경우에는 아직 Kernel symbol을

감추는 기능을 제공하는 LKM 루트킷이 공개되지 않은 것으로 알고 있다.

2.2 파일 감추기

공격자는 어느 순간에나 시스템에서 자신의 존재(자취)를 감추어야 한다. 특히, 공격자가 사용하는 파일이나

디렉토리는 시스템 관리자에게 전혀 보이지 않도록 해야 한다. LKM 루트킷을 이용하여 다음과 같이

이러한 작업을 할 수 있다.

"getdents"는 ls 명령을 실행할 때 호출되는 system call 인데, 다음과 같은 커널 모듈을 이용하여 getdents

시스템 콜의 호출을 가로채서 공격자가 만든 시스템 콜 인 hacked_getdents가 실행되도록 한다.

그리고 hacked_getdents 루틴은 공격자가 만든 파일은 출력이 되지 않도록 해준다.

int hacked_getdents( )

int init_module()

orig_getdents = sys_call_table[__NR_getdents];
sys_call_table[__NR_getdents] = hacked_getdents;

void cleanup_module()

sys_call_table[__NR_getdents] = orig_getdents;

2.3 스트링 감추기

전통적인 루트킷에서는 ps, netstat, who 등의 프로그램을 수정하여 공격자의 프로세스나 IP주소, 또는 ID 등이

나타나지 않도록 하는 방법을 사용한다. 하지만 이는 시스템 관리자에의해 쉽게 발견될 수 있다는 문제점이 있다.

보다 좋은 방법은 결국 시스템에서 무엇인가를출력할 때는 write() 시스템 콜을 사용하므로 다음과 같이

write 시스템 콜을 가로채서 특정스트링(공격자의 id, ip 주소 등)을 출력하지 않도록 하는 트로이잔 버전의

write 시스템 콜을실행시키는 것이다.

int hacked_write( )

int init_module( )

orig_write = sys_call_table[__NR_write];
sys_call_table[__NR_write] = hacked_write;

void cleanup_module()

sys_call_table[__NR_write] = orig_write;

III. LKM 루트킷 탐지

기존의 Rootkit은 공격자의 프로세스, 디렉토리, 파일 그리고 접속 사실까지도 숨길 수 있다.

하지만 이들은 ps, df, netstat, top, lsof 와 같은 사용자 계층의 프로그램 코드를 변경하여 원하는 기능을 제공하는 것이다.

따라서 이러한 Rootkit 은 파일의 사이즈, 사용되는 시스템콜 추적, 그리고 파일의 무결성을 검사하여 쉽게 탐지할 수 있다.

 하지만 이러한 방법은 LKM Rootkit을 탐지하는 데는 효과적이지 못하다. 왜냐하면 LKM루트킷은 사용자 계층의

프로그램을 수정하지 않고, 직접 커널의 활동을 변경시키기 때문이다. 따라서 LKM 루트킷이 설치된 시스템을

분석하기 위해서는 더 이상 시스템 명령을 사용해서는 탐지할 수가 없으며 다음의 kstat, carbonite와 같은

분석 프로그램의 도움을 받아야만한다. 애석하게도 LKM 루트킷은 이미 OS 별로 공개되어 있는 반면,

위 도구는 Linux 기반이므로 다른 UNIX 시스템에서는 사용할 수 없다.
 
 
1. chkrootkit

chkrootkit은 대부분의 일반적인 루트킷을 탐지해 주는 도구로 대부분의 Unix, Linux 시스템상에서 컴파일되고 실행된다.

다음 사이트에서 다운로드 받을 수 있다.

http://www.chkrootkit.org

위 도구는 LKM 루트킷이 아닌 일반적인 루트킷을 비교적 잘 탐지해 주며, 인터넷 웜을 포함한 현재까지 알려진 대부분의

루트킷을 탐지할 수 있다. 하지만 공격자가 루트킷을 많이 변형시켜 사용할 경우에는 잘 탐지하지 못할 수도 있음을

고려하여야 한다.

 특히, chkrootkit 모듈중에 “chkproc” 이라는 모듈은 LKM 루트킷의 존재여부를 탐지하는데 유용하게 사용될 수 있다.

특히, Solaris와 같이 특별히 LKM 루트킷 탐지를 위한 별도의 공개보안도구가 없는 경우에는 더욱 그러하다.
 
 Chkproc 프로그램은 “ps” 결과와 /proc 디렉토리 내의 검색 결과를 비교하여, “ps” 결과에 나오지 않는 프로세스를

찾을 수 있도록 한다. /proc 디렉토리는 kernel이 관리하는 프로세스정보를 사용자에게 보여주기 위한 인터페이스

역할을 하는 가상 디렉토리이다. 물론 공격자가 특별히 특정 프로세스를 감추지 않았다면 사실 별로 소용이 없다.

따라서 LKM 루트킷탐지를 위한 보조 도구로 쓸 수 있는 것이지, 모든걸 해주는 것은 아니다.

그리고 주의할 것은 많은 프로세스와 작업이 수행되는 시스템에서는 프로세스가 수시로 생겼다가 사라질수 있으므로,

chkproc 프로그램이 잘못된 결과를 보여줄 수 있다는 점을 고려하여야 한다.

다음은 chkproc 프로그램에서 “-v” 옵션을 사용하여 나온 결과이다.

[root@violet93 chkrootkit-0.30]# ./chkproc -v
PID 542: not in readdir output
PID 542: not in ps output
You have 1 process hidden for readdir command
You have 1 process hidden for ps command



2. Linux LKM 루트킷 탐지

2.1 Kstat

kstat는 다양한 Kernel의 정보를 보여주는 도구로, Linux 상에서 LKM을 분석하기 위한 기
능을 제공한다. Linux kernel 2.2.x 와 2.4.x 에서 테스트되었으며, 각 각 서로 다른 소스로
만들어져 있다. Kstat의 원래 다운로드 사이트는 아래와 같으며, 백업 사이트로 Securitymap
에서 다운로드 받을 수 있다.

원본 사이트 : http://s0ftpj.org/en/site.html
백업 사이트 : http://www.securitymap.net/sp/sp-index.html

(1) Kstat 컴파일 및 설치(For Linux Kernel 2.2.x, Tested Redhat 6.2)

[root@cagers96 KSTAT]# make kstat
Compiling kstat on cagers96...
gcc -O2 -Wall -Werror -I./include-linux/ -c ./src-linux/main.c
gcc -O2 -Wall -Werror -I./include-linux/ -c ./src-linux/if.c
gcc -O2 -Wall -Werror -I./include-linux/ -c ./src-linux/util.c
gcc -O2 -Wall -Werror -I./include-linux/ -c ./src-linux/kmem.c
gcc -O2 -Wall -Werror -I./include-linux/ -c ./src-linux/ps.c -D CAP_BOUND=`cat /proc/sys/kernel/cap-
bound`
gcc -O2 -Wall -Werror -I./include-linux/ -c ./src-linux/lkms.c -D HEXMODS=0xc01ec480
gcc -O2 -Wall -Werror -I./include-linux/ -c ./src-linux/syscalls.c -D SYSTEMMAP=/boot/System.map
gcc -O2 -Wall -Werror -I./include-linux/ main.o if.o util.o ps.o kmem.o lkms.o syscalls.o -o kstat
gcc -O2 -Wall -Werror -c ./src-linux/knull.c
strip kstat
[root@cagers96 KSTAT]# ls
Makefile doc/ if.o kmem.o kstat* main.o src-linux/ util.o
README examples/ include-linux/ knull.o lkms.o ps.o syscalls.o



※ SMP(more than one processor) kernel일 경우에는 Makefile 파일에서 CC 부분(5번째 줄)에
다음과 같이 _”-D__SMP__”_를 추가한다. 자신의 시스템이 SMP 인지는 “uname ?a |
grep -i smp” 명령으로 확인 할 수 있다.
 
CC = gcc -D__SMP__ -O2 -Wall -Werror

(2) Kstat24 컴파일 및 설치(For Linux Kernel 2.4.x, Tested Redhat 7.2)

 o Include/linux.h 50번째 라인 수정

    #elif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,16)
    수정 후: #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,16)

 o make.orig 수정

    CC = gcc -O2 -Wall ?Werror 라인에서 다음과 같이 ?Werror 제거
    수정후: CC = gcc -O2 -Wall

    INCLUDE = -I./include/ -I/usr/src/linux/include 라인에서 리눅스 소스 디렉토리 수정,
    Redhat 7.2의 경우 다음과 같이 수정하였음.
    수정후: INCLUDE = -I./include/ -I/usr/src/linux-2.4/include

 o src/procex.c 34번째 라인

    MODULE_LICENSE("GPL"); 라인 주석 처리
    수정후: // MODULE_LICENSE("GPL");

 o src/knull.c 20번째 라인

    MODULE_LICENSE("GPL"); 라인 주석 처리
    수정후: // MODULE_LICENSE("GPL");

 o src/ps.c 128번째 라인 주석 처리

    if(taskk.flags&PT_PTRACE_CAP)printf("PF_PTRACE_CAP ");
    수정후: // if(taskk.flags&PT_PTRACE_CAP)printf("PF_PTRACE_CAP ");

 o 컴파일 : ./install.sh

 o 컴파일시 netproto.c 에서 컴파일 에러가 날 경우,

  - make.orig 파일에서 다음 라인 수정

    $(CC) $(INCLUDE) -c $(SRC)netproto.c --> 주석 처리
    수정후: # $(CC) $(INCLUDE) -c $(SRC)netproto.c

    마찬가지로 다음 라인에서 netproto.o 제거

    OBJS = main.o if.o util.o ps.o kmem.o lkms.o syscalls.o netproto.o procsyms.o
    수정후 : OBJS = main.o if.o util.o ps.o kmem.o lkms.o syscalls.o procsyms.o
    

  - src/main.c 파일에서 다음과 같이 세 라인을 주석 처리

    // case 'n':
    // show_netprotos();
// break;

 o 컴파일 : ./install.sh
    
 o 설치 : make install

* 컴파일시(./install.sh) 여러 질문이 나오는데, 대부분 무시하면 되고, Ipv6를 지원하는 가에 대한 질문에만 “y” 또는 “n”로

대답하면 된다.

* Kstat24(for Linux Kernel 2.4.x)의 경우, 테스트 시 Redhat 7.2에서 제대로 컴파일을 하지 못했다.

따라서 “?n” 옵션은 사용할 수가 없다. 하지만 이정도만 해도 LKM 루트킷을 탐지하는데 큰 도움이 될 것이다.

(3) kstat 사용 방법

다음은 kstat 명령 사용법으로 이중 LKM 루트킷 탐지에 유용한 몇 가지에 대해서만 설명하도록 한다.

Kstat24(for Linux Kernel 2.4.x) 에서는 이전 버전보다 많은 기능을 지원한다.

Usage: kstat [-i iff] [-P] [-p pid] [-M] [-m addr] [-s]
-i iff may be specified as 'all' or as name (e.g. eth0)
displays info about the queried interface
-P displays all processes
-p pid is the process id of the queried task
-M displays the kernel's LKMs' linked list
-m addr is the hex address of the queried module
displays info about the module to be found at addr
-s displays info about the system calls' table

o kstat -s

s 옵션은 LKM 루트킷을 가장 효과적으로 탐지할 수 있는 방법으로, 이는 커널의 각 시스템 콜의 올바른 주소와 현재

커널에서 사용하는 시스템 콜 주소가 일치하는지를 점검한다. 즉, 올바른 시스템 콜의 주소가 담긴 "system.map" 파일의

내용과 현재 커널이 사용하는 시스템 콜 주소를 비교해서 그 결과가 다르면 LKM 루트킷이 설치된 것으로

의심할 수 있는 것이다.

[root@cagers96 KSTAT]# kstat -s
sys_fork 0xc284652c WARNING! Should be at 0xc0108c88
sys_read 0xc2846868 WARNING! Should be at 0xc012699c
sys_execve 0xc2846bb8 WARNING! Should be at 0xc0108ce4
sys_kill 0xc28465d4 WARNING! Should be at 0xc01106b4
sys_ioctl 0xc2846640 WARNING! Should be at 0xc012ff78
sys_settimeofday 0xc2846a8c WARNING! Should be at 0xc0118364
sys_clone 0xc2846580 WARNING! Should be at 0xc0108ca4



위는 knark 이라는 LKM 루트킷이 설치됐을 때 "kstat -s"를 실행시킨 결과이다. 보이는것처럼 여러 시스템 콜의
주소가 서로 상이함을 알 수 있다. sys_settimeofday 루틴은0xc0118364 주소에 위치해 있어야 하는데,
현재 0xc2846a8c 위치에 있으므로 뭔가 잘못됐다는 것을 보여준다.
보통 LKM 루트킷이 설치되면 여러 개의 시스템 콜 주소가 변경된것으로 나오게 된다.

o kstat -M

 현재 Kernel에 로드되어 있는 모든 LKM을 리스트 해준다. ?S 옵션과 마찮가지로 LKM 루트킷을 찾는데 유용한 옵션이다.

다음은 “kstat ?M” 결과를 보여준다. Kernel에 로그된 LKM의 이름과 주소를 보여준다.
 

[root@dbserver2 2.4]# kstat -M
Using /lib/modules/misc/knull.o
Module Address
knull 0xc48cc000
pcnet32 0xc48dd000
ide-scsi 0xc48c8000
scsi_mod 0xc48af000
ide-cd 0xc48a7000
cdrom 0xc489f000
ext3 0xc480c000
jbd 0xc4800000

o kstat ?m

 특정 주소에 로드된 LKM에 대한 자세한 정보를 보여준다. “-M” 옵션을 사용하여 나온 결과에서 보다 자세한

LKM 정보를 보고 싶을 때 사용한다.
 
[root@dbserver2 2.4]# kstat -m 0xc48dd000

Probing memory at 0xc48dd000

Name: pcnet32
Size: 12144
Flags: MOD_RUNNING MOD_VISITED MOD_USED_ONCE
First Registered Symbol: __insmod_pcnet32_S.rodata_L12 at 0xc48dfae0

o kstat -P

kstat -P 옵션은 현재 실행중인 모든 프로세스 목록을 보여준다. 이것은 LKM 루트킷에 의해 숨겨진 프로세스까지도 보여준다. LKM 루트킷에 의하여 보이지 않는 프로세스가 있는지 알아보기 위해서는 kstat -P 결과와 ps -ef 명령의 결과가 같은지 비교해 보아야 한다.

다음은 LKM 루트킷을 이용해 portmap 프로그램을 감추었을 경우, kstat -P 결과와 ps -ef 결과의 차이를 보여준다.

[root@cagers96 KSTAT]# kstat -P
PID PPID UID GID COMMAND
1 0 0 0 init
2 1 0 0 kflushd
3 1 0 0 kupdate
4 1 0 0 kpiod
5 1 0 0 kswapd
6 1 0 0 mdrecoveryd
241 1 1 0 portmap
256 1 0 0 lockd
257 256 0 0 rpciod
266 1 0 0 rpc.statd
280 1 0 0 apmd
331 1 0 0 syslogd

[root@cagers96 KSTAT]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Mar30 ? 00:00:06 init [3]
root 2 1 0 Mar30 ? 00:00:00 [kflushd]
root 3 1 0 Mar30 ? 00:00:00 [kupdate]
root 4 1 0 Mar30 ? 00:00:00 [kpiod]
root 5 1 0 Mar30 ? 00:00:00 [kswapd]
root 6 1 0 Mar30 ? 00:00:00 [mdrecoveryd]
root 256 1 0 Mar30 ? 00:00:00 [lockd]
root 257 256 0 Mar30 ? 00:00:00 [rpciod]
root 266 1 0 Mar30 ? 00:00:00 rpc.statd
root 280 1 0 Mar30 ? 00:00:00 /usr/sbin/apmd -p 10 -w 5 -W -s
root 331 1 0 Mar30 ? 00:00:00 syslogd -m 0



 o kstat -p

kstat -p는 프로세스에 대해 자세한 정보를 제공한다. kstat -p를 실행할 때는 프로세스 id를 입력 값으로 주어야 한다.

하지만 “carbonite"라는 도구를 사용하는 것이 "-P" 또는 _”-p” 옵션을 사용하는 것보다 더 자세한 정보를 얻을 수 있다.

[root@cagers96 KSTAT]# kstat -p 975
Name: ping
State: S (sleeping)
Pid: 975
PPid: 935 (bash)
Uid: 500 500 500 500
Gid: 500 500 500 500
Flags: PF_SUPERPRIV
Crucial Capabilities Check
Open Files
0 CHAR //2
1 CHAR //2
2 CHAR //2
3 210.107.195.36:1 0.0.0.0:0



2.2 carbonite

carbonite는 시스템에서 실행되는 프로세스 관련 정보를 포함한 /proc 파일시스템을 보다
정확히 분석하기 위한 도구이다. 이 도구는 "cryogenic" 이라는 사용자 레벨에서의 분석도구
를 기반으로 만들어 졌으며, LKM에 의해 숨겨진 프로세스를 점검할 수 있도록 확장한 것이
다. carbonite는 실행중인 프로세스 정보를 관리하는 리눅스 커널 구조체인 task_struct 를 검
색하여 프로세스 정보를 보여준다. carbonite는 리눅스 커널 2.2 대에서만 테스트 하였다.

(1) 다운로드

carbonite는 다음 사이트에서 다운로드 받을 수 있다.

원본 사이트: http://www.foundstone.com
백업 사이트: http://www.securitymap.net/sp/sp-index.html

(2) 컴파일 및 설치

압축을 푼 후에 다음과 같이 컴파일 하면 된다.

[root@www carbonite]# make

※ SMP(more than one processor) kernel 일 경우 다음과 같이 'CFLAGS' 라인에 있는 주석처
리를 제거한다. 자신의 시스템이 SMP 인지는 “uname ?a | grep -i smp” 명령으로 확인 할수 있다.

CFLAGS = -D__SMP__ --> 주석기호 제거
all: carbonite.o
...

(3) 사용 방법

스크립트 파일인 "carbonite.sh"을 다음과 같이 실행하면 된다.

[root@www carbonite]# ./carbonite.sh

결과 파일은 /tmp/CARBONITE 디렉토리에 생성되는데, 이중 CARBONITE.html 파일을 보면 된다.

현재 실행되고 있는 프로세스에 대한 상당히 자세한 정보를 보여주는데, 결과 중두 번째 컬럼(H?) 부분에

"Y"라고 표시되면 해당 프로세스가 LKM 루트킷에 의하여 숨겨졌다는 것을 의미한다.

한번 "ps" 명령으로 확인해 보면 확실히 알 수 있게 된다. 다음의 예는LKM 루트킷에 의해 감추어진 프로세스인

xterm 을 찾아낸 것이다. 이젠 적어도 PID를 알아냈으니 해당 프로세스를 중지 시킬 수 있다.

PID H? UID STAT COMM START TIME (Jiffies)
814 N 0 R (running) insmod 0 Days ago at 16:13:03 (26674)
0 N 0 R (running) swapper 0 Days ago at 16:08:37 (0)
1 N 0 S (sleeping) init 0 Days ago at 16:08:37 (73)
2 N 0 S (sleeping) kflushd 0 Days ago at 08:13:46 (33)
...
1684 N 0 S (sleeping) bash 0 Days ago at 17:09:21 (3213563)
1695 Y 0 S (sleeping) xterm 0 Days ago at 17:09:34 (3214855)

****
PID: 1695
COMMAND: xterm
ARGS: xterm

ENV: LESSOPEN=|/usr/bin/lesspipe.sh %s
COLORTERM=
HOSTNAME=mikebarry
LOGNAME=mbarry
MAIL=/var/spool/mail/mbarry
MACHTYPE=i386
TERM=xterm
HOSTTYPE=i386-linux
PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin

3. Solaris LKM 루트킷 분석

 Solaris LKM 루트킷을 분석하기 위한 공개도구는 아직 없는 것으로 알고 있다. 따라서Solaris 상에서 LKM 루트킷을

분석하기 위해서는 수작업을 필요로 한다. 또한 수작업의 한계로 인하여, 새로운 종류의 LKM 루트킷은 탐지 못할 수도

있음을 고려하기 바란다.
 
 Solaris 시스템에서 LKM 루트킷 분석을 위해 사용할 수 있는 명령으로는, kernel에 로딩된 모듈 목을 보여주는

“modinfo”와 kernel symbol table(/dev/ksyms) 내용을 보여주는 “nm”을 사용할 수 있다.

즉, “modinof” 결과와 “nm ?x /dev/ksyms” 결과를 서로 비교해 보는 것이다.

만약 LKM 루트킷이 사용하는 symbol이 kernel symbol table에 등록되지 않는다면 이러한 분석은 소용이 없을 것이다.

하지만 아직 그러한 Solaris용 LKM 루트킷은 공개되지 않은 것으로알고 있다.
 
 다음은 Solaris용 LKM 루트킷(slkm)이 설치된 시스템에서 “modinfo” 명령으로 모든 로딩된LKM을 리스트 한 것이다.

물론 LKM 루트킷 이름은 감추어져 있기 때문에 나오지 않는다.
 
Load Addr ID Load Addr Size Module Name
---------------------------------------------
f59e1000 -- 5 f59e1000 4577 specfs
f59e5994 -- 78 f59e5994 1c19 tlimod
f59e7378 -- 80 f59e7378 2d8 ipc
f59e7670 -- 7 f59e7670 2ddc TS
f59ea45c -- 8 f59ea45c 4f0 TS_DPTBL
f59ea94c -- 9 f59ea94c 27c28 ufs
f5a12574 -- 10 f5a12574 ec4c rpcmod
f5a211c0 -- 11 f5a211c0 28f84 ip
f5a4bfb8 -- 12 f5a4bfb8 ce3 rootnex
f5a4cc9c -- 13 f5a4cc9c 1ec options
f5a4ce88 -- 14 f5a4ce88 76c dma
f5a4d5f4 -- 15 f5a4d5f4 cb7 sbus
f5a4e2ac -- 16 f5a4e2ac 1ae7 iommu
f5a4fd94 -- 17 f5a4fd94 1648 sad
f5a513e8 -- 18 f5a513e8 61f pseudo
f5a51a0c -- 19 f5a51a0c 103bc sd
f5a61dc8 -- 20 f5a61dc8 7136 scsi
f5a68f18 -- 21 f5a68f18 d6f5 esp
f5a78378 -- 28 f5a78378 12926 procfs
f5a89cac -- 35 f5a89cac 45d0 udp
f5a8d27c -- 77 f5a8d27c 92a3 rpcsec
f5a93ef0 -- 87 f5a93ef0 163b ptem
f5a952dc -- 71 f5a952dc 7f6 kstat
f5a95e44 -- 32 f5a95e44 616 clone
f5a9afd4 -- 34 f5a9afd4 11a1 md5
f5a9d178 -- 86 f5a9d178 e53 pts

 
 위 결과와 다음 명령을 사용하여 kernel symbol table에 등록된 symbol에 대해서 주소 순서로 나온 결과를 비교하면,

감추어진 LKM 루트킷 모듈을 찾아낼 수 있다.
 
    nm ?x /dev/ksyms | awk ‘{print $2, $1, $3, $4, $5, $6}’ | sort

다음은 위 두 명령을 사용하여 나온 결과를 비교하여, “modinfo” 결과에는 나오지 않지만
“nm” 결과에 나오는 부분중에 의심이 되는 부분을 정리해 논 것이다. 결과에서 볼 수 있듯이,

newcreat64, newchdir, newsetuid 등 충분히 의심이 갈만한 symbol 들을 발견할 수 있다.

Data Segment
Load addr Size Type Symbol name
|0xf5af6eb0|0x00000005|OBJT |magic
|0xf5af6eb8|0x00000006|OBJT |key
|0xf5af6ec0|0x00000009|OBJT |oldcmd
|0xf5af6ed0|0x0000001d|OBJT |newcmd
|0xf5af6ef0|0x00000004|OBJT |security
|0xf5af6ef4|0x00000004|OBJT |promisc
|0xf5af6ef8|0x00000008|OBJT |modlmisc
|0xf5af6f00|0x00000014|OBJT |modlinkage

Text Segment
Load addr Size Type Symbol name
|0xf5b53db8|0x00000000|NOTY |gcc2_compiled.
|0xf5b53db8|0x0000006c|FUNC |check_process
|0xf5b53e24|0x0000003c|FUNC |check_for_process
|0xf5b53e60|0x00000054|FUNC |sitf_isdigit
|0xf5b53eb4|0x0000007c|FUNC |sitf_atoi
|0xf5b53f30|0x000000c4|FUNC |newioctl
|0xf5b53ff4|0x00000084|FUNC |newcreat64
|0xf5b54078|0x00000074|FUNC |newchdir
|0xf5b540ec|0x00000080|FUNC |newopen64
|0xf5b5416c|0x0000010c|FUNC |newgetdents64
|0xf5b54278|0x0000008c|FUNC |newexecve
|0xf5b54304|0x00000050|FUNC |newsetuid
|0xf5b54354|0x00000b5c|FUNC |_init
|0xf5b54434|0x0000001c|FUNC |_info

4. 커널 루트킷 제거

 앞서 설명한 도구나 방법을 이용하면 LKM 루트킷이 설치되어 있다는 사실만을 알 수 있게 된다.

그리고 몇몇 공격 프로세스를 찾아서 제거할 수도 있을 것이다. 하지만 공격자가사용하는 모든 파일 및 기타 다른 백도어를

찾기 위해서는 근본적으로 커널에 로딩되어 있는 LKM 루트킷을 제거해야 한다. 그래야 LKM 루트킷으로 숨겨진 다른

파일들을 일반적인분석 방법으로 찾아낼 수 있게 된다. 사실 피해 시스템 분석은 이제부터가 시작인 것이다.
 
 다음은 LKM 루트킷을 찾아서 제거하는 방법에 대해서 설명한다. 이는 최근에 “공격자들이 LKM 루트킷을 설치하여

사용하는 방법”에 대한 지식을 바탕으로 찾아 내는 것이다.
 
LKM은 시스템을 재 부팅하게 되면 사라지므로 시스템을 재 부팅시켜보는 것도 LKM 루트킷을 제거하는 방법이 될 수 있다.

시스템을 재 부팅시킨 후에 위 도구를 사용해서 또 다시 LKM 루트킷이 있는 것으로 추측되면, 이는 공격자가 시스템의

어딘가에 LKM 루트킷을로딩(실행)하도록 설정한 것이다. 대부분의 경우 공격자는 시스템의 시작 스크립트

파일중의하나에 LKM 루트킷을 설치하도록 설정한다. 따라서 /etc/rc.d 디렉토리 내의 파일들을 조사하여야 한다.
 
실예) 다음은 w0rmbreed 라는 인터넷 웜 프로그램 중에서 실제 LKM 루트킷의 하나인
adore 를 설치하는 공격 스크립트이다. 여기서는 “/etc/rc.d/rc.sysinit” 파일에 aodre LKM
루트킷을 설치하도록 설정한다.

  Install.sh 파일
 
 …
 mv adore/adore.o /lib
 echo
 if [ -f /lib/adore.o ]; then
  mv adore/ava /bin/AVA
  echo -n "Setting up rc.syswhatever: ..."
  if [ -f /etc/rc.d/rc.sysinit ]; then
  cat rc >> /etc/rc.d/rc.sysinit
  else
  if [ -f /etc/rc.d/rc.sysvinit ]; then
  cat rc >> /etc/rc.d/rc.sysvinit
  fi
  fi
 fi
  …
 
  rc 파일
 
 if [ -f /sbin/insmod ]; then
  /sbin/insmod -f /lib/adore.o >/dev/null 2>&1
 else
  if [ -f /bin/insmod ]; then
  /bin/insmod -f /lib/adore.o >/dev/null 2>&1
  fi
 fi

 

 
위 예의 경우에서는 “/etc/rc.d/rc.sysinit” 파일에서 rc 파일의 내용을 찾아내서 삭제하고 시스템을 재 부팅 시키면

LKM 루트킷이 사라지며, 이제는 일반적인 방법으로 시스템을 분석하면 된다.
 
또 다른 방법으로는 cron job 을 이용하여 LKM 루트킷을 설치할 수도 있으므로 cron 테이블 또한 모두 조사해 봐야 한다.

그리고 기타 일반적인 백도어 설치 방법을 사용하여 LKM루트킷을 설치할 수 있으므로 다음 문서를 참조하여

일반적인 피해 시스템 분석을 하면서찾아내야 한다.
 
 ※ “UNIX 피해 시스템 분석 및 침입자 모니터링 : Part I” 참조
   http://www.securitymap.net/sdm/docs/ids/Scene-of-the-Crime.pdf
 
 여기서 한 가지 지적하고 싶은 것은 현재는 공격자들이 아직 LKM 루트킷을 제대로 활용하지 못하고 있기 때문에

종종 발견되고 있다는 것이다. 좀더 시간이 흐르게 되면(어쩌면요즘에도) 아마 피해 시스템에서 공격자 흔적을

찾기가 매우 어려워 질 것이다.
 

IV. 결론

 해킹 피해 시스템을 분석해보면 LKM 루트킷이 설치된 Linux, Solaris 시스템을 많이 접하게 된다.

예전과는 다르게 이제는 소위 스크립트 키디(Script kiddies)라 불리는 일반 공격자까지도 커널 루트킷을 사용하고 있다.

그리고 위에서 설명한 탐지방법에 대해서도 우회 가능한 공격방법이 있을 수 있으며, 사실 제대로 설치된 LKM 루트킷은

탐지하기가 매우 어렵다. 또한 다양한 종류의 LKM 루트킷를 제공하기 때문에 위 방법만으로 모든 종류의 LKM 루트킷을

찾으리라는 보장도 없다.

중요한 것은 예방을 통하여 해킹을 당하지 않는 것이다. 새로 발견되는 보안 취약점에 대한 즉각적인 보안 패치,

그리고 중요 파일 시스템에 대한 무결성 검사는 시스템 보안의 가장 기본적인 것이면서 간과되는 부분이다.

또한 LKM 공격도구는 많이 공개되어 있는 반면, LKM 보안을 위한 도구가 아직 많이 나와 있지 않은 상태인데,

그 중에 하나가 StMichael_LKM 이라는 도구가 있다. 이는 리눅스 시스템에서 LKM 루트킷이 설치되지 못하도록

통제해주는 도구이다.

http://www.sourceforge.net/projects/stjude

기타 새롭게 공개되는 LKM 보안을 위한 도구는 Securitymap 사이트의 보안도구맵 메뉴/
유닉스시스템보안 부분에 계속 업데이트 할 예정이므로 자주 참고 하기 바란다.
 
본 문서는 일상적으로 관리되지 않는 시스템에서 LKM 루트킷을 어떻게 탐지하는가에 대해서 설명했는데,

이러한 관리되지 않는 시스템은 완전한 분석이 불가능하다. 하지만 평소에 시스템 보안 관리를 한다면 해킹을 당했다

하더라도 시스템 재 설치를 하지 않고도 적절히 복구할 수 있을 것이다. “보안은 침입을 당한다 또는 당하지 않는다

보다는 침입(위협)을 감내할 수준에서 관리하는 것을 말한다.”
 
지금 이 순간에도 공격자는 수많은 시스템에 설치된 커널 루트킷을 통해 당신의 눈에 보이지 않게 당신의 시스템을

사용하고 있을 지도 모른다.

[Reference]

http://s0ftpj.org/en/site.html
http://www.securitymap.net/
http://www.pimmel.com/articles/bsdkern.html
http://www.pimmel.com/articles/slkm-1.0.html
http://www.pimmel.com/articles/lkm-hacking.html
http://www.certcc.or.kr/paper/incident_note/in2000004.html
http://www.sans.org/newlook/resources/IDFAQ/knark.htm
http://howto.tucows.com/LDP/LDP/lkmpg/

[한글 참고 사이트]

http://kldp.org/리눅스_커널/
http://kldp.org/개발자(developer)_코너/커널_프로그래밍/
2002 By SecurityMap, www.securitymap.net

관련 링크: http://www.securitymap.net/

'Security' 카테고리의 다른 글

Linux.Slapper 웜  (7) 2002.09.15
sendmail 도움말 숨기기(?)  (0) 2002.09.02
트로이목마를 내포한 OpenSSH패키지 배포  (0) 2002.08.03
OpenSSL 다중 취약점  (0) 2002.08.02
원격침입과 도스공격이 가능한 PHP 취약점  (0) 2002.07.23

+ Recent posts