文章源自【字节脉搏社区】-字节脉搏实验室
作者-Jadore
栈:先进后出
动态调试下的栈:
几个寄存器:
EAX:函数执行完后的返回结果
ECX:计数器
EDX、EBX:计算器
ESP:指向栈顶的指针
EBP:指向栈底的指针
ESI、EDI:源地址,目的地址寄存器
EIP:存储CPU要读取指令的地址
莫里斯蠕虫(Morris worm):
https://baike.baidu.com/item/%E8%8E%AB%E9%87%8C%E6%96%AF%E8%A0%95%E8%99%AB/9035909?fr=aladdin
缓冲区溢出(Buffer Overflow|Buffer Overrun):
由于程序设计时缺乏对缓冲区(Buffer)的边界进行检查而导致在向缓冲区写入超过其本身的数据时而引起的异常,一般会对相邻的内存区域进行覆盖,通常会被利用执行恶意代码获取系统权限。
产生缓冲区溢出的几种可能:
1.数组索引不在合法范围内
通常我们可能比较多地注意到数组的上界不应被超过,而往往却不太会在意数组的下界,来看到这个例子:
#include<stdio.h>
int main()
{
int position=-1;
int value=100;
int array[10];
if(position >= 10)
{
return -1;
}
array[position]=value;
return 0;
}
2. 没有保证足够的存储空间存储复制过来的数据:
strcpy是一个不安全的函数,看这个例子:
#include<stdio.h>
int main()
{
int array[]={1,2,3,4,5,6,7,8,9};
int array2[9];
strcpy(array2,array);
return 0;
}
而莫里斯蠕虫利用的不安全函数为gets,gets函数与strcpy函数一样,并没有检查数组越界的功能,当然除了这两个,还有诸如strcat(),strncpy(),strncat(),甚至输入函数scanf()也并不安全,对应的有更加安全的函数,即在函数名后加上_s,如scanf_s()函数
3. 整数溢出:
整数溢出可分为宽度溢出和算术溢出
宽度溢出:把一个宽度较大的操作数赋给宽度较小的操作数,就有可能发生数据截断或符号位丢失(分别对应以下两个例子):
#include<stdio.h>
int main()
{
int a=1;
float b=3.14;
int c;
c=a+b;
return 0;
}
#include<stdio.h>
int main()
{
signed int value1 = 10;
usigned int value2 = (unsigned int)value1;
}
算术溢出,该程序即使在接受用户输入的时候对a、b的赋值做安全性检查,a+b依旧可能溢出:
#include<stdio.h>
int main()
{
int a;
int b;
int sum=a+b;
return 0;
}
4. 空字符错误:
当’\0’用%c输出时是空格,而用%d输出时是0
#include<stdio.h>
int main()
{
char array[]=”1”;
printf(“%d”,str[1]);
return 0;
}
还有一种遍历数组的情况:
#include<stdio.h>
int main()
{
char str[10]=”0123456789″;
int i;
for(i=0;str[i]!='\0';++i)
{
printf(“str[%d]=%c\n”,i,str[i]);
}
printf(“str[%d]=%d\n”,i,str[i]);
return 0;
}
可以看到,遍历数组时超过了数组边界,并试图读取超出数组边界之外的数据,直到程序读取到一个空字符之后才停止:
还有一种情况会丢失空字符结束符:
char array[]={'0','1','2','3','4','5','6','7','8'};
正确的写法应为:
char array[]={'0','1','2','3','4','5','6','7','8',’\0’};
或者char array[11]={'0','1','2','3','4','5','6','7','8','9’};
这里看看相关的缓冲区溢出漏洞ms08_067:
ms08_067是在执行路径规范化时函数返回地址被覆盖从而导致的任意代码执行的目的,ms08_067的攻击模块已经集成到metasploit中,直接看到exploit函数,首先使用connect函数建立TCP连接,然后进行SMB会话连接,给相关的参数赋值。自动探测时,通过smb_fingerprint函数获取信息
2表示构建恶意路径,先是初始化一些变量,包括填补字符串padder = [*(‘A’..’Z’)],服务器名称server以及前缀prefix、路径path:
target部分,定义赋值了变量Ret、Scratch,变量Ret的值对应SVCHOST.EXE系统文件中的JMP ESI指令地址,该exploit利用ESI寄存器中指向栈空间的地址,覆盖返回地址,因无法直接进行覆盖,因此需要通过JMP ESI指令进行跳转,最终再执行栈中的shellcode:
首先构造含跳转地址的填充字符串jumper,初始化长度为70字节,内容为’A’–’Z’的随机字符串,从第5字节起填充4字节由初始化变量Ret定义的返回地址6,从第51字节起填充8字节的空指令和2字节的跳转指令”\xeb\x62”:
接着,生成恶意路径path(8),由8个部分组成,其中包括编码的Shellcode、触发漏洞的unicode相对路径’\\..\\..’、填补字符串pad、EBP栈基址、RET返回地址、跳转指令jumper、字符串结尾”\x00” * 2:
最后只需要与目标主机正常交互发送这些数据,尝试一次攻击得到一个反向shell: