Hacking & Security/System2008. 11. 25. 15:05

[memcpy 를 사용할 때 Buffer Overflow 를 막기 위한 방법]


1. Buffer Overflow?
버퍼 오버플로우란 사용자가 할당받은 버퍼의 크기보다 더 많은 크기의 데이터를 복사하게 되어, 다른 메모리 영역에 잘못된 값을 쓰게 되어 문제를 발생시키는 것이다.
단지 데이터가 손실되거나 파괴되는것이 아니라, 이 기법을 사용하면 프로그램의 실행 순서를 바꿀 수 있기 때문에, 굉장히 큰 문제를 초래할 수 있는 버그이다.
Buffer Overflow 는 매우 잘 알려진 보안 취약점 중 하나로, 이에 대한 자세한 내용은 서적, 인터넷 등을 찾아보면 쉽게 이해할 수 있다.

본 글에서는 이러한 내용까지 다룰 수 없기 때문에, Buffer Overflow에 대한 내용은 다른 참고자료들을 찾아보길 바란다.


2. memcpy?
memcpy 는 메모리를 복사하는 C 함수이다. 많은 사람들이 이 함수를 사용하게 되는데, 이 함수는 다음과 같이 사용한다.

memcpy(data, initdata, size);

즉, initdata 를 data 로 size 만큼 복사하라는 것이다.
좀 더 자세하기 memcpy 를 조사해 보자. 먼저, memcpy 의 정의는 다음과 같다.

void * __cdecl memcpy(void *, const void *, size_t);

모두 기본 자료형을 쓰고 있지만, size_t 는 기본 자료형이 아니다. 그렇다면 size_t 는 어떻게 정의되어 있을까?

typedef unsigned int size_t;

이와 같이 unsigned int 로 정의되어 있다.


3. Buffer Overflow in memcpy
이런 memcpy 에서 어떻게 문제가 발생할까?
문제는 다음과 같은 경우 발생한다.


int nSize = 0;
int nLimit = 100;
char szData[100];
char szInitData[100];

//
//
// nSize 를 입력받거나 대입하는 부분
//
//
if(nSize < nLimit)
 {
    memcpy(szData, szInitData, nSize);
}

일반적으로 위와 같이 nSize 가 nLimit 을 넘지 않는 지만 확인한다.
하지만, nSize 가 음수인 경우에는 어떻게 될까?
nSize 와 nLimit 는 모두 signed 형 이므로, nSize &lt; nLimit 은 true 가 되어 memcpy 로 넘어간다.
그렇다면 memcpy 에서는 어떻게 될까?
memcpy 에서는 size_t 로 받게 되고, 이것은 unsigned 형 이므로, 2147483648 보다 큰 수가 된다.

즉, 의도하지 않은 크기의 메모리를 더 복사하게 되는 것이다.


4. 해결 방법
이 문제를 피해가기 위한 방법은 여러가지가 있지만, 여기서는 3가지를 소개한다.

4.1. memcpy 의 Wrapper Function
다음과 같이 Wrapper Function 을 만들어서 사용한다.

BOOL safe_memcpy(void* data, void* source, int size)
{
      if(size < 0) return FALSE;

      memcpy(data, source, (size_t)size);
}

4.2. 확인 제어문 강화
if(nSize &lt; nLimit) 만으로 검사하지 않고 다음과 같이 음수도 확인한다.

if(nSize > 0 && nSize < nLimit)

또한, 제어문에서 unsigned 값으로 변환하여 확인한다

if((unsigned int)nSize < (unsigned int)nLimit)

4.3. unsigned 형으로 변환하여 사용
nSize, nLimit 값을 항상 unsigned 값으로 사용한다.


[요약]
memcpy 를 사용할 때에는 항상 size 값이 unsigned 임을 유의하고, 음수 처리의 특성상 signed 값이 unsigned 값에 대입되어 기대하지 않은 효과를 낼 수 있다는 것에 유의하여야 한다.

Posted by skensita
Hacking & Security/System2008. 11. 25. 14:57
[%n 포맷 토큰]

1. 포맷 토큰이란?
printf, sprintf 을 사용하게 되면, %d 등의 포맷 토큰을 사용하게 된다.
이 포맷토큰은 실질적으로 문자열로 변환되었을 때 얼마나 많은 양을 차지할지 가늠하기가 쉽지 않은데, 그렇다고 해서 항상 크기를 계산하여 작성하는 사람들은 그리 많지 않다.
이 포맷 토큰 자체가 편의성을 위한 것이기 때문이다.

대부분 다음과 같이 많이 사용하게 되는데,

