漏洞利用&渗透测试基础

理解恶意代码的本质与区别:
病毒:
蠕虫:
木马:

两个问题:
1、为啥只是点了一个url啥都没做就中木马?
a:主要有缓冲区溢出等漏洞的存在,执行了以外的代码,在不知情的情况下下载木马并且运行
2、word、excel、ppt等非可执行文件会不会导致恶意代码执行?
a:会的,与1同理。

堆栈基础:
【一个进程可能会被分配到不同的内存区域执行】
进程使用的内存分为:
1、代码区:二进制代码,cpu到这里取指令并执行
2、数据区:存全局变量
3、堆区:进程在此动态申请内存,用完后还。(动态分配和回收)
4、栈区:动态存储函数之间的调用关系,保证被调用函数返回时恢复到母函数中继续执行。

堆区和栈区:
栈(stack):是向低地址扩展的数据结构,是连续的内存分区,栈顶地址跟栈的最大容量是系统预先规定好的,其中windows下默认大小为2MB,当申请超过剩余空间就会报溢出
堆(heap):向高地址扩展,是不连续的内存区域,堆得大小跟计算机的虚拟内存有关。操作系统有记录空闲内存地址的链表【此处请回忆操作系统关于内存的分配与回收】

二进制文件:
PE文件格式【pe是win32可执行文件遵守的数据格式,如*.exe/*dll等】
pe文件格式把可执行文件分成若干数据节(section)
.text:由编译器产生,存放二进制机器码,是反汇编和调试的对象
.data:初始化的数据块,宏定义、全局变量、静态变量等
.idata:可执行文件所使用的动态链接库等外来函数与文件信息
.rsrc:存放程序的资源,如图标等

加壳:可执行程序资源压缩。
加壳是用来保护文件的,加壳过的程序可以直接运行,但不能查看源代码,经过脱壳以后才可以查看源代码。
加壳虽然增加了cpu负担,但是减少了硬盘读写时间(大部分情况)加壳工具通常分为两种:压缩壳和加密壳
压缩壳:减小软件体积大小,加密保护不是重点
加密壳:有些单纯保护程序,有些附加功能,如提供注册机制、使用次数、时间限制。

漏洞:
漏洞的分类:
一、按被攻击者利用的地点
1、本地利用漏洞
2、远程利用漏洞
二、根据形成原因:
1、输入验证错误漏洞
2、缓冲区溢出漏洞:向程序缓冲区输入超过数据长度的数据导致溢出从而破坏对战,使得程序运行其他指令。
3、设计错误漏洞
4、访问验证漏洞
5、配置错误漏洞
6、意外情况处置错误漏洞:没有考虑到处理逻辑。
7、竞争条件漏洞:程序处理文件等实体的时候,在时序和同步方面存在缺陷,导致攻击者可以利用存在的机会窗口施以外来影响。
8、环境错误漏洞
9、外部数据被异常执行漏洞:攻击者在外部输入的非法数据,被系统作为代码解释并且执行,如sql、xss。
三、根据漏洞生命周期不同阶段:
1、0day
2、1day
3、已公开漏洞
四、危险等级划分
1、第一级:紧急
2、第二级:重要
3、第三级:中危
4、第四级:低危

漏洞库
CVE
NVD
CNNVD
CNVD
BugTrag

缓冲区溢出漏洞一些案例
1、vc6程序的例子,书本p52.
主函数没有调用why_here但还是可以执行,因为发生了溢出。在f中声明了数组buff长度为1,但就像我们平时不细心的时候不对下标进行校验,程序就对数组外的内存进行了读写。
buff是静态数组,buff的值是数组在内存的首地址(你看在java里直接打印数组也是打印出数组的地址。),而int buff【1】意味着开辟4字节的整数数组空间,那么buff【2】原本指向是恢复到main函数的地址,如果被其他函数的地址覆盖,那么会进行一个跳转。
2、验证密码的案例,课本p55
密码根据一个返回值判断验证结果,当输入的密码超过了7个字符,越界字符的ascii码会修改掉某些值,比如认证的值,那么如果溢出的数据刚好把认证的值修改成允许通过的值的话,那么程序流程就会改变。

常见漏洞:
一、缓冲区溢出漏洞
缓冲区是一块连续的内存区域,用于存放运行时加载到内存的运行代码和数据。而缓冲区溢出就好比 你把写有你名字的卡片丢到一个快装满的箱子,然后装不下的部分掉到了旁边一个叫做“优秀学生人选”的箱子里,你就一不小心变成了一个优秀学生奖的人选。
缓冲区溢出产生的原因是人没有很好的检查、约束。还有就是缺乏类型安全功能的程序语言(c、c++为了效率部分函数(strcpy、strcat、sprintf、gets等)不对数组边界条件和函数指针引用进行边界检查
措施:
栈随机化
栈破坏检测
限制可执行代码区域

1、栈溢出漏洞
被调用的子函数中写入的数据的长度,大于栈帧的基址到ESP之间预留的保存局部变量的空间时,发生栈溢出。因为数据是从低地址往高地址增长,多出的数据就会越过栈帧的基址,覆盖基址以上的空间。
那么如果多余部分是无效地址,程序会失败,当如果是恶意程序的地址,就会执行恶意程序。
【我想这漏洞的应用应该是用于绕过一些权限执行代码】
2、堆溢出
堆溢出比栈溢出难度更大,但也成为了缓冲区溢出的主要攻击方式,因为可以绕过预防栈溢出的机制。
3、单字节溢出
指的是缓冲区溢出仅能溢出一个字节
通过覆盖堆栈中的返回地址,使程序跳转到shellcode或者指定程序执行。

二、格式化字符串漏洞
与栈溢出相似,最早出现在2000年,是c语言程序常见的攻击方式。
原理:格式化字符串,如c语言print()等函数
print()格式:printf(“format”,输出列表),format的结构为%[标志][输入最小宽度][.精度][长度]类型
类型如下:
%d整形输出,%ld长整型输出
%o以八进制数输出整数
%x以十六进制数输出整数
%u以十进制输出unsign型数据(无符号数)
%c输出字符
%s输出字符串
%f输出浮点,也就是小数
———–

如果调用如printf这些函数,给出了格式化字符串,没有提供实际对用的参数,这些函数就会将格式化字符串后面的多个栈中的 内容 弹出作为参数,并根据格式化符号将其输出。
example:C语言
#include<stdio.h>
int main(void){
int a=1,b=2,c=3;
char buf[]=”test”;
printf(“%s %d %d %d\n”,buf,a,b,c);
return 0; }
———————-
debug: test 1 2 3
———————-
当我们增加一个参数的时候如printf(“%s %d %d %d %x\n”,buf,a,b,c);
———————-
debug:test 1 2 3 c30000
这里c30000是因为%x没有对应的参数,所以自动在栈区找下一个地址作为参数输入

可以相当于是任意内存的读取。

利用%n格式符写入数据
%n:把前面已经打印的长度写入某个内存地址
example:c语言
#include<stdio.h>
main()
{
int num=66666666;
printf(“before:num=%d\n”,num);
printf(“%d%n\n”,num,&num);//第一个参数是num的值,第二个参数是num的地址
printf(“after:num=%d\n”,num);
}
————————-
debug:
before:num=66666666
66666666
after:num=8
————————–
这说明了确实可以写入数据到内存,那么当写入的数据是一个函数的返回地址就可以控制程序的使用流程。不过需要使用printf的特性:自定义打印字符串宽度
在格式符中间加上一个十进制整数来表示输出的最少位数,如果实际要比定义多,就按照实际,实际要少的话就会补0
—————————–
修改一下第二条 printf(“%100%n\n”,num,&num);
结果就是num会被改成100.
因此要想写入地址,比如0x8048000,其对应十进制:134512640作为控制宽度就行
如:printf(“%0134512640d%n\n”num,&num);
debug之后num的值为 8048000

三、整数溢出漏洞
我们知道,整数可以分为有符号数和无符号数
有符号数正数最高位:0
有符号数负数最高位:1
当进行运算时结果如果大于该类型的表示范围,就会发生溢出。
整数溢出可以分为三个大类:
1、存储溢出
使用另外的数据类型来存储整型数。如把大的变量放到小的变量的存储区域,最后只能保存小变量可以储存的位。
2、运算溢出
对整形变量进行运算时没有考虑边界范围,造成运算后的数值范围超出空间
3、符号问题
由于在开发过程中一般长度变量使用无符号整型数,当程序员忽略了符号,进行安全检查的时候会容易出现问题。

——–软件防护技术————————————–

1.ASLR(Address Space Layout Randomization)地址空间分布随机化
通过将系统关键地址随机化,使攻击者无法获得需要跳转的精确地址的技术。函数的地址一般是系统DLL、可执行文件本身、栈数据或者PEB(process environment block)中的固定地址。
ASLR随机化的关键地址包括:PE文件映像地址、堆栈基址、堆地址、PEB和TEB(thread environment block)地址等。
当程序启动将执行文件挂加载到内存,os通过内核提供该功能,在原来的映像基址上加随机数为新的基址。随机数的范围为 1~254 .
2.GS Stack protection
一项缓冲区溢出的检测防护技术。当编译器有这个功能时,编译器针对函数调用和返回时添加保护和检查功能的代码,在函数被调用时,在缓冲区和函数返回地址增加了一个32位的随机数security_cookie,返回时检查cookie有无变化。
3.DEP(data execute prevention)数据执行保护
限制内存堆栈区的代码为不可执行状态,防止溢出后的代码执行。
window中将包含执行代码和dll文件的txt段既代码段的内存区域设置成可执行代码的内存区域,其他区域不包含执行代码。
然而在xp以及之前的os没有做出限制。
启动DEP之后会将敏感区域设置为不可执行的non-executable标志位。因此跳转了也不能执行就防止了攻击。
4.safeSEH
SEH(structured exception handler)是windows异常处理机制所采用的重要数据结构链表。程序设计者diy定义程序发生异常的时候的应对函数,保存在SEH中。如果攻击者构造的代码通过溢出覆盖SEH中异常处理函数句柄,会发生执行攻击指令的情况。
safeSEH用来保护SEH。当该选项启动,程序会将pe文件中合法的SEH异常处理函数的地址解析出来制成一张SEH函数表,放于PE文件的数据块中,用于异常情况处理时检查。
当发生异常,通过对每个处理异常的函数检查其是否在SEH中,如果没有就是该函数非法,终止异常处理;接着检查句柄是否在栈上,如果在栈上也将终止。
上述两个步骤可以防止在堆上伪造异常链和把shellcode放在栈上的情况,最后还要检测异常处理函数和句柄的有效性。【vista版本开始,pe文件都采取SafeSEH编译选项。】
5.SEHOP(structured exception handler overwrite protection)结构化异常处理覆盖保护
通过对程序使用的SEH结构进行一些安全检测,判断应用程序是否受到了SEH攻击。原理时检测程序栈中所有SEH结构链表的完整性。

——————

漏洞利用技术:
即使开启了微软的应用保护的技术,还是有很多手段绕过或者找到其他利用方式
一、地址利用技术
根据触发的条件不同,内存给调用函数分配内存的方式不同,shellcode植入的地址也不同
1.静态shellcode地址的利用技术
当存在溢出的程序是os自启动的程序,他的内存地址、函数调用时分配的栈帧地址一般都是固定的,此时溢出之后写入栈帧的shellcode的内存地址也是静态。因此可以直接将shellcode代码在栈帧的静态地址直接覆盖到原来的返回【所以为什么有些病毒啊啥的删除了重启还存在】
————————
2.动态变化的shellcode地址利用技术
当漏洞存在于动态链接库中,而这些库会被动态加载,因此每一次执行地址都是动态的。所以要利用漏洞就需要每一次都能定位到shellcode的起始地址。
那么需要利用esp寄存器的特性来解决。
第一步:找到内存中任意一个汇编指令 jmp esp,这条指令执行后可以跳转到esp寄存器保存的地址,准备在溢出后将这条指令的地址覆盖返回地址
第二步:设计好利用缓冲区漏洞的数据,使得覆盖返回地址为jmp esp指令的地址,再覆盖与返回地址相邻的高位地址并写入shellcode
第三步:函数完成后返回,根据返回地址中指向jmp esp指令的地址去执行jmp esp操作,此时出触发前两步设置好的代码,shellcode被执行
【mov eax,esp和jmp eax等也可以实现进入栈区的功能】
—————————
3.heap spray技术
如果有些软件漏洞不能精确定位shellcode,同时加载地址也是动态变化时应该采取heap spray技术了。
heap spray(堆喷洒)是在shellcode前面加上大量的滑板指令(slide code)组成一段很长的注入代码段,然后反复向内存填充,再结合漏洞利用技术是程序跳转到堆中任意一个注入代码的地址,程序指令就会顺着滑板代码最终执行到shellcode代码
滑板指令(slide code)是由大量NOP(no-operation)空指令0x90填充组成的指令序列。当程序遇到这些nop指令,cpu的指令指针会一个一个执行不做任何具体操作,直到到最后一个指令,开始执行之后的指令,执行的往往是shellcode代码。
—————————–

二、绕过DEP保护
因为有些代码需要在不可执行区域执行,所以有时候DEP会出错
微软提供两种DEP配置
Opt-In Mode:DEP只对系统进程和特别定义的进程启用
Opt-Out Mode:对所有进程和服务启用,除了禁用的进程。
基本思想:
ROP(return-oriented programming)返回导向编程,基于代码复用,允许绕过DEP和ALSR但是不能绕过GS。ROP允许攻击者从已有的库或可执行文件中提取指令片段,构建恶意代码
1.rop 通过rop链(retn)实现有序汇编指令的执行
2.rop链由一个一个rop代码块组成
3.rop代码块时由“目的执行指令+retn指令”组成


漏洞挖掘

首先要明白检测漏洞的技术
软件安全检测技术:
1.软件静态安全检测技术
2.软件动态安全检测技术
3.软件动静结合的安全检测技术

静态检测

针对未处于运行状态的软件开展的安全分析测试,一般对源代码和可执行文件。
包括:词法分析、数据流分析、污点传播、符号执行、模型检验、定理证明
1.词法分析:对代码进行基于文本或者字符标识的匹配分析对比,来找到符合特征的危险函数,API等
【工具:checkmarx、ITS4】
2.数据流分析:分析代码的变量的取值的变化和执行的情况,分析流程控制。
【工具:coverity、klockword】
3.污点传播分析:分析代码中输入数据对程序执行路径的影响,来发现不可信的输入数据导致的程序执行异常。
【工具:pixy】
4.符号执行:在不是实际执行的过程中将程序的输入表示为符号,根据执行流程和输入参数的赋值变化,把程序的输出表示成包含这些符号的逻辑或者算数表达式。
【工具:exe、sage、smart】
5.模型检验:将程序执行过程抽象为状态迁移的模型,采用状态迁移过程中的安全属性的验证来判断程序的安全性质。
【工具:slam】
6.定理证明:转化为数学上的定理证明
【误报率低,在静态检测中最为准确】
【工具:saturn】

由于对于软件、可执行文件的检测几乎都是通过反汇编得到的汇编代码进行检测,缺少语义分析,误报率高。因此要先反汇编之后,把汇编代码转化为中间语言,在分析中间语言基础上针对得到的部分语义信息进行缺陷和漏洞的检测。
比较广泛使用的两种中间语言:
1.二进制插装平台binnavi的REIL
2.动态插装平台valgrind的VEX

静态安全检测实践:
IDA Pro是常用的反汇编工具。它提供的一些函数调用信息和代码引用信息,可以定位程序中调用strcpy、memcpy、spritf等危险函数的代码位置,再通过分析传入的参数信息判断是否存在缓冲区溢出漏洞或者是格式化字符串漏洞

动态检测

对于正在运行的程序,通过构造非正常的输入来检测是否出现故障或者崩溃等非正常输出,并通过检测软件运行中的内部状态信息来验证或者检测软件缺陷。动态检测对象是可执行代码,准确率高,误报率低。
主要技术:模糊测试、智能模糊测试和动态污点跟踪

1.模糊测试(fuzzing)是自动化或半自动化的安全漏洞检测技术,通过向目标软件输入大量的畸形数据,观察异常发现潜在的漏洞,属于黑盒测试。
缺点:数据生成比较随机,导致覆盖率不高
工具:SPIKE、Peach

步骤:
1.确认测试对象和输入数据
2.生成模糊测试数据
3.检测模糊测试数据
4.监测程序异常
5.确定可利用性

2.智能模糊测试(最普遍)smart fuzzing
相比模糊,这个方法是引入了基于符号执行等可进行程序理解的方法,有针对性的设计测试用例.

步骤:
1.反汇编:需要对可执行文件进行输入数据、控制流、执行路径之间相关关系分析
2.中间语言转换
3.采用智能技术分析输入数据和执行路径的关系
4.利用分析获得的输入数据集合,对执行路径集合进行测试
工具:TaintScope
【核心思想是用小代价去找最有可能产生漏洞的执行路径集合,避免盲目的对程序进行全路径覆盖测试。】

3.动态污点分析dynamic taint analysis
通过分析内部真实指令的执行过程,追踪数据在程序内部的传递、处理流程、操作是否涉及安全问题从而分析出潜在的漏洞。
工具:bibblaze
外部不可信的数据被打上唯一标签之后可以被跟踪

动态监测实践:
【工具:FileFuzz】
1、非明文格式特殊文件
比如用记事本打开txt能看到明文,但是记事本打开ppt会乱码,但是通过编辑16进制文件是可以做到修改的。
【玩ctf杂项的时候经常需要查看文件16进制格式,看看文件头有没有藏着什么有用的消息,比如图片格式或者加密的标志位判断是真的加密了还是伪加密而已】
工具:010edited、UltraRdit
用自动化工具发掘
2.使用filefuzz
fliefuzz采用字节替换法批量生产待测文档文件,然后将待测文档逐一调用相应的文件处理,并且监视过程中发生的错误,记录错误。
【安装fliefuzz前要安装 .net framework框架组件包】

动静结合

将动态分析和静态分析结合起来对二进制进行分析
工具:bitblaz
BitBlaze由三部分组成:TEMU、Vine、Rudder

TEMU:动态分析模块,实质是一个虚拟机,实现全系统模拟,既可以跟踪用户控件的指令流,也能跟踪进入系统内核,记录指令流和操作数

Vine:静态分析模块,分前端后端,把二进制变成Vine中间语言(VIL),后端用来在VIL做静态分析,比如函数调用图和控制流图等,vine后端模块记录追踪路径的约束条件,可将某个路径约束取反,生成满足新路径约束条件的输入

Rudder:是一个混合进行符号执行和实际执行的模块。
如果检测到符号输入影响到执行路径的选择,模块就会将具体执行和符号执行结合,探测多条可执行路径。