DEP 그리고 ROP (AKA 준나 어렵네)

2018. 1. 11. 10:180x04 pwnable/윈도우즈 어플리케이션 취약점 분석

728x90

DEP : Data Execution Prevention 

 

 

힙 이나 스택과 같은 데이터 영역에서 실행을 금지하는 기법 

 

소프트웨어 기반 : safeSEH

하드웨어 기반 : NX/XD

 

NX : AMD에 있는 실행 방지 페이지 보호

XD : INTEL에 있는 실행 불가능 비트 

 

인텔 보호 정책 설정 레벨은 4가지다.

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

AlwaysOn : 모든 프로세스를 DEP에 의해 보호  => VISTA는 이것땜에 망했다.

 

OptOut : 예외 설정 외 프로세스는 DEP에 의해 보호 (화이트리스트기반인듯)

예외설정에 포함되지 않는 착한애들(OutBoundary)만 DEP에 의해 보호 

 

OptIn : 예외 설정 외에 프로세스는 DEP에 의해 보호되지 않음 

-> 너가 따로 요청한다면 그때 DEP에 의해 보호해 주겠다. (중간자 역할)

즉, 원하는 프로세스만 DEP 적용 허용하겠다.

 

AlwaysOff : 어떤 프로세스를 DEP에 의해 보호하지 않음 

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

 

아.. 프로세스 작업관리자에서 DEP를 확인가능 하구나.

 

프로세스 익스플로러에서도 DEP 확인 가능 (아는 만큼 보이구나)

 

<DEP 우회 기법 일부> - 나머지는 구글링으로 찾아보기. 

 

1. ret2libc (no shell code)

: 쉘코드로 직접 점프하는 것이 아니라 기존에 존재하는 라이브러리 또는 함수를 호출하는 방법을 이용.

결국 라이브러리/함수 내의 코드가 실행되고, 공격자가 의도한 코드가 실행되게 된다.

 

2. ZwProtectVirtualMemory

: ret2libc를 기반으로 하며, 다수의 ret2libc 함수를 체인으로 연결해 메모리 특정 부분의 실행 가능 

여부를 재정의 한다.

프로세스별로도 가능하지만, 메모리별로도 가능하다는 것이 증명된 ZwProtectVirtualMemory.

 

3. Disable DEP for the process (NtSetInformationProcess)

: DEP가 여러 모드를 가지고 있기 때문에 OS는 기본적으로 각 프로세스에 대해 DEP를 해제할 수 있다.

NX 활성화 유무를 담당하는 코드를 가지는 핸들러나 API를 이용한다.

 

DEP 우회는 이렇게 한다 ! (Hardware DEP bypass)

 

1. Stack Pivot을 활용하여 ESP의 위치를 내린다.

 

2. ret2libc 기법을 활용하여 ESP로 점프한다.

 

3. ROP(Return-Oriented Programming)로 ZwProtectVirtualMemory를 구성하여 

ShellCode 메모리 재 정의 

 

4. ShellCode 실행

 

<TIP>

ESP+0x490을 하면 0x490오프셋만큼 "ESP 위치가 내려간다" 

 

 

ROP 시 리턴을 해야하는데 중요한 사항에 대해 알아봄

 

RETN 8 : WINDOWS API함수는 Standard call 방식을 사용한다.

Windows API함수는 8바이트의 인자가 필요한데, 하나는 문자열, 하나는 주소 (각 4바이트씩)

 

WinExec는 4바이트 push 2번 

call WinExec 실행 

쭉 작업하고 RETN 8 하게 되면 위에서 4바이트 push한 값이 정리 되는 것

 

여기서도 pop pop ret를 사용하네. (가젯)

 

ex)

3000h  *WinExec

3004h      RET

3008h &("calc.exe") <- 실행할 어플리케이션의 주소 (4바이트)

300ch    0x0000 0005   <- SW_HIDE (4바이트) 

 

 

ROP Weapon 중 VirtuallProtect를 사용할 것임

 

우선 VirtualProtect함수를 뜯어봐야함

 

4개의 인자가 필요하다.

 

BOOL WINAPI VirtualProtect 

{

_In_ LPVOID lpAddress, // input

_In_ SIZE_T dwSize,

_In_ DWORD flNewProtect,

_Out_ PWORD lpflOldProtect // output

};

 

dwSize : 페이지 용량 재정의  -> 팁 : 스택 영역에 맞게 사이즈 조정을 해야함 

flNewProtect : 어떤 속성을 정의할 것인지 판단 (Read Write Excute)

lpflOldProtect : NULL이 나올 수 없음. lpflOldProtect가 잘못되면 VirtualProtect 자체가 안됨

(즉, 쓰기 권한이 없으면 오류) -> VirtualProtect 못 씀 

 

팁 : Immunity Debugger로 보면 정확히 RWE에 대해 알 수 있음

 

ROP Weapon :: VirtualProtect() design 

 

3000h &VirtualProtect()

3004h &JMP ESP (중요)

3008h Auto(Shellcode addr)

300Ch 쉘 코드 크기 

3010h RWE 속성 값 (0x40)

3014h 쓰기 가능한 주소 

 

Register를 이용해서도 ROP를 디자인 할 수 있다.

하지만, 중요한건 esp를 하드코딩하면 문제가 생긴다.

 

ESP를 하드코딩하면 안되는 이유?

=>현 스택의 TOP을 찾을 수 없기 때문임. 

 