char szMsg[256];
sprintf(szMsg, \"%x %d\", nData1, nData2);

만약, %x, %d 등이 변환되는 길이가 매우 커서 256 크기를 넘어선다면, 이는 심각한 오류를 초래할 수 있다.

2. %n 포맷 토큰
%n 포맷토큰은 출력된 문자열의 길이를 알려주는 기능을 하며 상당히 유용하게 사용할 수 있다.
사용 방법은 다음과 같다.

int nData;
printf(\"%n\", &nData);

이렇게 사용하면 nData 에, printf 에서 출력한 문자의 길이를 얻을 수 있다.

다음은 간단한 사용 예 이다.

int nRetSize = 0;
printf(\"Your String is %X %d %s %n\\n\", 1024, 1024, \"Test String\", &nRetSize);
printf(\"Printed bytes: %d\\n\", nRetSize);

[요약]
%n 연산자를 사용하여 출력된 문구의 길이를 알아낼 수 있다. 이러한 정보들은 디버깅시 좋은 정보가 될 수 있으며, buffer overflow 에 대한 예방과 조취에 유용하게 사용될 수 있다.
Posted by skensita
Hacking & Security/System2008. 11. 25. 14:56
[현재 실행되고 있는 메모리 주소를 알아오기]

1. 레지스터
현재 실행되고 있는 메모리 주소는 eip 레지스터에 저장되어 있다. 이 주소를 얻기 위해서는 여러가지 방법이 있는데, 여기서 소개할 내용은 단순히 어셈블리만으로 얻어오는 방법이다.
eip 레지스터의 내용을 얻어오기 위해서 가장 먼저 생각해 볼 수 있는 것은 mov 연산자를 사용하는 것이다. 하지만, mov 연산자 등으로는 eip 에 접근할 수 엇기 때문에, 다른 편법을 사용하여야 한다.

2. call instruction
call 명령은 함수를 호출하기 위한 명령이다. call 은 jmp 와 다르게, 실행 된 후 ret 명령을 만나면 호출된 주소로 되돌아와야 한다. 이 되돌아오기 위한 주소를 저장하는 곳은 바로 스택인데, call 을 하게 되면, 자기자신의 주소를 stack 에 push 하는 효과를 낳는다.
이 특징을 가지고 eip 의 주소를 알아낼 수 있다.

3. 간단한 구현
call GETEIP
GETEIP:
pop eax

이렇게 구현 하였을 경우, eax 에 eip 갓이 들어가게 된다. 하지만 얻은 값은 pop eax 의 위치가 되므로, 실제로 pop eax 뒤의 주소를 알아내어 무언가를 쓰려고 한다면, 디스어셈블하여 pop eax 가 얼만큼을 차지하는 지 알아내야 한다.
위의 것을 디스어셈블하면 다음과 같다.

E8 00 00 00 00 5F

여기서, E8 00 00 00 00 은 call 명령이며, 5F 가 pop 명령이다. 즉, 구해진 eax + 1 이 우리가 원하는 현재 명령 다음에 실행될 주소를 가지고 있는 것이다.
여기서 +1 은 매우 중요한데, 이것이 이 코드가 꼭 하드코딩되어야 하며 프로세스 아키텍쳐가 변경될 경우(pop 명령의 길이) 코드를 변경하여야 하는 단점이 있다.
이러한 제약사항을 효과적으로 제거한 것이 다음의 구현이다.

4. 좀 더 좋은 구현

BEGIN:
jmp GETEIP3
GETEIP2:
pop eax
jmp GETEIPEND
GETEIP3:
call GETEIP2
GETEIPEND:

이렇게 하였을 경우 GETEIPEND 의 주소를 GETEIP2: 에서 구할 수 있다. eax 값을 그대로 사용하면 된다. (디스어셈블 하여 직접 확인한 후 하드 코딩 할 필요가 없다)


[요약]
eip 의 값을 얻어내는 방식을 알아보았다. 이 방법을 사용하여 실행도중 자기자신을 변경하거나 하는 일들을 할 수 있게 된다. 이 방법으로 eip 를 얻어내는 것은 x86 기반의 cpu 에서만 가능하며, stack 을 꼭 사용하여야 한다.

Posted by skensita
Hacking & Security/System2008. 11. 19. 10:06
Posted by skensita
Hacking & Security/System2008. 11. 19. 10:03
Posted by skensita
Hacking & Security/System2008. 11. 18. 17:58
사용자 삽입 이미지

2007/05/02(수)
전략마케팅실 기술기획 / 대리 이성열

지난 강좌에서 Buffer Overflows 공격의 원리에 대해서 간단한 샘플코드를 이용하여 살펴보았다. 이번 강좌에서는 주어진 크기의 데이터 공간에 위치하지 못한 데이터가 메모리에 쓰여져 시스템의 임의의 명령을 실행시키도록 하는 코드 즉, 쉘코드(shellcode)를 작성하는 방법을 살펴보도록 한다.

Shellcode란?
Shellcode란 시스템의 특정 명령을 실행하도록 하는 기계어 코드로 기본적으로 어셈블리(Assembly) 언어로 작성된 프로그램을 이진 덤프(binary dump)함으로서 얻을 수 있으며, Buffer Overflow 공격에서 shellcode는 시스템에서 할당한 기억공간(Buffer)보다 큰 데티어로 메모리의 특정 위치에 저장되어 공격자에 의도하는 결과를 이끌어낸다.

Shellcode와 레지스터(Register), 어셈블리(Assembly)
Buffer Overflow 공격 시 공격자가 취약한 시스템으로 전송한 shellcode는 CPU의 레지스터 영역에 쓰여지게(Overwrite) 된다.
시스템의 레지스터는  여러 가지가 있으나 Shellcode는 이중 EAX, EBX, ECX, EDX 레지스터 영역에 쓰여져 시스템의 정상적인 실행 명령대신 shellcode가 의미하는 명령을 실행하게 한다. 이렇게 시스템의 레지스터에 실행명령을 overwrite되는 shellcode는 사람이 이해할 수 없는 기계어로 작성되는데 이를 위해서 Assembly 언어로 작성된 프로그램을 이진 덤프(Binary dump)하여 얻을 수 있다.

여기서 간단하게 Assembly 언어를 이용하여 임의의 명령을 실행하는 방법을 알아보자면, 임의의 명령어가 실행하기 전에 명령어(정확하게 표현하자면 '시스템 호출')를 식별할 수 있는 코드가 eax 레지스터에 기억되고 나머지 abx, ecs, edx 레지스터에는 시스템 호출 시 넘겨지는 매개변수 값이 저장된다.
eax ~ edx 레지스터에 시스템 호출에 필요한 값들을 저장하였으면, 그 다음으로는 0x80 인터럽트를 발생시켜 cpu가 레지스터에 저장된 값들을 가지고 시스템 호출을 실행한다.


Shellcode 예제 1 - exitshell
Shellcode 작성의 첫번째 예제는 프로그램을 종료하는 exit() 함수를 shellcode로 작성하여 shellcode 작성법을 살펴보도록 본다.

Shellcode도 다른 일반 프로그램과 마찬가지로 함수들의 조합으로 작성되는 프로그램으로 함수를 호출하기 위해서는 아래의 두 가지 사항을 알아야 한다.


● 호출하려는 함수의 시스템 호출 방법
   √  Unix Like : 시스템 호출 번호
   √  Windows : 시스템 호출의 메모리 주소
● 시스템 호출의 매개변수 값

먼저 시스템 호출 방법은 Unix 시스템과 Windows 시스템과 차이가 있는데, Linux 등과 같은 Unix like 시스템 호출을 정의한 헤더파일에서 시스템 호출번호를 이용하여, Windows 계열의 운영체제에서는 kernel23.dll을 별도의 도구를 이용하여 호출하려는 시스템의 주소를 이용한다.

사용자 삽입 이미지

 [그림 1 : /usr/include/asm/unistd.h에 정의된 시스템 호출]


사용자 삽입 이미지

 [그림 2 : dumpbin 프로그램을 이용한 kernel32.dll의 메모리 정보]


사용자 삽입 이미지

[그림 3 : dumpbin 프로그램을 이용한 kernel32.dll내 시스템 호출 메모리 정보]

첫 번째 예제로 작성할 exitshell을 Linux 시스템에서 C언어로 작성한다면 다음과 같은 함수가 된다.

사용자 삽입 이미지

위 C언어로 작성된 프로그램과 동일하게 실행하는 프로그램을 어셈블리 언어로 작성하기 위해서는 아래의 사항에 대한 확인이 필요하다.


● exit() 함수의 시스템 호출번호
● exit() 함수 호출시 매개변변수 값

위 두 가지 중 exit() 함수에 대한 호출번호는 /usr/include/asm/unistd.h 파일에서 확인하면 '1'로 정의되어 있으며, 매개변수 값은 오류 없이 실행이 종료되었음을 의미하도록 '0'으로 설정한다.


이렇게 확인한 정보들을 이제는 레지스터에 저장하는데 각 사항을 저장하는 위치는 아래와 같다.


● eax 레지스터 : exit() 함수의 시스템 호출번호
● ebx 레지스터 : exit() 함수 호출시 매개변변수 값

이제까지의 내용을 어셈블리 언어로 작성하여 완성된 코드는 아래와 같다.

사용자 삽입 이미지

 exitshell.s

● Line 03 ~ 04: eax 레지스터에 exit() 시스템 호출 번호 '1' 저장
● Line 05 ~ 06 : ebx exit() 시스템 호출 매개변수 값 '0' 저장
● Line 07 : exit(0) 실행을 위한 인터럽트 발생


작성된 코드를 컴파일 및 링크시키고 정상적인 실행을 확인한 후 생성된 실행 파일을 덤프하면 최종적인 shellcode를 얻을 수 있다.

사용자 삽입 이미지

사용자 삽입 이미지

 
[그림 4 : exitshell.s의 실행 및 이진덤프를 통한 shellcode 작성]

이번 강좌에서는 간단한 함수 호출을 통해서 어셈블리 언어 프로그래밍과 이를 통하여 shellcode를 생성하는 단계까지 소개하였다.

다음 강좌에서는 shellcode 작성 시 유의하여 할 사항 및 shellcode 작성에 대해서 좀더 구체적으로 살펴보는 시간을 갖도록 하자.

- 끝 -

 
출처 : < AHNLAB coconut >
Posted by skensita
Hacking & Security/System2008. 11. 18. 17:51
사용자 삽입 이미지

2007/04/02(월)
전략마케팅실 기술기획 / 대리 이성열

'Buffer Overflow 공격'.. 보안에 대해서 관심있는 사람뿐 아니라 시스템/네트웤, 개발자에 이르기까지 한 번 쯤은 들어봤을 만한 용어이다. 적어도 IT분야에서는 널리 알려진 이 용어에 대해서 인터넷, 보안서적 등을 살펴보면 "지정된 크기의 저장공간(Buffer) 보다 넘치게(overflow) 입력되는 Overflow 현상을 이용하여 Buffer에 입력되지 못한 조작된 데이터를 시스템의 특정 위치에 기록하여 공격하는 기법" 이라는 간략한 설명으로 시작되는 Buffer Overflow에 대해서 그 원리에 대해서 충분히 이해하기란 쉽지않아 이번 Lecture에서는 Overflow 공격의 기본 원리를 Buffer Overflow 공격기법 중 하나인 Stack Overflow를 중심으로 정리한다.


기본 메모리 구조 - Stack, Heap, Code
Overflow 공격을 설명하기에 앞서서 우선은 시스템의 메모리 구조에 대한 이해가 필요하다. 일반적인 시스템에서 메모리는 아래의 그림처럼 3가지로 구분되는데 메모리의 상세구조에 대해서 설명하기 보다는 우선 Stack Overflow 공격과 관련된 Stack 영역과, Code 영역에 대한 이해를 돕기로 한다.

사용자 삽입 이미지

[그림 1] 일반적인 메모리 구조


STACK 영역

Stack 영역은 프로그램안에 함수에 의해서 선언된 변수에 할당되는 영역으로 시스템이 특정 함수를 실행하는 경우 함수내에 선언된 변수와 이들 값을 저장하기 위해서 할당되었다가, 함수의 실행이 종료되면서 해제되는 영역이다.
프로그램의 함수가 실행되면 Stack 영역에 Stack Frame 이라는 영역이 함수에 할당되는데 이 Stack Frame에 해당 함수가 실행하는데 필요한 정보들이 저장되어있다.

사용자 삽입 이미지

[ Stack & Stack Frame ]


CODE 영역
Code 영역은 프로그램이 실질적으로 실행될 명령어가 저장되는 공간으로 기계어로 프로그램(또는 함수)의 실행코드가 기계어 형태로 저장되어있다.



Buffer Overflow에 취약한 프로그램?
Buffer Overflow에 취약한 프로그램에 대해서 설명하기 전에 우선은 아래의 샘플코드를 살펴보자.
먼저 6라인의 char var[10]; 부분에서는 var라는 변수에 최대 10Byte의 자료가 저장될 수 잇는 공간을 할당하였다. 다음 7라인을 보면 strcpy()함수를 실행하는데 6라인에서 선안한 10Byte 크기의 var 변수공간에 32byte 크기의 문자열을 저장하고 있어 이 샘플코드를 컴파일하고 실행하면 Buffer Overflow 현상이 발생하여 프로그램이 정상적으로 실행하지 못할 것을 예상할 수 있다.

사용자 삽입 이미지

위 샘플코드를 컴파일하고 실행하고 로그를 확인한 결과를 다음에 보여주는데 프로그램이 실행한 직후 프로그램상에서 오류가 발생하였으며, 이벤트 로그를 확인한 결과 주소 0x45454545 부분에서 문제가 발생했음을 알 수 있다.

사용자 삽입 이미지

[ 샘플코드 실행결과 ]

 

사용자 삽입 이미지

[ 로그 확인 결과 ]


Buffer Overflow 현상과 Stack Frame
이제 지정한 크기의 저장공간에 큰 값을 입력하였을 경우 시스템 내부적으로 어떻게 동작하여 Overflow가 발생하면서 오류가 발생하는지 살펴보자.

최초 샘플프로그램이 실행되면서 Stack 영역에 하나의 Stack Frame에 할당되며, 이 Stack Frame에 'var' 변수를 위한 공간이 할당된다.

그 다음으로 scrcpy()함수가 실행되면서 'var'변수에 문자열 'AAAABBBBCCCCDDDDEEEEFFFF GGGGHHHH' 문자열이 저장되는데 문제는 12byte 크기의 저장소에 32byte 크기의 문자열이 저장되면서 'var'에 할당된 12byte 까지는(주1) 정상적으로 저장될 것이다.

※ 주1 : 샘플 프로그램에서 'var'에 할당한 메모리 크기는 10byte 이지만 테스트한 시스템은 32bit 시스템으로 실제 'var' 변수에는 12byte의 공간이 할당되며, 문자열도 12byte 크기가 저장된다.)

그렇다면 'var'에 저장되고 남은 문자열은 어디에 있는지 살펴보면, 운영체제가 var에 할당해준 12byte 공간에 이어서 문자열이 저장되는 것을 메모리 맵을 통해서 확인할 수 있다.

사용자 삽입 이미지

[ 문자열 저장확인 ]

이제 주어진 문자열이 지정한 공간의 넘어서 메모리에 쓰여졌을을 확인하였는데, 이렇게 불필요하게 쓰여진 데이터가 프로그램에 미치는 영향을 확인해야 한다.

Strack Frame의 구조를 다시 살펴보면 Stack Frame의 argument영억에 이어서 Instruction 영역이 존재하는데 Instruction 영역은 함수가 종료한 후에 실행될 명령어가 저장된 주소값을 가지고 있다.
따라서 위 그림에서 '45 45 45 45' 문자열에 위치한 곳은 strcpy()함수가 실행되고 다음 실행할 명령어가 있는 위치한 주소값을 저장한 곳이다.

그러나 잘못된 프로그래밍에 의해서 strcpy() 다음 명령어가 저장된 주소값이 지워져 버린것이다.
이를 확인하지 위해서 메모리 주소 '45 45 45 45 '를 확인한 메모리 맵이 아래 그림이다.

사용자 삽입 이미지


메모리 상의 '45 45 45 45' 위치를 확인한 결과 '?' 표기로 되어있어 아무런 기계어 코드가 없는 것을 확인할 수 있으며, 프로그램은 strcpy()를 실행한 후에 '45 45 45 45' 위치의 '????' 명령어를 실행하던 중 오류가 발생한 것이며 프로그램의 각 실행코드와 메모리 동작을 나타내면 아래 그림으로 표현 할 수 있다.
 

사용자 삽입 이미지


지금까지 살펴본 내용은 Buffer Overflow 현상이 발생하는 과정을 메모리 특히 Stack 영역에 국한하여 살펴보았으며, 다음 강좌에서는 Overflow 취약점을 이용하여 임의의 명령어를 실행하는 방법을 설명하면서 메모리 동작을 더욱 자세하게 살펴보겠다.



출처 : http://www.coconut.co.kr/board/seculetter.php?id=seculetter
Posted by skensita
Hacking & Security/System2008. 11. 14. 11:18
Posted by skensita
Hacking & Security/System2008. 11. 14. 11:08

[source : http://blog.naver.com/ex______/50029410228 ]

커널단에서 프로세스를 보호하는 방법에 대해서 요약 작성 및 정리한 자료입니다.

 프로세스를 보호하는 드라이버를 거의 완벽에 가까울 정도로(?) 코딩하는 방법은 없을 지 고찰한 결과 아래와 같은 결론에 도달하게 되었습니다.

 목표 프로세스를 보호하는 방법

- 유저 모드 후킹

일단 기본적으로 프로세스를 조작하는 API인 NTDLL.DLL의 NtOpenProcess, NtTerminateProcess, NtReadVirtualMemory, NtWriteVirtualMemory, KERNEL32.DLL의 DebugActiveProcess 등이 후킹되어야 할 것입니다.
그리고 WINSTA.DLL의 WinStationTerminateProcess를 후킹하여 Terminal Service를 이용한 프로세스 종료를 차단합니다.

- 커널 후킹

ZwOpenProcess/ObReferenceObjectByPointer/PsLookupProcessByProcessId
ZwOpenProcess는 ObReferenceObjectByPointer를 호출하고 이는 다시 PsLookupProcessByProcessId를 호출하는데, 이들을 후킹하면 프로세스를 여는걸 어느정도 방지할 수 있을겁니다.

 KeAttachProcess / KeStackAttachProcess

일단 프로세스의 메모리를 읽고 쓸땐 이 API를 사용하여 Page를 대상 프로세스의 Page로 전환해야하는데, 이를 방지하는 역할을 합니다.

ZwReadVirtualMemory/ZwWriteVirtualMemory/ZwTerminateProcess

프로세스의 메모리를 보호하며, 프로세스를 종료로부터 보호합니다.

ZwOpenThread/ZwGetContextThread/ZwSetContextThread/ZwTerminateThread

쓰레드를 보호합니다. 또한 쓰레드의 종료를 차단합니다.

 ZwCreateFile (IoCreateFile)

이 것을 후킹하여 드라이버 혹은 프로세스 파일에 접근하는걸 차단할 수 있습니다.

경우에 따라선 ntoskrnl.exe를 접근하는 것을 막아 후킹을 해제하는걸 막아줄 수 있습니다.

 ZwQuerySystemInformation

이 것을 후킹하면 프로세스를 숨길 수 있습니다. 밑에서 소개하는 DKOM보다는 안정적입니다.

 
- Internal 함수 후킹

 PspTerminateThreadByPointer:

프로세스 종료의 가장 근본적인 API로 쓰레드를 종료시키는 가장 저수준의 API입니다. 따라서 이 함수를 후킹해놓으면 프로세스를 함부로 종료할 수 없습니다. 이 API는 NtTerminateThread()에서 ObfDereferenceObject()전에 호출되므로 코드바이트로부터 이를 추적해나갈 수 있습니다.

 KiAttachProcess:

KeAttachProcess/KeStackAttachProcess의 Internal 함수로 후킹할 수 있으면 해놓는게 좋습니다. KeAttachProcess()에서 호출하는데, 이것이 이미 후킹되어있었을 수도 있으므로, 파일로부터 구해와야 합니다. 이 방법은 제가 블로그에 쓴 적 있습니다.

참고: [Unexported Symbol] KiAttachProcess() 주소값 구하기.

 
- DKOM

 적절한 DKOM은 강력한 무기가 될 수 있습니다. 아래와 같은 방법을 사용함으로써 프로세스를 효과적으로 보호할 수 있습니다.

 1. DebugPort = NULL

디버거를 무력화하여 예외 처리를 프로세스에게 넘기는 방법으로, 디버거를 무력화시키는 방법중 하나입니다. 이 값이 NULL인지 아닌지 체크하는 방법도 비교적 괜찮은 방법이나, NT4/2000계열에서는 간혹 쓰레기값이 들어있는 경우가 있기 때문에 권장하지 않습니다.

 EPROCESS에서의 DebugPort의 Offset 구하는 방법은 대략 아래와 같이 코딩할 수 있습니다.

 VOID CalcHandleTableOffset(VOID)
{
 PEPROCESS Proc = IoGetCurrentProcess();
 HANDLE PID = PsGetCurrentProcessId();
 PVOID GuessHandleTable = NULL;
 int i = 0;
 for(i = 0; i < PAGE_SIZE; i+=4)
 {
  GuessHandleTable = *(PVOID *)((PCHAR)Proc + i);
  if(MmIsAddressValid(GuessHandleTable))
  {
   if(*(PHANDLE)((PCHAR)GuessHandleTable + 0x08) == PID)
   {
    HANDLE_TABLE_OFFSET = i;
    return;
   }
  }
 }
}

 CalcHandleTableOffset();

DEBUG_PORT_OFFSET = HANDLE_TABLE_OFFSET - 8;

 2. EPROCESS->ActiveProcessLinks Unlinking

프로세스를 숨겨서 보호합니다. 추가적으로 EPROCESS의 UniqueProcessId를 이상한 값으로 바꾸면 핸들을 얻는것을 어느정도 막을 수 있습니다. (물론 관련 ETHREAD 구조체에서 PID를 얻을수도 있기 때문에 이 방법이 꼭 좋은건 아닙니다.)

만약 프로세스 ID를 바꾸었다면 프로세스 종료 전에 복구하여야합니다.(그렇지 않으면 CID_HANDLE_DELETION 블루스크린을 맛보시게 될겁니다.)

그리고, 추가적으로 PspCidTable이라는 Unexported symbol에서 프로세스 Offset을 NULL로 만들어놓으면, 근본적으로 핸들을 얻을 수가 없을겁니다. PspCidTable은 PsLookupProcessByProcessId()에서 ExMapHandleToPointer(Ex)를 호출하기 전에 참조하는 심볼로, 바이트 코드를 트래버싱하거나, XP 이상에서는 KPCR->KdVersionBlock의 PspCidTable 필드를 참조해서 구할 수도 있습니다.

 3. EPROCESS::HandleTable Unlinking

숨겨진 프로세스를 찾는 대표적인 방법으로는 크게 두가지가 있습니다. EPROCESS::HandleTable를 traverse 하거나 PspCidTable을 트레버싱하는 방법입니다. 물론, ETHREAD를 이용하거나 기타 여러가지 방법(brute-force 등등)을 이용하면 뚫리긴 합니다만, HandleTable을 끊음으로서 더욱 찾기 힘들게 할 수 있습니다.

 위에서 언급한 ActiveProcessLinks와 핸들 테이블은 아래와 같은 LIST_ENTRY 구조입니다.

typedef struct _LIST_ENTRY {
    PLIST_ENTRY Flink;
    PLIST_ENTRY Blink;
} LIST_ENTRY, *PLIST_ENTRY;

 
드라이버를 효과적으로 숨기는 방법

 1. PsLoadedModuleList unlinking

PsLoadedModuleList는 DRIVER_OBJECT 구조체의 DriverSection에 의해 Point됩니다. 그리고 아래와 같은 구조를 가지고 있습니다.(출처: rootkit.com 책)

typedef struct _MODULE_ENTRY
{
 LIST_ENTRY ModuleListEntry;
 DWORD Unknown1[4];
 DWORD BaseAddress;
 DWORD DriverStart;
 DWORD Unknown2;
 UNICODE_STRING Driver_Path;
 UNICODE_STRING Driver_Name;
} MODULE_ENTRY, *PMODULE_ENTRY;

 
ModuleListEntry를 순회해서 BaseAddress가 DriverObject->DriverStart와 같은 엔트리를 찾아내서 ModuleListEntry를 끊어주면 숨겨줄 수 있을겁니다.

 2. IoGetDeviceObjectPointer Hook

심볼릭 링크로부터 DEVICE_OBJECT를 얻는 대표적인 방법으로 이를 후킹하면 DEVICE_OBJECT를 얻는걸 막을 수 있고, 결과적으로 드라이버를 2차적인 루트킷 스캐너로부터 숨길 수 있게됩니다. DEVICE_OBJECT는 DRIVER_OBJECT를 포인트하기 때문에, 필수적으로 후킹해야합니다.

 3. ZwOpenKey() Hook

드라이버 서비스 키를 traversing 하여 드라이버 정보를 얻는것을 차단할 수 있습니다.

 4. IoCreateFile() Hook

드라이버 파일을 접근하지 못하게 함으로써 드라이버에 대한 접근을 차단합니다. ZwCreateFile()은 IoCreateFile을 직접 호출하므로, IoCreateFile()을 후킹하면 탐지되기가 더 어려우며, 그만큼 효과를 발휘하게 됩니다.

 5. ** 잡소리 **

보호된 프로세스로부터 드라이버 언로드 메시지가 왔을 때만 드라이버 언로드 핸들러를 설정합니다. 이는, 제 3자가 드라이버를 그냥 언로드하는걸 막을 수 있습니다. 

그리고 DeviceIoControl 메시지는 보호 대상 혹은 클라이언트의 메시지만 신뢰하는 것이 가장 좋습니다. (즉 말하자면 나머지는 쌩~ 까버리자는거죠.)

끝으로..
저는 위 방법들이 꼭 최선 해결책이라고 생각하진 않으며..
더 좋은 방어법이 있거나, 혹은 이를 우회하는 공격법이 존재할 수 있습니다.
그러므로, 자신만의 공격법이나 방어법을 만드는 것이 가장 좋은 방법이라고
생각이 됩니다.

Posted by skensita
Hacking & Security/System2008. 11. 14. 11:04
국내의 키보드 보안 업체는 여러곳 있으며, 현재 Cross-Hacking 과 과다경쟁에 의해서 스펙상의 보안수준은 다들 비슷하며 기본적으로 프로그램이 보호하는 구간에서의 해킹은 거의(!) 불가능 할 정도로 보안 수준은 향상되었습니다. 키보드 보안 프로그램 들은 키보드장치 (H/W) 에서 키보드 포트나 USB 포트로 입력되는 사용자의 입력값을 안전하게 특정 어플리케이션(IE 등) 에 전달하는 역할을 합니다.
즉, PC 단에서 발생하는 모든 '개인정보' 유출에 관해서 모든 이슈를 키보드 보안 프로그램으로 방어할 수 있는 것은 아니라는 말이죠. 예를들면, 멕시코 등 남미쪽에서 2006년 이후 극성이었던 overlap 형 spyware 나 iframe injection 에 의한 form 삽입등으로 사용자 정보를 빼내가는 행위를 단지 키보드 보안 프로그램이 설치되었다고 해서 막을 수는 없는 것입니다.

그러나 대부분의 키보드 보안 제품들이 금융감독원의 지침에 의해서 금융권에 적용되다 보니 보수적인 기업 문화와 기술에 대한 이해 부족으로 어려운 점도 참 많았습니다.
키보드 보안 프로그램들이 OS 커널레벨에서 다른 상용프로그램에 비해 더 깊숙한 곳을 컨트롤 하는 제품이다 보니 여러가지 Hardware 적 software 적 충돌도 많은데, 적용되는 곳이 '돈' 과 밀접한 곳이다 보니 장애가 생기면 무척이나 난감한 상황이 발생하게 됩니다.

생각하면 생각할 수록 별의별 일들이 많았네요.. 끝이 없을 것 같아 이만 사설은 접어야 겠습니다. ^^

* 키보드 타입

키보드 보안 제품은 말 그대로 키보드에서 입력되는 데이타를 보호하는 것입니다.
여기서 구분에 사용되는 키보드 타입이란 키보드 데이타가 전달되는 포트에 따른 구분입니다.
USB 타입과 PS/2 타입, 무선키보드로 나눌수 있습니다.
또 무선키보드는 통신방식에 따라 블루투스와
IrDA, RF 등으로 구분할 수 있습니다.
화상키보드는 뭘까요? 화상키보드는 소프트웨어 어플리케이션 키보드 이므로 여기서는 논외로 합니다.
키보드 보안 프로그램은 각각의 타입에 따라서 보호하는 드라이버를 별도로 개발해야 합니다.
따라서 PS/2, USB, 무선키보드 드라이버를 개발하는 것이 키보드 업체들이 서비스 해야 할 영역이지요.
보통 PS/2, USB 는 표준에 명확하므로 대부분의 업체들은 이들 키보드사용을 지원합니다.
무선키보드의 경우에는 USB 동글이를 사용하는 경우 USB 포트로 데이타로 변환되어 OS 로 전달되므로
이러한 경우 USB 키보드로 간주됩니다.
그러나 적외선키보드나 블루투스 키보드 등의 경우는 별도의 포트를 사용하므로 별도의 드라이버를 지원해야 합니다. 요즘은 블루투스 키보드가 많이 사용되고 있어서 이를 지원하는 업체들도 늘어나고 있습니다.
본 포스트에서는 PS/2, USB 형 키보드 보안에 대해서만 기술합니다.


* 키보드 입력 처리 절차 취약점

Kernel Level

Port Monitoring (PS/2 타입 키보드)
사용자의 키 입력 후 키보드 Port 에 쓰여진 입력 값은 일정 시간 메인보드의 키보드 Port 에 남아있게 됩니다. 악성프로그램이 메인보드의 키보드 Port 에 남아있는 키보드 데이터를 읽어갈 수 있으며, 이를 Port Scanning/Monitoring 이라 합니다.

IDT Hooking
(PS/2 타입 키보드)
IDT (Interrupt Descriptor Table) 은 인터럽트를 처리하는 루틴인 ISR (Interrupt Service Routine) 의 주소를 저장하고 있으며, 악성 프로그램은 사용자의 키보드 인터럽트가 발생했을 때 입력을 가로채기 위해 IDT 에 저장된 ISR 의 주소를 자신의 ISR 의 주소로 변경할 수 있습니다.

USB Bus Hooking (USB 타입 키보드)
USB Driver Stack 상에 존재하여 사용자 키보드 입력 값을 모니터링 할 수 있습니다.

Filter(Client) Driver Hooking/Filtering (PS/2, USB 타입 키보드)
Windows OS 커널은 키보드 입력 데이터를 제어하기 위해 Filter Driver (Client Driver) 의 형태로 사용자의 키 입력을 얻을 수 있도록 지원합니다. 악성프로그램은 일반적인 키보드 Filter Driver Client Driver 의 하단으로 설치되어 사용자의 입력값을 획득 할 수 있습니다.

User Level

Message Hooking

악성 프로그램이 윈도우에서 사용하는 메시지 큐 (System Message Queue, Thread Message Queue) hooking 하여 사용자의 키 입력 값을 획득 할 수 있습니다.

 
SubClassing

각 윈도우 어플리케이션은 메시지 처리를 위해서 Windows Procedure 를 가지고 있다. 악성 프로그램은 이 Windows Procedure 를 재 설정 하는 방법으로 사용자의 키보드 입력 값을 획득 할 수 있습니다.


BHO Hooking

BHO (Browser Helper Object) 는 브라우저 구동 시 동시에 로딩되어 브라우저에서 일어나는 일을 감시할 수 있도록 IE 가 지원하는 기능을 사용하는 모듈이며, 악성 프로그램은 BHO 의 형태로 IE 에서 사용자의 키보드 입력 값을 획득 할 수 있습니다.


API Hooking
실행중인 프로세스에 Injection 한 후 키보드 입력 처리에 관련된 windows API, , DrawText, DrawTextEx, ExtTextOut 등의 함수를 Hooking 하여 입/출력 파라미터를 모니터링 하는 방식으로 사용자의 키보드 입력 값을 획득 할 수 있습니다.

Memory Scan

실행중인 프로세스의 메모리를 Scan 하여 메모리 상에 저장된 사용자의 키보드 입력 값을 획득 할 수 있습니다.

Screen Capture
사용자의 입력 값이 Text 인 경우 화면에 입력된 값을 screen capture 기능을 통해서 획득할 수 있습니다.


* PS/2 키 입력 처리 방법 및 보안 취약성

[PS/2 포트를 통한 키 입력 흐름]


사용자 삽입 이미지

1.     PS/2 Port : PS/2 방식 키보드로부터 키보드 입력 값 접수

2.     8042 Register : 키보드 입력 값 임시 저장 버퍼

3.     IDT (Interrupt Descriptor Table) : 시스템 인터럽트 발생시에 인터럽트 핸들러와 연결되는 테이블 (키보드 입력 시에 ISR 호출)

4.     ISR (Interrupt Service Routine) : 인터럽트 처리 핸들러 (키보드 입력 시 입력 값을 처리하기 위해 호출됨)

5.     Keyboard Filter Driver : 키보드 입력 값 Filtering (optional)

6.     Keyboard Class Driver : PS/2, USB 공통으로 사용하는 OS 기본 최 상단 드라이버

7.     System Message Queue : 윈도우 OS 의 메시지 처리과정

8.     Thread Message Queue : 윈도우 OS 의 메시지 처리과정

9.     Application Program (IE ) : 최종적으로 키보드 입력 값 접수

 

[보안위협]

1.     PS/2 Port 를 감시하여 사용자 키 입력 획득

2.     IDT hooking 하여 사용자 키 입력 획득

3.     Keyboard Filter Driver 를 삽입하여 사용자 키 입력 획득

4.     System Message Queue Hooking 하여 사용자 키 입력 획득

5.     Thread Message Queue Hooking 하여 사용자 키 입력 획득

6.     SubClassing (Application Message 처리루틴 변경) 을 통해 사용자 키 입력 획득

7.     Application API Hooking 을 통한 사용자 키 입력 획득

8.     BHO(Browser Helper Object) 를 통한 사용자 키 입력 획득

9.     Screen Capture 를 통한 사용자 키 입력 획득

10.   사회공학적 방법을 통한 사용자 키 입력 획득


[키보드 보안 프로그램 구현방법]

A 업체에서 구현하는 방식입니다.
보호모드로 들어가면 커널구간에서 Random 키를 발생시켜서 사용자 키 입력과 섞는 방법을 사용합니다.
커널구간에 KeyLogger 가 설치되어 사용자의 입력값을 가로채어도 유효한 데이타를 추출할 수 없도록 하는 기법입니다. 커널레벨에서 드라이버가 보안 프로그램으로 직접 사용자의 입력값을 전달하므로
유저구간에서 설치된 KeyLogger 로부터 데이타를 보호합니다.

사용자 삽입 이미지

다른 업체들의 경우는 대부분 다음과 같은 방법으로 키보드 보안을 구현하고 있습니다.
즉, IDT hooking 을 통한 ISR 의 교체 등을 사용하고 있습니다. 약간씩 구현상 상이한 점이 있지만 대부분 컨셉은 비슷합니다
.

사용자 삽입 이미지

위와 같은 방식 외에 Keyboard 필터 드라이버를 설치하여 사용자의 키 입력값을 보호하는 경우 등이 있을 수 있지만, 보안성이 낮아서 국내 서비스 프로그램 중에는 그러한 방식으로 사용중인 곳은 없습니다.

PS/2 키보드 보안 프로그램들이 위와 같은 기술로 보안을 제공하고 있지만
위의 [보안위협] 에서 설명한 것들 중 다음의 공격에는 보안을 제공하는데 한계가 있습니다.

1.     PS/2 Port
를 감시하여 사용자 키 입력 획득 --> 보호
2.     IDT
hooking 하여 사용자 키 입력 획득 --> 보호
3.     Keyboard Filter Driver 를 삽입하여 사용자 키 입력 획득 --> 보호
4.     System Message Queue Hooking 하여 사용자 키 입력 획득 --> 보호
5.     Thread Message Queue
Hooking 하여 사용자 키 입력 획득 --> 보호
6.     SubClassing (Application Message
처리루틴 변경) 을 통해 사용자 키 입력 획득 --> 보호
7.     Application API Hooking
을 통한 사용자 키 입력 획득 --> 보호
8.     BHO(Browser Helper Object)
를 통한 사용자 키 입력 획득 --> 보호못함
9.     Screen Capture
를 통한 사용자 키 입력 획득 --> 보호못함
10.  
사회공학적 방법을 통한 사용자 키 입력 획득 --> 보호못함

왜일까요?
BHO 는 브라우저에서 DOM 을 읽을 수 있습니다. 키보드 보안 프로그램이 아무리 열심히 데이타를 보호해서 브라우저에 사용자 키 값을 전달하는 순간, BHO 형태의 keylogger 에게 노출될 수 밖에 없는 것이죠.
이를 막을 수 있는 방법은 없을까요? 키보드 보안 프로그램 자체로서는 DOM 에 노출되는 시간을 최소화 시킬 수는 있어도 Form 이 Submit 되기 직전에 사용자 입력값을 가져가는 것을 막기란 불가능 한 일입니다.
그래서 2005년 금융감독원은 BHO 취약점을 해결하라는 지침을 내렸지요.
그런데 어떻게 해결할 수 있을 까요? 그래서 국내 금융권에서 E2E 보안 이니 확대 E2E 보안이니 하는 것들이 생겼습니다. 이것은 키보드보안 프로그램과 PKI Client 프로그램을 연동해서 사용자 키보드 데이타를 서버까지 직접 전달한다는 개념입니다. 이렇게 n 개의 키보드 보안 업체와 m 개의 PKI 업체가 서로 업체별 프로토콜로 서로 E2E 연동이란 것을 하다보니, 그야말로 이때부터 본격적인 스파게티가 되 버립니다.. -_-;


* USB 키 입력 처리 방법 및 보안 취약성

[USB 포트를 통한 키 입력 흐름]


사용자 삽입 이미지

       1.    
USB Bus : USB 방식 키보드로부터 키보드 입력 신호 전달

2.     PCI Bus Driver

3.     Host Controller Driver : USB Driver Stack 의 가장 최하위 단. Port driver miniport 드라이버()로 구성된다. 시스템이 USB Device (H/W) 를 발견하면 적당한 miniport 드라이버를 로드한다. 로드된 miniport 드라이버는 port driver 를 로드하며 port 드라이버는 H/W independent host controller driver 역할을 수행한다. USB Transaction, Power management bus enumeration을 수행한다.

4.     USB Hub Driver : Host Controller Driver root hub enumerate 하게 되면 로드된다. USB Bus Driver 라고도 불리우며, 시스템의 각각의 허브에 대한 Device Driver 이다.

5.     USB Generic Driver : 각각의 vendor 나 사용자 가 정의한 Client Driver 들은 Hub Driver 상단에 위치할 수 있으며, 특정장치의 특성에 맞는 Client 드라이버가 사용될 수 있다. 만약 특정 장치에 대한 것이 아닌 일반적인 Client Driver 를 작성하여 사용할 때 USB Generic Driver 상단에 Client Driver 가 설치될 수 있으며, 이 경우 사용되는 공통 parent 드라이버이다.

6.     HID USB Driver 

7.     Keyboard HID Class Driver :

8.     Keyboard Class Driver : PS/2, USB 공통으로 사용하는 OS 기본 최 상단 드라이버

9.     System Message Queue : 윈도우 OS 의 메시지 처리과정

10.   Thread Message Queue : 윈도우 OS 의 메시지 처리과정

11.  Application Program (IE ) : 최종적으로 키보드 입력 값 접수


 

[보안위협]

      1. USB Bus Driver hooking 하여 사용자 키 입력 획득
2. USB Hub Driver hooking 하여 사용자 키 입력 획득
3. USB Generic Driver hooking/Filtering 하여 사용자 키 입력 획득
4. HID USB Driver hooking/Filtering 하여 사용자 키 입력 획득
5. Keyboard HID Class Driver hooking/Filtering 하여 사용자 키 입력 획득
6. Keyboard Class Driver hooking/Filtering 하여 사용자 키 입력 획득
7. System Message Queue 를 Hooking 하여 사용자 키 입력 획득
8. Thread Message Queue 를 Hooking 하여 사용자 키 입력 획득
9. SubClassing (Application Message 처리루틴 변경) 을 통해 사용자 키 입력 획득
10. Application API Hooking 을 통한 사용자 키 입력 획득
11. BHO(Browser Helper Object) 를 통한 사용자 키 입력 획득
12. Screen Capture 를 통한 사용자 키 입력 획득
13. 사회공학적 방법을 통한 사용자 키 입력 획득


[키보드 보안 프로그램 구현방법]

2006년 정도까지만 하더라도 국내의 대부분의 업체들은 업체별로 약간의 차이가 있겠으나 대부분 USB Generic Driver 레벨에서의 필터링이나 후킹 방식으로 사용자의 키 입력을 받아서 안전한 경로를 통해 어플리케이션에 전달하는 방식으로 키 입력을 보호하였습니다. 이러한 방식은 OS 가 제공하는 API 를 통한 구현으로 안전하게 구현할 수 있는 방식이었으며 동작에 안정성을 어느정도 제공받을 수 있었지요.

그러나 고려대 학생들에 의해서 USB 버스 하운드 였던가요? USB 드라이버 개발자 들 사이에서 사용하는 USB 버스 모니터링 툴이 있습니다. 이 툴을 이용하면 사용자의 키 입력값이 보인다는 공공연한 사실이 기사화 되었고, 금감원으로 부터 키보드 보안 프로그램을 적용한 국내 금융권에도 이 보안 취약성을 패치하라는 지령이 떨어집니다.

USB 드라이버를 개발한 보안 업체들은 고대 학생들이 굳이 이야기 해 주지 않아도 다들 알고 있는 사실이었을 것입니다. 그럼 키보드 프로그램 개발 업체들이 왜 이 취약성을 허용하고 있었던 것일까요?
키보드 보안분야에서 일했던 사람으로 변명을 좀 하자면, USB 버스는 PS/2 포트와는 다르게 키보드 전용 장치가 아니기 때문에, USB 버스를 후킹한 후 입력데이타 들 중 키보드 장치를 정확히 분간해 내고, OS 레벨에서 처리해 주던 여러가지 처리들을 직접 해야 하는 상당히 까다로운 작업이 요구되었기 때문입니다.
단지 어렵기 때문이 아니라, USB 보안 수준을 내릴 경우 국내 금융권/포탈 등에 서비스 되고 있고, 아마 천만 이상의 사용자들이 매일 사용하는 키보드 보안 프로그램이 별별 USB 장치들과 충돌을 일으키지 않을까(오동작) 걱정하지 않을 수 없었습니다. 키보드 보안 프로그램은 보안 프로그램이면서 금융권을 사용하기 위해 반드시! 설치되어야 하는 상용 프로그램이었기 때문이죠!!
보안성과 가용성 사이의 저울질은 상용 보안 프로그램을 만드는 업체라면 항상 고민하게 되는 문제입니다.

물론 덕분에(?) 두려워서 적용하지 못했던 기술을 '지침' 입니다.. 하고 필드에 적용해 볼 수 있는 명분이 생겼죠. 결국 지금은 몇몇 업체들은 아래와 같이 Bus Driver 레벨에서 보안을 제공하고 있습니다.

사용자 삽입 이미지

1. USB Bus Driver hooking 하여 사용자 키 입력 획득 --> 보호
2. USB Hub Driver hooking 하여 사용자 키 입력 획득 --> 보호
3. USB Generic Driver hooking/Filtering 하여 사용자 키 입력 획득 --> 보호
4. HID USB Driver hooking/Filtering 하여 사용자 키 입력 획득 --> 보호
5. Keyboard HID Class Driver hooking/Filtering 하여 사용자 키 입력 획득 --> 보호
6. Keyboard Class Driver hooking/Filtering 하여 사용자 키 입력 획득 --> 보호
7. System Message Queue 를 Hooking 하여 사용자 키 입력 획득
--> 보호
8. Thread Message Queue 를 Hooking 하여 사용자 키 입력 획득 --> 보호
9. SubClassing (Application Message 처리루틴 변경) 을 통해 사용자 키 입력 획득 --> 보호
10. Application API Hooking 을 통한 사용자 키 입력 획득 --> 보호
11. BHO(Browser Helper Object) 를 통한 사용자 키 입력 획득 --> 보호못함
12. Screen Capture 를 통한 사용자 키 입력 획득--> 보호못함
13. 사회공학적 방법을 통한 사용자 키 입력 획득 --> 보호못함


* User Level 키 입력 처리 방법 및 보안 취약성

위에서 설명한 바와 같이 Message Queue 나 Subclassing 에 대한 방어는 커널레벨에서 유저레벨의 어플리케이션에 직접 사용자의 입력값을 전달하는 것으로 보호를 진행하고 있습니다.
그러나 앞에서 설명한 BHO 를 통한 사용자 키 입력 획득은 키보드 보안 프로그램에서 처리할 수 있는 영역이 아닙니다. 즉, 키보드 보안 프로그램이 보호하는 영역이란 사용자의 키보드 입력부터 특정 어플리케이션(보통 브라우저가 많겠죠) 에 키 입력 값을 전달하는 그 순간까지입니다.
어플리케이션에서 키 입력 값을 노출하게 되면 도로아미타불 이란 말이죠 ^^

* 마치며..

지금까지는 매우 원론적으로 키보드 보안 프로그램의 동작방식을 설명했습니다.
그러나 위에 보호한다고 하는 영역이 100% 안전한가? 라고 묻는다면 솔직히 '그렇지는 않다' 고 할 수 밖에 없습니다. 예를 들면 키로거 들이라고 해서 위와 같이 구현을 못할까요?
키로거들도 할 수 있습니다.. 그래서 키보드 보안 프로그램들에서는 곳곳에서 타 프로그램에 의해서 중요한 자원이 선점된 것들이 있는지 확인하고 복구하는 기능도 포함되어 있습니다.

국내의 대부분의 키보드 보안 프로그램들은 그들간의 과열 경쟁으로 인해서 이러한 선점과 복구 등에 대해서는 대응하는 최고의 기술을 가지고 있다고 생각합니다.

그럼 키입력을 가로채는 키로거들은 주로 어떤 형태를 가지고 있을까요?
정확한 통계는 알 수 없습니다만, 안철수연구소의 ASEC 의 키로거 수집 현황을 보면 드라이버 후킹/필터링 방식의 키로거보다는 phishing 등의 방식을 이용한 스파이웨어 형태가 더 많은 것 같습니다.

ASP 형 키보드 보안 프로그램이라는 것들은 어플리케이션(브라우저)가 프로그램을 구동시킨 후 부터 그 역할을 수행하게 되는데, phishing 사이트에서 사용자의 키 입력을 가로채는 것에 대한 대안은 아니겠지요 -_-;
그럼 PC 상주형 키보드 보안 프로그램을 사용하면 되는 건가요?
PC 상주형 키보드 보안 프로그램들은 최종적으로 데이타를 전달해야 할 어플리케이션이 정해져 있지 않기 때문에 프로그램들이 키 입력 값을 사용 할 수 있도록 특정 구간에서 특히 유저레벨에서 사용자의 키 입력 흐름을 정상 궤도로 흘려 보내야 할 것 입니다. 취약성이 발생하겠죠.
그럼 메모리 해킹은 어떨까요?
브라우저의 메모리를 모니터링해서 사용자 키 입력 값이 메모리 상에서 떠 다니는 것을 방지할 수 있을까요?
키보드 보안 프로그램과 E2E 연동된 PKI 프로그램에서는 내부적으로 데이타를 암호화 해서 저장하고 있습니다만, 그것도 언젠가 풀릴 때를 노려서 값을 획득하는 프로그램이 있다면요? 즉, 100% 신뢰된 보안이란 존재하지 않는다는 것입니다.

그럼 키보드보안 프로그램이 필요없는 것인가요?
키보드 보안 프로그램이 여러가지 한계를 가지고 있어도 키보드 보안 프로그램이 적용된 사이트가 있다면 그렇지 않은 사이트에 비해서 더 안전한 것은 확실합니다. 보안이란 단계적으로 적용되어야 하는 것이며, 하나의 어플리케이션이나 어플라이언스 장비들로 커버할 수 있는 부분이 아닙니다.

사용자 개인정보가 '돈' 으로 직결되는 시대이다 보니, 정말이지 개인정보보호란 중요한 분야입니다.
PC 단에서 개인정보보호를 위한 방안으로 키보드 보안 프로그램이 필요한 것은 사실이지만, 이젠 PC 단에서의 개인정보 보호도 키보드 보안 프로그램에 의존하는 것이 아니라 패러다임 쉬프트가 일어날 때가 되지 않았나 싶습니다..

Posted by skensita