一、漏洞
是计算机系统的硬件、软件、协议在系统设计、具体实现、系统配置或安全策略上存在的缺陷
漏洞是计算机系统本身存在的缺陷,漏洞存在和利用都有一定的环境要求,漏洞本身是没有危害的,只有被攻击者恶意利用,才能给计算机系统带来威胁和损失。
漏洞是攻击的一个入口
二、漏洞分类
根据漏洞形成的原因进行分类
输入验证错误漏洞
缓冲区溢出漏洞
输入的数据超过其规定长度,造成缓冲区溢出,破坏程序正常的堆栈,使程序执行其他指令
设计错误漏洞
设计不合理
意外情况处理错误漏洞
一些意外情况没有考虑到
访问验证错误漏洞
访问验证部分存在错误
配置错误漏洞
配置参数、访问权限、策略安装位置有误
竞争条件漏洞
环境错误漏洞
外部数据被异常执行漏洞
用户在外部非法输入的数据,被系统作为代码解释执行,典型的有SQL注入和XSS等。
根据漏洞的生命周期不同阶段
0day漏洞
还处于未公开状态的漏洞。危害比较大
1day漏洞
通常指发布补丁时间不长的漏洞,但是安装这些补丁的人并不多,这种漏洞仍存在一定的危害
以公开漏洞
大多数用户已经打过补丁的漏洞
。
三、公开的漏洞库
CVE NVD BugTraq CNNVD CNVD
漏洞库提供了操作系统和应用程序特定版本所包含的漏洞信息,部分漏洞的专家建议、修复办法和专门的补丁程序,极少数的漏洞库还提供了检测、测试漏洞的样本验证代码POC
POC (proof of concepts,为观点提供漏洞)
样本验证代码,POC是为了验证漏洞是否存在的代码
CVE 行业标准
CVE (common vulnerabilities and Exposures)通用漏洞列表,相当于软件漏洞的行业标准。
实现了安全漏洞命名机制的规范化和标准化。
NVD 美国的 世界上最大的
NVD 美国国家漏洞数据库
CNNVD
中国国家信息安全漏洞库 隶属于中国信息安全测评中心,测评
CNVD
国家信息安全漏洞共享平台,是国家互联网应急中心CN CERT /CC维护的漏洞库,致力于建设信息安全漏洞共享知识库
针对漏洞的应急响应机构
美国 US-CERT
中国 国家互联网应急中心 CNCERT 或者简称CC
是漏洞数据的主要提供者或漏洞库的主要维护者
四、第一个漏洞 (VC2005)
溢出漏洞 。通过缓冲区溢出来更改返回地址的值,导致程序执行的流程发生改变。
#include<iostream>
using namespace std;
void why_here() {
printf("why u r here?\n");
exit(0);
}
void f() {
int buf[1];
//函数名表示函数在代码空间的地址
//编译器没有对边界值进行检测
buf[2] = (int)why_here;
}
int main() {
f();
return 0;
}
溢出漏洞 造成了返回地址被修改
指令寄存器EIP没有跳转到主函数而是跳转到why_here中去了。
对下列程序进程修改,从而造成溢出漏洞
填*(p+2) 或p[2] (指向的内容进行修改)
原因:局部变量按照声明的顺序来申请空间
修改(p+2)指向的内容
五缓冲区溢出漏洞
缓冲区:缓冲区是一块连续的内存区域,用于存放程序运行时加载到内存的运行代码和数据。
缓冲区溢出:缓冲区溢出是指程序运行时,向固定大小的缓冲区写入超过其容量的数据,多余的数据会越过缓冲区的边界覆盖相邻内存空间,从而造成溢出。
缓冲区溢出产生的根本原因:出于效率的考虑,部分函数不对数组边界条件和函数指针引用进行边界检查。检查交给程序员来进行
1栈溢出
写入数据的长度,大于栈帧的基址ebp到esp之间预留的保存局部变量的空间
栈溢出漏洞的利用
修改返回地址
输入 AAAABBBBCCCCDDDD
修改临接变量的值(落后)
漏洞:在调用strcpy函数时,没有对password进行长度检查,导致buffer申请的空间溢出,覆盖掉了authenticated的部分值
攻击方法:输入一个长度为8位的字符串,这样buffer[8]就变成了0(0是字符串的结束标识符)用来覆盖相邻的局部变量,同时输入的字符串要大于PASSWORD这样局部变量的初值才能是1,用buff[8]来覆盖局部变量10000中的1。
是小端存储
这种方法已经落后了,现在变量之间会有一些随机数,这样溢出时,只会修改随机数的值,不会影响邻接变量
2堆溢出
在堆中发生的缓冲区溢出。栈溢出随着编译器的各种安全措施,越来越难实现。堆溢出已经成为缓冲区溢出的主要方式之一,利用堆溢出可以有效地绕过基于栈溢出的防护措施。
动态申请8个字节的空间,但是在这8个空间中放入24个字节,从而造成了堆溢出,覆盖了堆管理结构
ecx为可分配堆区块的前指针,edi为可分配堆区块的后指针,现在ecx已经被CCCC覆盖edi被DDDD覆盖
mov dword ptr [edi] ,ecx 把ecx中双字型(32位)数据赋给edi
堆溢出 修改了堆管理结构的指针的值,从而造成了可以向任意内存单元写入任意数据
3单字节溢出
是指程序中的缓冲区仅能溢出一个字节,可以用来修改临接变量或者函数返回值(必须紧挨栈帧指针ebp)
格式化串漏洞
什么是格式化串呢?
printf () 等系列函数可以按照一定的格式将数据进行输出
一般形式为
printf( "format",输出列表) //format就是格式化串
在printf中如果只提供了格式化符号串(例如%d %s等)没有提供参数(输出列表),那么函数会将格式化字符
串后面的多个栈的内容弹出来作为参数,并根据格式化符号将其输出。
char *buf 对应的输入类型是诸如 "%x%x%x%x...%x"等
%n不是格式化操作,而是将到%n为止的字符串的长度写入到参数指定的位置
利用%n可以实现像任意地址写入任意的数
例如 sprintf函数的作用是把格式化的数据写入某个字符缓冲区。
int sprintf(缓冲区,格式化字符串,参数)
调用这段程序时,用aaaabbbcc%n作为命令行参数,没有对应的参数,那么栈就会弹出已经读取的字符作为参数,所以把aaaa弹出作为了参数,由于aaaabbbbcc长度为10,那么最终10就会被写入到aaaa对应的地址中去
六、漏洞利用 expliot
Expliot漏洞利用,有漏洞不一定就有利用,但是有漏洞利用expliot就有漏洞
漏洞利用的手段
如何向进程中植入一段用于获得shell的代码,并且将这段植入进程的代码称为shellcode
shell
shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行
实际上shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核。
漏洞利用的核心
漏洞利用的核心就是利用程序漏洞去执行shellcode(shellcode是向进程中植入的代码)以便劫持进程的控制权。要达到该目的,需要通过代码植入的方式来完成,其目的是淹没返回地址,以便劫持进程的控制权,让程序跳转去执行shellcode
漏洞:verify函数没有对输入的字符长度进行检查,可能会发生溢出
漏洞的利用:可以选择覆盖flag的值从而通过验证,也可以选择覆盖返回地址植入代码从而改变程序
掩盖flag
输入48个字符从而淹没flag的值,最后4个字符的值全0,其它字符任意
植入代码
植入方法:直接输入机器代码(语言)(相当于直接输入指令)
最后用buffer的地址来覆盖返回地址是因为,之前输入的机器代码都存放在buffer数组中,当前函数结束后转跳到buffer数组就可以开始执行这些机器代码,从而达到注入的效果
不过winxp之后 上述代码就不能用了,因为只有在winxp中 API的地址才是静态的,也就是说在别的系统中window函数的入口地址都是动态变化的。
高级语言、汇编语言和机器语言区别
机器语言:是二进制代码语言,能够被计算机直接执行。
汇编语言:是通过助记符来表示机器语言的符号语言,汇编语言和机器语言一一对应,一个汇编指令对应唯一的机器代码
高级语言:更加自然地语言,通俗易懂。
通常运行一个由高级语言编写的程序,需要先通过编译器转化为汇编语言,然后汇编语言跟机器语言一一对应,翻译为机器能够直接执行的机器语言。
七shellcode的编写 pwn
编写难题
第一步:先用高级语言书写要执行的shellcode
第二步:换成对应的汇编代码
第三步:根据汇编代码,找到对应地址中的机器码
\x是转义字符,告诉编译器需要用特殊的方式进行处理。\x表示后面的字符是十六进制数,\0表示后面的字符是八进制数。
"\x33" //告诉编译器这个字符是16进制数