selph
selph
发布于 2022-03-21 / 281 阅读
0
0

漏洞学习--栈溢出

基础知识

pe文件里有区段,这里主要用到两种:代码段、数据段

程序在内存中分为两种:栈和堆(一个或多个,堆是可增长的)

常用函数调用约定:cdeclstdcallthiscallfastcall

栈溢出--变量淹没

变量淹没这种情况现实中很少出现,栈溢出一般以控制ebp和返回值为主

简单示例:(VS2022)

#include <iostream>
int main(int argc,int** argv)
{
    int a;
    char b[4];
    scanf("%d",&a);
    scanf("%s",b);
    std::cout <<"output:" << a;
}

在release编译选项下,越先定义的变量会位于栈中越靠近ebp的位置定义在下面的变量会淹没上面的变量):此例中a=1,b=6666,因为b数组的溢出导致a变量被淹没成了0:

image-20211130123059385

代码运行结果:

image-20211130123600369

栈溢出--控制返回地址&手工植入代码

示例代码:(WindowsXP+VC6.0)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WINDOWS.h>

#define PASSWORD "1234567"
int verify_password (char *password)
{
	int authenticated;
	char buffer[44];
	authenticated=strcmp(password,PASSWORD);
	strcpy(buffer,password);//over flowed here!	
	return authenticated;
}
void main()
{
	int valid_flag=0;
	char password[1024];
	FILE * fp;
	LoadLibrary("user32.dll");
	if(!(fp=fopen("password.txt","rw+")))
	{
		exit(0);
	}
	fscanf(fp,"%s",password);
	valid_flag = verify_password(password);
	if(valid_flag)
	{
		printf("incorrect password!\n");
	}
	else
	{
		printf("Congratulation! You have passed the verification!\n");
	}
	fclose(fp);
}

跟变量淹没一样的原理,不过输入更大了,把返回地址也给覆盖了,当程序返回的时候,会跳入我们构造的地址

  • 因为WindowsXP还没有ASLR机制,所以可以在输入处硬编码地址
  • 因为可以在栈中保存更多的数据,所以可以在栈中构造shellcode

因此,可以在返回地址处填写地址跳转到栈中的shellcode处:

正常情况:

image-20211201091605877

覆盖后:

image-20211201091631824

栈中该地址的内容:

image-20211201091659690

这里之前我有个疑惑:为啥栈中除了造成溢出的函数那里之外,再往下还有一份shellcode

实际上那是进入溢出函数之前的函数将其作为参数存在栈中的,如果溢出函数后续代码修改到了shellcode,就可以跳转到之前栈里的shellcode进行执行

这种构造方式存在问题:硬编码获取user32.MessageBoxA函数,该函数地址在不同系统上是不一样的,而且得加载了user32.dll才会有该函数


快速确认缓冲区溢出位置,可以使用 msf 中的 pattern_create.rb(kali linux :/usr/share/metasploit-framework/tools/exploit/pattern_create.rb)脚本来生成随机字符串


评论