본문 바로가기
파이썬 스터디 과제/파이썬 해킹 프로그래밍

10장-윈도우 드라이버 퍼징

by laoching 2015. 2. 17.
728x90
반응형

윈도우 드라이버를 공격하는 것은 이제는 버그 헌터뿐만 아니라 공격 코드 개발자에게도 일상적인 것이 됐다.

이번 장에서는 IOCTL을 구현하는 로컬 디바이스에 연결하는 방법과 해당 디바이스에 IOCTL을 전달하는 방법을 다룬다.

드라이버에 IOCTL을 전달하기전에 immunity디버거를 사용해 변형된 IOCTL을 만든다.

그 다음 디버거에 내장되어있는 정적분석 라이브러리인 driverlib를 이용해 대상 드라이버에 대한 자세한 정보를 얻어내는 방법과 컴파일된 드라이버 파일에서

중요한 제어 흐름, 디바이스의 이름, IOCTL 코드를 해석하는 방법 등을 배운다.

driverlib를 통해 ioctlizer라는 독립적인 드라이버 퍼저를 작성한다.


드라이버 통신

윈도우 시스템의 거의 모든 드라이버는 특정한 디바이스 이름과 심볼릭 링크로 운영체제에 등록된다.

심볼릭 링크란? : 유저 모드에서 드라이버의 핸들을 구해 드라이버와의 통신을 가능하게 하는 것


드라이버 핸들을 구하기위해 사용하는 kernel32.dll의 CreteFileW API 프로토 타입     

첫 번째 파라미터는 핸들을 얻으려는 파일이나 디바이스의 이름을 입력한다.

드라이버의 경우에는 심볼릭 링크 이름을 전달한다.

dwDesiredAccess는 디바이스에서 읽을 것인지 아니면 쓸 것인지를 나타낸다.

여기서는 GENERIC_READ(0x80000000)와 GENERIC_WRITE(0x40000000)를 선택할 것이다.

dwShareMode 파라미터를 0으로 설정하면 CreateFileW로 구한 디바이스 핸들을 닫기 까지는 외부에서 해당 디바이스에 접근할 수 없다.

lpSecurityAttributes 파라미터를 NULL로 설정하면 핸들에 디폴트 보안 속성이 적용되고 자식 프로세스에 의해 핸들이 상속되지 않는다.

디바이스가 실제로 존재하는 경우에만 핸들을 구하기 위해 dwCreationDisposition 파라미터의 값을 OPEN_EXISTING(0x3)으로 설정 할 것이다.

디바이스가 존재하지 않는 경우에는 CreateFileW API가 실패한다.

마지막 두 파라미터는 각기 0과 NULL로 설정한다.

일단 CreateFileW API를 통해 핸들을 구하면 그 핸들을 이용해 디바이스에 IOCTL을 전달할 수 있다. IOCTL을 디바이스에게 전달하려면 kernel32.dll의 DeviceIoControl API를 사용해야 한다.

프로토 타입

첫 번째 파라미터에는 CreateFileW를 이용해 구한 핸들을 입력한다.

dwIoControlCode 파라미터는 디바이스 드라이버에 전달할 IOCTL 코드를 나타낸다.

IOCTL 코드에 의해 드라이버가 어떤 종류의 작업을 수행할지 여부가 결정된다.

lpInBuffer는 디바이스 드라이버에 전달할 정보가 담겨지는 버퍼에 대한 포인터다.

드라이버를 퍼징하기 위해 사용되는 데이터를 바로 이 버퍼에 담아 드라이버에 전달한다.

nInBufferSize 파라미터는 드라이버에 전달되는 버퍼의 크기를 나타내기 위한 단순한 정수 값이다.

lpOutBuffer와 lpOutBufferSize 파라미터는 앞의 두 파라미터와 동일한 성격의 파라미터이긴 하지만 앞의 경우와는 반대로 드라이버에서 정보를 전달받기 위해 사용되는 파라미터다.

lpBytesReturned 파라미터는 드라이버에서 전달받은 데이터의 크기를 구하기 위해 사용한다.

lpOverlapped에는 단순히 NULL값을 입력한다.


Immunity 디버거를 이용한 드라이버 퍼징

Immunitu 디버거의 뛰어난 후킹 기능을 이용하면 DeviceIoControl 호출이 드라이버에 전달되기 전에 중간에 가로채 내용을 변경할 수 있다.

ioctl_fuzzer.py라는 파이썬 파일을 만들자


먼저 IOCTl 코드를 읽고

입력 버퍼의 길이와

그것의 위치를 알아낸다.

그리고 입력 버퍼와 크기가 동일판 버퍼를 만들어 그곳에 임의의 값을 채워 넣는다.

