欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

Windows Shellcode学习笔记——通过VirtualProtect绕过DEP

来源:本站整理 作者:佚名 时间:2017-04-12 TAG: 我要投稿

0x00 前言

在掌握了栈溢出的基本原理和利用方法后,接下来就要研究如何绕过Windows系统对栈溢出利用的重重防护,所以测试环境也从xp转到了Win7(相比xp,Win7的防护更全面)。本文将要介绍经典的DEP绕过方法——通过VirtualProtect绕过DEP

0x01 简介

本文将要介绍以下内容:

  • VS2012的编译配置
  • 利用Immunity Debugger的mona插件自动获取ROP链
  • 对ROP链的分析调试
  • 调用VirtualProtect函数时的Bug及修复

0x02 相关概念

DEP:

溢出攻击的根源在于计算机对数据和代码没有明确区分,如果将代码放置于数据段,那么系统就会去执行

为了弥补这一缺陷,微软从XP SP2开始支持数据执行保护(Data Exection Prevention)

DEP保护原理:

数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,而有了DEP,此时CPU会抛出异常,而不是去执行指令

DEP四种工作状态:

  • Optin
  • Optout
  • AlwaysOn
  • AlwaysOff

DEP绕过原理:

如果函数返回地址并不直接指向数据段,而是指向一个已存在的系统函数的入口地址,由于系统函数所在的页面权限是可执行的,这样就不会触发DEP

也就是说,可以在代码区找到替代指令实现shellcode的功能

但是可供利用的替代指令往往有限,无法完整的实现shellcode的功能

于是产生了一个折中方法:通过替代指令关闭DEP,再转入执行shellcode

内存页:

x86系统一个内存页的大小为4kb,即0x00001000,4096

ROP:

面向返回的编程(Return-oriented Programming)

VirtualProtect:

BOOL VirtualProtect{ LPVOID lpAddress, DWORD dwsize, DWORD flNewProtect, PDWORD lpflOldProtect }

lpAddress:内存起始地址 dwsize:内存区域大小 flNewProtect:内存属性,PAGE_EXECUTE_READWRITE(0x40) lpflOldProtect:内存原始属性保存地址

通过VirtualProtect绕过DEP:

在内存中查找替代指令,填入合适的参数,调用VirtualProtect将shellcode的内存属性设置为可读可写可执行,然后跳到shellcode继续执行

0x03 VS2012的编译配置

测试环境:

  • 测试系统: Win 7 x86
  • 编译器: VS2012
  • build版本: Release

项目属性:

  • 关闭GS
  • 关闭优化
  • 关闭SEH
  • 关闭DEP
  • 关闭ASLR
  • 禁用c++异常
  • 禁用内部函数

具体配置方法:

配置属性-c/c++-所有属性

  • 安全检查 否(/GS-)
  • 启用c++异常 否
  • 启用内部函数 否
  • 优化 已禁用(/Od)

配置属性-链接器-所有属性

  • 数据执行保护(DEP) 否(/NXCOMPAT:NO)
  • 随机基址 否(/DYNAMICBASE:NO)
  • 映像具有安全异常处理程序 否(/SAFESEH:NO)

0x04 实际测试

测试1:

测试代码:

char shellcode[]=
	"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
	"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
	"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
	"\x41\x41\x41\x41\x42\x43\x44\x45";

void test()
{
	char buffer[48];
	memcpy(buffer,shellcode,sizeof(shellcode));
}

int main()
{
	printf("1\n");
	test();
	return 0;
}

注:

strcpy在执行时遇到0x00会提前截断,为便于测试shellcode,将strcpy换成memcpy,遇到0x00不会被截断

如上图,成功将返回地址覆盖为0x45444342

测试2:

shellcode起始地址为0x00403020

PUSH 1  
POP ECX

对应的机器码为 0x0059016A

将返回地址覆盖为shellcode起始地址

shellcode实现如下操作:

PUSH 1
POP ECX

其他位用0x90填充

c代码如下:

char shellcode[]=
	"\x6A\x01\x59\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
	"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
	"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
	"\x90\x90\x90\x90\x20\x30\x40\x00";

void test()
{
	char buffer[48];
	memcpy(buffer,shellcode,sizeof(shellcode));
}

int main()
{
	printf("1\n");
	test();
	return 0;
}

如上图,shellcode成功执行,ECX寄存器赋值为1

测试3:

开启DEP,再次调试,发现shellcode无法执行,如图

测试4:

下载安装Immunity Debugger

下载mona插件,下载地址如下:

https://github.com/corelan/mona

将mona.py放于C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands下

启动Immunity Debugger,打开test.exe

使用mona插件自动生成rop链,输入:

!mona rop -m *.dll -cp nonull

如图

[1] [2] [3]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载