[6주] 10장 윈도우 드라이버 퍼징

Posted by dw0rdptr
2015. 2. 23. 09:20 Study/파이썬 해킹 프로그래밍

윈도우 드라이버를 공격에는 드라이버에 대한 원격 공격보단 공격 대상 시스템에 대한 상승된 권한을 획득하기 위해 로컬 드라이버를 공격하는 것이 보편적이다. 오버플로우나 Impersonation 공격 취약점이 있는 드라이버가 로컬에 설치되어 있는 경우 그것을 이용하면 시스템 권한을 획득하기 때문에 원격으로 공격해 제한된 권한만을 얻었을 때보다 훨씬 자유롭게 시스템의 모든 정보에 접근 할 수 있다.

드라이버가 상호작용하려면 유저모드와 커널 모드 간의 변환이 이뤄져야 한다. 

불안정하게 구현된 loctl 핸들러를 이용하면 상승된 권한을 획득하거나 공격 대상 시스템에서 에러가 발생하게 만들 수 있다.


이제 Immunity 디버거의 후킹기능을 이용해 드라이버 퍼징을 해보자

윈도우 시스템에서는 드라이버 통신을 위해 IOCTL을 전달하려면 DeviceIoControl 함수를 사용해야 하는데 이 함수의 호출이 드라이버에 전달되기 전에 중간에 가로채 내용을 변경 할 수 있다. 이런 식으로 Immunity 디버거를 이용해 간단한 퍼저를 만들 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#ioctl_fuzzer.py
import struct
import random
from immlib import*
 