그 버퍼의 내용을 입력 버퍼에 써넣고 테스트 케이스를 로그파일에 저장한다.

이와 같은 작업을 완료한 다음에는 유저 모드 프로그램으로 리턴한다.

이 코드를 작성한 뒤 icotl_fuzzer.py 파일을 Immunity 디버거의 PyCommand 디렉토리에 넣는다.

그 다음에는 IOCTL을 사용하는 프로그램(방화벽, 안티 바이러스 프로그램 등)을 하나 선택하고 그것을 디버거 내에서 실행시킨 후 ioctl_fuzzer를 이용해 퍼징을 수행한다.

두 개의 IOCTL 코드에 대해 입력 버퍼의 내용을 완전히 바꿔 드라이버에 전달했다.

계속해서 유저 모드 프로그램과 상호 작용함으로써 입력 버퍼의 내용을 변형시킬 수 있으며, 언젠가는 대상 드라이버에 에러가 발생하게 할 수 있을 것이다.


Driverlib - 드라이버 정적 분석 툴

드라이버에서 핵심이 되는 정보를 추출하려면 리버스 엔지니어링이라는 작업을 수행해야한다.

driverlib는 이런 작업을 자동으로 수행하게 설계된 파이썬 라이브러리다.


디바이스 이름 알아내기

Immunity 디버거가 제공하는 강력한 파이썬 라이브러리인 driverlib를 이용하면 드라이버 내의 디바이스 이름을 찾아내는 것은 매우 간단하다.

드라이버에서 디바이스 이름을 찾아내는 driverlib의 코드

위 코드는 단순히 드라이버에서 문자열 리스트를 추출하고 그 중에서 '\device\' 문자열을 찾는다.

이는 드라이버가 등록하는 심볼릭 링크 이름에 '\device\' 문자열이 사용되기 때문이며,

심볼릭 링크 이름을 통해 유저모드 프로그램은 해당 드라이버의 핸들을 구할 수 있다.

테스트를 해보자.

beep.sys를 연결하라는데

내 vm에는 beep.sys가 없다..


IOCTL 디스패치 루틴 찾기

IOCTL 인터페이스를 구현하는 드라이버는 반드시 IOCTL 요청을 처리하는 IOCTL 디스패치 루틴을 가져야 한다.

드라이버가 로드될 떄 DricerEntry 루틴이 가장 먼저 호출된다.

DriverEntry 루틴의 구조

기본적인 DriverEntry 루틴이긴 하지만 대부분의 디바이스가 어떻게 자신을 초기화하는지 보여준다.

어셈블리 코드에서 REG는 MajorFunction 구조체를 나타내며 MajorFunction 구조체의 오프셋 0x70에 함수 포인터인 CONSTANT를 저장하고 있다.

이를 통해 IOCTL 처리 함수의 위치와 IOCTL 코드를 어느 부분에서 찾아야 할지 추론할 수 있다. 

위 코드는 특정 패턴을 찾아내기 위해 Immunity 디버거의 강력한 검색 API를 사용한다. 일단 매칭되는 패턴을 찾으면 IOCTL 디스패치 함수를 나타내는 함수 객체를 반환한다.

이 반환된 함수에서 유효한 IOCTL 코드를 찾는 것이다.


IOCTL 코드 찾기

IOCTL 디스패치 루틴은 전달된 IOCTL 코드 값에 따라 다양한 잡업을 수행한다.

IOCTL 코드 값을 찾아내려면 IOCTL 코드 값에 따라 분기되는 모든 가능한 수행 경로를 조사해야 한다.

먼저 IOCTL 디스패치 함수의 골격이 C 언어로 어떻게 작성되는지 살펴보고 그 다음에는 IOCTL 코드 값을 추출하기 위해 어셈블리 코드를 디코딩하는 방법을 살펴본다.

1. 디스패치 루틴에 처리하게 요청된 IOCTL 코드가 무엇인지 판단한 다음에는 일반적으로

2. switch{} 문을 이용해 드라이버가 어떤 작업을 수행해야 하는지 판단한다.

이런 과정이 어셈블리 언어로 변환될 때는 몇 가지 현태로 변환될 수 있다.

switch{}문이 어셈블리 언어로 변환되는 방법은 다양하다.

CMP 명령을 사용하는 형태는 단순히 전달된 IOCTL 코드값과 상수 값을 비교한다.

비교에 사용되는 상수값은 드라이버를 지원하는 IOCTL 코드 값이다.

동일한 레지스터값에 대한 SUB 명령과 몇 가지 혀애의 조건 JMP 명령으로 이뤄진다.

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

SUB ESI, 1337

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

이 명령으로 가장 작은 IOCTL 코드 값이 0x1337 이라는 것을 알 수 있다.



드라이버 퍼저 작성


728x90
반응형

댓글