/ POSTS

windows-0x07

0x07 DEP(1)

What is DEP?

DEP는 특정 메모리의 실행 권한을 제거하여, 메모리에 올라온 Shellcode의 실행을 막는 메모리 보호 기법입니다.

DEP는 Window XP Service Pack 2에서부터 적용된 기법이며, H/W 기반 DEP와 S/W 기반 DEP로 나눌 수 있습니다.

이 두 가지 방식의 차이는 CPU가 하드웨어적으로 DEP를 지원 가능한가를 기준으로 나뉜다고 생각하시면 됩니다.

CPU에서 지원 가능한 경우 H/W DEP라 하고, 그렇지 않은 경우 S/W로 그 기능을 지원하는 방식인 것이죠.

최근 나온 CPU들은 대부분 H/W 기능을 지원하므로 일반적으로 DEP라고 하면 H/W DEP라고 생각하시면 됩니다.

DEP에는 아래와 같이 4가지 종류의 옵션이 존재합니다.

  • OptIn : 지정된 Binary만 보호
  • OptOut : 지정된 Binary를 제외하고 보호
  • AlwaysOn : 모든 Process 항상 보호
  • AlwaysOff : 모든 Process 보호하지 않음

OS를 설치한 후, 직접 변경한 적이 없다면 Default로 OptIn 옵션이 적용되어 있고 다음과 같이 확인할 수 있습니다.

[그림 1-1]

DEP가 적용되면 Shellcode를 STACK에 넣어서 실행해왔던 지금까지의 방법으로는 성공할 수가 없게 됩니다.

하지만 이 기법 역시 우회가 가능하며, DEP를 우회하기 위해 필요한 기법과 개념들은 다음과 같습니다.

  • Gadget
  • RTL
  • Chaining RTL
  • ROP

지금부터 하나씩 살펴보겠습니다.

Gadget

Gadget이란 “mov esp, ebp” 등의 명령어 조각을 의미했었습니다.

하지만 지금 시스템 해킹에서의 Gadget은 RET로 끝나는 명령어 조각을 의미하게 되었습니다.

이 Gadget이 공격에서 왜 중요한지는 Chaining RTL과 ROP를 다루면서 이야기하겠습니다.

RTL

RTL은 Return to Library의 약자로 해석을 하면 Library로 돌아간다는 의미입니다.

Library로 돌아간다?

아까 앞에서 DEP가 적용되면 Shellcode를 실행할 수가 없다고 했었던 것 기억나시죠?

그래서 RET를 Shellcode의 주소로 변조하는 것이 아니라, 실행 권한이 있을 수 밖에 없는 Library의 함수의 주소로 변조하는 것이죠.

그리고!!!

RTL에서 가장 중요한 것은 함수의 인자를 직접 생성해야 한다는 것입니다.

Shellcode를 실행했을 때와는 다르게 이 방법은 RET 주소를 원하는 함수의 주소로 바꿔줬을 뿐이기 때문이죠.

그 함수가 실행되기 위해서는 함수의 동작에 필요한 인자들이 있어야하겠죠?

CPU가 알아서 자동으로 만들어 줄리가 없으니,,STACK에 직접 구성해줘야 합니다.

인자를 1개만 취하는 함수 Func()가 있다고 가정하겠습니다.

RTL 기법으로 이 함수를 호출하기 위해서는 STACK을 다음과 같이 구성해야 합니다.

[그림 1-2]

위 그림에서 중간 STACK 그림을 보면 Func을 호출하기 위해 RET를 Func 함수의 주소로 변조하고, 그 위치에서 +8만큼 떨어진 곳에 함수의 인자를 구성한 것을 확인할 수 있습니다.

그런데 왜 인자의 위치를 저렇게 구성해줘야 할까요?

다음 그림을 보면서 설명해보겠습니다.

[그림 1-3]

빨간 색으로 표시된 부분은 Main 함수의 에필로그 과정, 파란 색으로 표신된 부분은 Func 함수의 프롤로그 과정을 나타낸 것 입니다.

먼저, 빨간 색. Main 함수의 에필로그 과정을 봅시다.

POP EIP, JMP EIP를 코드를 수행했을 때의 위치가 가장 왼쪽 STACK에 표시된 ESP가 가르키는 곳 입니다.

이후, JMP EIP 코드가 실행되면 새로운 함수가 실행되고 새 함수의 프롤로그 과정이 진행됩니다.

프롤로그 과정의 일부인 PUSH EBP가 실행되면 새 STACK Frame의 기준점을 만듭니다.

여기까지 진행된 후, STACK을 살펴봅시다.

Main 함수의 RET가 뒤이어 실행되는 함수의 SFP 자리로 바뀐다는 것을 알 수 있습니다.

그리고 그 다음에, RET를 만들어주었습니다.

RTL이 아니라 정상적인 방식인 CALL을 통해 함수가 실행된다면 STACK에 다음에 실행할 곳의 위치를 넣어줍니다.

그런데 우리는 컴파일러가 만들어주는 것이 아니기 때문에 직접 만들어줘야 합니다.

그래서 저 위치에 다음에 실행할 곳의 주소 역할을 할 값을 직접 넣어 구성해주는 것 입니다.

쉽게 생각하면 컴파일러가 만들어주는 구조를 똑같이 흉내낸다고 생각하시면 되겠습니다.

이것이 RTL의 전부입니다.

다음은 디버거를 통해 RTL을 진행한 그림입니다.

[그림 1-4]
[그림 1-5]

잘 실행이 되네요^^

그런데 이 방식은 실제 환경에서는 거의 불가능한 방법이라는 것도 알아두셔야 합니다.

실제 환경에서는 ASLR, Safe SEH 등이 기본적으로 적용되어 있기 때문에 내가 원하는 함수의 주소를 찾아내기 힘들기 때문입니다.

그래서 이 기법은 ROP를 위한 기본 개념 정도로만 이해하시기 바랍니다.

글이 너무 길어졌네요. 이만 줄이고 다음 편에서 ROP를 알아보도록 하겠습니다.