ESP를 건들리지 않으면 생기는 장점 

쉘코드를 리턴된 값에서 사용할 수 있다?

 

calc.exe - PUSHAD 후 스택 모습을 디자인 하면 이렇다

 

EDI RET

ESI *WinExec

EBP RET

ESP &("calc.exe")

EBX 0x0000 0005

EDX ...

ECX ...

EAX ...

*ESP calc.exe --> 그럼 현재 esp는 calc.exe의 주소가 되는 것 

 

이제 드디어 ROP를 수집하는 것을 배워본다.

타겟 : 1.4.1 wireshark 

 

mona.py는 실행 중인 바이너리의 가젯을 수집할 수 있다. 완전 개 쩔지 않음? ㅋㅋ

 

!mona rop -o  # 운영체제 모드를 쓰지 않는 옵션 : -o 

운영체제의 버전이 바뀔 수 있기 때문에 운영체제의 모듈은 수집하지 않는 것 

(똑똑이)

 

ROP 검색방법 : Kali linux

니가 제일 편한걸로 해 

 

[] : space

ROP 검색 방법 1 

cat rop.txt | grep ":[][]# POP EAX #[]RETN[][][]**"

 

ROP 검색 방법 2  -E: 정규표현식

cat rop.txt | grep -E":[][]# POP .A. #[]RETN[][][]**" # .A. 하면 EAX가 검색 됨 

 

ROP 검색 방법 3

cat rop.txt | grep ":[][]# MOV EAX, DWORD PTR ...:\[EAX\][]#[]RETN" 

# 역슬래쉬 안쓰면 오류 발생 ~

 

EXAMPLE ) calc.exe PUSHAD 후 스택 모습 

 

 

그림 1

EDI RET

ESI *WinExec

EBP RET

ESP &("calc.exe")

EBX 0x0000 0005

EDX ...

ECX ...

EAX ...

*ESP calc.exe --> 그럼 현재 esp는 calc.exe의 주소가 되는 것 

 

그림 2 

 

구성 가이드 실제 값

 

For EDI # POP EDI # RETN 0x64fa  0bc4

# RETN 0x64fa  0bc5

 

For ESI # POP ESI # RETN 0x0051 99e2

&WinExec 0X7c86 23ad

 

For EBX # POP EBX # RETN 0x64fa  09e6

0x0000 0005 0x0000  0005

 

For PUSHAD  #PUSH AD #RETN 0x0046 0d9b

     calc.exe 0x6361 6c63

                  \0 0x00

 

반드시 알아야 하는건, ret 하게 되면 esp는 내려간다.  

 

자 우선 For EDI 부터 할 건데, For EDI라는 것을 EDI 레지스터안에 있는 값을 사용하겠다는 의미

지금 그림에선 EDI에 RET가 있으니 RET에 대한 실제 주소를 찾음(0x64fa 0bc5)

 

POP EDI 하는 순간 RETN의 실제 값 0x64fa  0bc5은 #RETN으로 들어감

 

RET 했으니 ESP가 내려가게 되고 지금 ESP가 내려가면 ESI가 실행 됨 

POP ESI가 될 때 &WinExec를 꺼내어서 ESI에 넣음 

그다음 또 RETN을 하게 되면 이제 ESP는 한 단계 더 내려가서 EBX를 가리킬 것이고 

 

EBX에서 POP EBX가 될 때 0x0000 0005를 꺼내어 EBX에 넣을 것이고

마찬가지로 ret 하게 되면 또 다시 ESP가 내려가서 이번엔 PUSHAD를 가리킨다.

 

PUSHAD하면 EAX~EDI까지 쌓이게 됨

뭔소린가...... 했더니 이거네 

 

PUSHAD는 EDI, ESI, EBX 순으로 스택에 쌓는 명령어지.

 

그렇다면 PUSHAD 딱 하는 순간 

 

ESP-> [EBX] 

[ESI]

[EDI] 가 스택에 쫜 올라가 있는데 

 

스택은 LIFO니까 가장 마지막에 쌓인 EBX부터 POP EBX 하면서 ESP를 내려 가는거네 

 

[EBX] (사라짐)

ESP-> [ESI]

[EDI] 

 

[EBX] (사라짐)

[ESI]  (사라짐)

ESP-> [EDI]

 

 

Windows XP dep 설정 

우선 숨김해제 다 풀고 

C드라이브의 boot.ini -> dep : alwayson

 

[boot loader]

timeout=30

default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS

[operating systems]

multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /noexecute=alwayson /fastdetect

 

Windows 7 이상 

bcdedit.exe /set nx alwayson

 

 

ROP 실습 두둥... 존나 어려워보인다 ㅋㅋㅋㅋ

 

A-PDF All to MP3 Converter 내에 가젯을 사용하여 Stack으로부터 직접 어셈블리어로 계산기를

실행하는 ROP를 디자인해보자.

 

WinExec() 사용하여 계산기 실행 후 ExitProcess()로 프로그램을 정상 종료하도록 설계하자.

이 코드를 구성하는 ROP를 레지스터를 활용하여 그림으로 그려보자.. 와우...

 

 

'0x04 pwnable > 윈도우즈 어플리케이션 취약점 분석' 카테고리의 다른 글

ROP3  (0) 2018.01.17
ROP2  (0) 2018.01.17
SEH Handler 우회 공부  (0) 2018.01.09
SEH Handler 공부  (0) 2018.01.09
SEH 기초공부 1  (0) 2018.01.09