class ioctl_hook(LogBpHook):
    
    def __init__(self):
 
        self.imm = Debugger()
        self.logfile = "C:\ioctl_log.txt"
        LogBpHook.__init__(self)
 
    def run(self, regs):
        """
        ESP+4    -> hDevice
        ESP+8    -> IoControlCode
        ESP+C     -> InBuffer
        ESP+10     -> InBufferSize
        ESP+14     -> OutBuffer
        ESP+18    -> OutBufferSize
        ESP+1C    -> pBytesReturned
        ESP+20     -> pOverLapped
        """
 
        in_buf =""
 
        ioctl_code = self.imm.readLong(regs['ESP']+8)
 
        inbuffer_size = self.imm.readLong(regs['ESP'+ 0x10)
 
        inbuffer_ptr = self.imm.readLong(regs['ESP'+ 0xC)
 
        in_buffer = self.imm.readMemory(inbuffer_ptr, inbuffer_size)
        mutated_buffer = self.mutate(inbuffer_size)
 
        self.imm.writeMemory(inbuffer_ptr, mutated_buffer)
 
        self.save_test_case( ioctl_code, inbuffer_size, in_buffer, mutated_buffer )
 
 
    def mutate( self, inbuffer_size ):
 
        counter = 0
        mutated_buffer = ""
 
        while counter < inbuffer_size:
            mutated_buffer += struct.pack( "H", random.randint(0255) )[0]
            counter += 1
 
        return mutated_buffer
 
    def save_test_case( self, ioctl_code, inbuffer_size, in_buffer, mutated_buffer ):
 
        message = "*****\n"
        message += "IOCTL Code: 0x%08x\n" % ioctl_code
        message += "Buffer Size: %d\n" % inbuffer_size
        message += "Original Buffer: %s\n" % in_buffer
        message += "Mutated Buffer: %s\n" % mutated_buffer.encode("HEX")
        message += "*****\n\n"
 
        fd = open(self.logfile, "a")
        fd.write(message)
        fd.close()
 
def main(args):
 
    imm = Debugger()
 
    deviceiocontrol = imm.getAddress( "kernel32.DeviceIoControl" )
        
    ioctl_hooker = ioctl_hook()
    ioctl_hooker.add( "%08x" % deviceiocontrol, deviceiocontrol )
        
    return "[*] IOCTL Fuzzer Ready for Action!"
 
cs


1.ioctl_fuzzer.py 파일을 immunity 디버거의 PyCommand 디렉토리에 넣는다. 

2. Immunity 를 실행하고 Open 메뉴로 와이어샤크를 실행시킨다 .

3. PyCommand로 ioctl_fuzzer를 이용해 퍼징을 수행한다.

4. 퍼징 수행 내용이 C:\ 경로에 로깅된다.


이 기술은 쉽고 효과적인 반면 퍼징 대상이되는 디바이스의 이름을 알지 못하며 유저 모드 소프트웨어가 사용하는 IOCTL 코드만을 알 수 있기 때문에 모든 테스트 케이스를 테스트 할 수 있는 것은 아니다.


Driverlib는 디바이스 이름과 IOCTL 코드 등 드라이버에서 핵심이 되는 정보를 자동으로 추출한다. 드라이버 퍼저에서 사용할 디바이스 이름과 IOCTL 코드를 driverlib를 통해 구한다.

사용법은 간단하다. C:\WINDOWS\System32\drivers\beep.sys 드라이버를 로드한 후 immunity 디버거의 PyShell에서

>>>import driverlib

>>>driver = driverlib.Driver()

>>>driver.getDeviceNames()

['\\Device\\Beep']

>>>

세 줄의 코드만을 작성해 \\Device\\Beep 라는 디바이스 이름을 찾아냈다.


이제 드라이버 퍼저를 작성하자. 먼저 IOCTL 코드 리스트와 디바이스 리스트를 구해 dictionary를 다시 파일에 저장한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pickle
import driverlib
from immlib import *
def main( args ):
    ioctl_list = []
    device_list = []
    
    dbg = Debugger()
    driver = driverlib.Driver()
 
    ioctl_list = driver.getIOCTLCodes()
    if not len(ioctl_list):
        return "[*] ERROR! Couldn't find any IOCTL codes."
 
    device_list = driver.getDeviceNames()
    if not len(device_list):
        return "[*] ERROR! Couldn't find any device names."
 
    master_list = {}
    master_list["ioctl_list"= ioctl_list
    master_list["device_list"= device_list
 
    fd = open( args[0], "wb")
    pickle.dump( master_list, fd )
    fd.close()
 
    return "[*] SUCCESS! Saved IOCTL codes and device names to %s" % args[0]
 
cs


디바이스 이름과 지원되는 IOCTL 코드값을 구했으므로 간단한 퍼저를 작성하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import pickle
import sys
import random
from ctypes import *
 
kernel32 = windll.kernel32
 
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 0x3
 
fd = open(sys.argv[1], "rb")
master_list = pickle.load(fd)
ioctl_list = master_list["ioctl_list"]
device_list = master_list["device_list"]
fd.close()
 
valid_devices = []
 
for device_name in device_list:
 
        device_file = u"\\\\.\\%s" % device_name.split("\\")[::-1][0]
        print "[*] Testing for device: %s" % device_file
        
        driver_handle = kernel32.CreateFileW(device_file,GENERIC_READ|
                                    GENERIC_WRITE,0,None,OPEN_EXISTING,0,None)
        if driver_handle:
            print "[*] Success! %s is a valid device!"
                
            if device_file not in valid_devices:
                        valid_devices.append( device_file )
            kernel32.CloseHandle( driver_handle )
        else:
            print "[*] Failed! %s NOT a valid device."
if not len(valid_devices):
        print "[*] No valid devices found. Exiting..."
        sys.exit(0)
 
while 1:
 
        fd = open("my_ioctl_fuzzer.log","a")
 
        current_device = valid_devices[ random.randint(0len(valid_devices)-1 ) ]
        fd.write("[*] Fuzzing: %s" % current_device)
 
        current_ioctl = ioctl_list[ random.randint(0len(ioctl_list)-1)]
        fd.write("[*] With IOCTL: 0x%08x" % current_ioctl)
 
        current_length = random.randint(010000) y
        fd.write("[*] Buffer length: %d" % current_length)
 
        in_buffer = "A" * current_length
 
        out_buf = (c_char * current_length)()
        bytes_returned = c_ulong(current_length)
 
        driver_handle = kernel32.CreateFileW(device_file, GENERIC_READ|
                                GENERIC_WRITE,0,None,OPEN_EXISTING,0,None)
        fd.write("!!FUZZ!!")
 
        kernel32.DeviceIoControl( driver_handle, current_ioctl, in_buffer,
                                current_length, byref(out_buf),
                                current_length, byref(bytes_returned),
                                None )
 
        fd.write( "[*] Test case finished. %d bytes returned.\n" % bytes_returned.value )
 
        kernel32.CloseHandle( driver_handle )
        fd.close()
 
cs


'Study > 파이썬 해킹 프로그래밍' 카테고리의 다른 글

[6주] 11장 IDAPython  (0) 2015.02.23
[5주] 09장 Sulley  (0) 2015.02.23
[5주] 08장 퍼징  (0) 2015.02.16
[4주] 07장 DLL과 코드 인젝션  (0) 2015.02.16
[3주] 05장~06장 후킹  (0) 2015.01.26