一、软件与系统安全基础
威胁模型
定义:对于影响系统安全的所有信息对的结构化表示
本质:从安全的视角解读系统与其环境
理解攻击者:
- 什么可信、什么不可信
- 攻击者的动机、资源、能力
- 攻击造成的影响
举例:
对于一段在email附件中的代码——硬件+系统软件可信,附件中的代码不可信
对于接收客户端请求的web服务器——服务器可信,客户端不可信
安全策略与策略执行
安全策略定义:
- 允许什么/不允许什么
- 谁被允许做什么
策略执行:
- 为了安全策略被遵循,要做什么
- 策略执行方法:利用某些机制
- 如说服、监控和威慑、技术上禁止、激励管理
安全策略的CIA模型:机密性、完整性、可用性
二、技术基础
x86内存模型
分段内存模型:
- 程序内存由一系列独立的地址空间(称为“段”)组成。代码、数据、和栈在不同的段中
- 逻辑地址=段选择器+偏移量
- 物理地址是CPU访问的实际内存位置
- CPU的内存惯例单元将逻辑地址转换为物理地址
指令集与调用惯例
汇编指令风格:
-
AT&T:源在目的前,例:mov $4,%eax
-
Intel:目的在源前,例:mov eax,4
栈帧:将调用函数和被调用函数联系起来的机制
栈帧包括:函数局部变量、向被调用函数传递的参数、栈帧基址帧和返回指令指针
栈帧基址帧:由EBP指向的被调用函数栈帧的固定参考点
返回指令指针:由CALL指令压入栈中的EIP寄存器中的指令地址(CALL的下一条指令的地址)
近调用/近返回——本地函数;远调用/远返回——OS函数或其他进程函数
x86主要调用惯例:
- cdecl:参数从右到左压栈;返回值由EAX返回;调用者清理栈(ESP自增)
- stdcall:被调用者清理栈(RETN n)
- fastcall:使用寄存器ECX、EDX传递参数的前两个参数
代码混淆
概念:通过重构增加代码逆向分析难度的技术
完美混淆器:
- 功能性:混淆前后,功能相同
- 多项式减速:混淆前后,时间和空间开销,最差情况下呈多项式级别放大
- 虚拟黑盒:混淆前后,攻击者无法推断出更多的东西
应用场景:
- 恶意软件通过代码混淆逃过杀毒软件检测和逆向工程师的审查
- 可以看做是静态防篡改技术
- 保护某些关键信息,防止非授权复制
混淆方式:
- 数据混淆
- 常量展开:将常量替换为某个计算过程,计算结果为该常量
- 数据编码方案:混淆时,将x编码为f(x);运行时用到,将f(x)恢复为x
- 基于模式的混淆:将指令映射成具有相同语义的更复杂的指令序列
- 控制流混淆
- 二进制静态分析对控制流的假定:打破一些假定(如CALL指令只用于函数调用、函数返回到CALL指令的后一条指令、条件跳转分支两侧均可能被执行等等)
- 组合使用函数内联与外联:把函数函数解封装或将某一块封装成函数
- 通过跳转破坏局部性:打乱代码原有顺序,通过跳转保持逻辑
- 不透明谓词:这种特殊的条件表达式仅在编译或混淆时已知,作为分支条件,增加冗余分支
- 插入无效代码:在两端有效代码之间插入一些无效代码
- 基于处理器的控制流间接化:用动态计算的分支地址实现混淆
- 控制流图扁平化:将复杂的分支结构由一个单一的分发器结构替代
三、软件漏洞利用与防护
常见的攻击手段
常见攻击手段有栈溢出、整数溢出、堆溢出、格式化字符串
应用程序地址空间内存布局:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1VFs3vs-1688570713539)(D:\typora\photograph\image-20230611003618014.png)]
-
栈溢出
- 缓冲区溢出覆写了程序栈上的数据,允许执行目标机器上的任意代码
-
整数溢出
- 一个整数值增大至超出其允许的最大值,或减小至超出其允许的最小值
- 无符号溢出指潜在的表示已无法表示一个整数值(max再加变成0,0再减变成max)
- 有符号溢出指一个整数值溢出到了符号位(+max再加变成-max,-max再减变成+max)
-
堆溢出
- 空闲堆块中前部分为元数据:前块大小、本块大小、previous指针、next指针
- 移除一个块的操作:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k81aHCM6-1688570713542)(D:\typora\photograph\image-20230611005106323.png)]
- 堆溢出攻击原理:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uARZevBc-1688570713542)(D:\typora\photograph\image-20230611005148965.png)]
-
格式化字符串
-
传入特定的函数并用于指定一个字符串格式的参数
-
攻击原因:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5OBgZKdM-1688570713543)(D:\typora\photograph\image-20230611005352882.png)]
-
格式化字符串中的“%n”
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T42iY4Te-1688570713544)(D:\typora\photograph\image-20230611010026839.png)]
-
防御方法
- 硬编码字符串,不是用包含“%*”的格式化字符串
- 避免使用“printf(arg)”、“%n”
- 使用支持pringf参数与格式化字符串匹配检查的编译器
-
高级攻击手段
常见高级攻击手段有代码注入、代码重用(return-to-libc、ROP)
-
代码注入
- 将ret设置为注入代码的其实地址,被注入代码可以做任何事情(如注入shellcode)
-
代码重用
- return-to-libc
- 定义:用危险的库函数的地址替换返回地址,如system()
- 攻击者需要构造合适的参数
- ROP
- 面向返回的编程,联合现有的代码片段(gadgets),执行任意行为,不需要注入代码
- turning complete:具有条件分支、可以任意修改内存
- 防御
- 非法控制流跳转检测
- 随机化
- return-to-libc
-
代码注入和代码重用的区别
- 代码注入是将execve写到栈上,更改返回地址指针,使之指向写入的代码,即在栈内执行程序,以实现攻击
- 代码重用是更改返回地址指针,使之直接指向execve,即重用可用的代码,以实现攻击
-
代码重用与代码注入的协同
- 先使用代码重用,禁用DEP
- 再使用代码注入
高级防御手段
常见高级防御手段有stack canaries、DEP、ASLR等(均为代码运行时防御)
-
stack canaries
- 由gcc的stackguard实现
- 原理:将一个随机值写到栈上的返回地址前,并在函数返回时检查该值,栈溢出可能会覆写该值,从而被检测到
- 攻击方法
- 若随机地址空间很小,可进行穷举搜索
- 内存泄露,攻击者将可以读取到随机值,从而构造绕过防御
- stackguard扩展——守卫页
- 在一个进程地址空间中关键内存区域之间放置守卫页
- 能失效缓冲区溢出攻击, 特别是对全局数据区的溢出攻击
-
DEP(Data Execution Prevention)
- 数据执行保护:阻止在栈、堆、全局数据区中执行代码
- 代码重用攻击可攻破
-
ASLR(Address space layout randomization)
- 地址空间布局随机化:只有基地址被随机化,在内存对象之间的相对距离不变
- 攻击方法
- 若随机地址空间很小,可进行穷举搜索
- 内存泄露,若攻击者可以读取指向栈的指针值,那就可以发现栈在哪里
防御性编程
- 在开发过程中进行防御
- 预防:
- 使用更安全的编程语言
- 代码评审(费根检查)
- 使用编译器机制,如stackguard
- 安全编程技术
- 使用边界检查库函数,使用更安全的库
- 输入验证:
- “所有输入都是恶意的”
- 必须对接收非可信输入的所有信道进行输入检查
- 最小化攻击面
四、模糊测试
模糊测试原理
在很多随机的、不正常的输入上运行程序,找出程序对这些输入进行响应时的错误行为
生成随机输入->使用随机输入运行大量程序->识别程序的崩溃->将随机输入与崩溃关联
能找到的错误包括:没有检查返回值、数据访问越界、没有检查空指针
模糊测试分类
总体上分为黑盒fuzzing、灰盒fuzzing、白盒fuzzing
黑盒fuzzing:
- 给程序随机输入,观察其是否崩溃
- 优点:容易配置
- 缺点:查找低效
- 低路径覆盖率
- 对于特定路径,可能很难产生输入,如校验和、哈希值等
- 可能导致程序因逻辑原因而终止
- 基于突变的fuzzing
- 用户提供一个良构输入,对于这个输入,生成随机的更改,只假定良构输入变体可能存在问题
- 优点:容易配置
- 缺点:由于初始输入的选择而具有很强偏向性+完全黑盒fuzzing缺点的前两条
- 基于生成的fuzzing
- 用户指定一个格式或协议规范,以生成输入,等价于写一个良构输入生成器
- 优点:更完全
- 缺点:工作量大(获得规范、写专门的输入生成器、每个程序都要重复)
灰盒fuzzing:
- 又称基于覆盖的fuzzing
- 对程序进行插桩,跟踪覆盖率
- 测试流程
- 用户选定的一些初始输入开始
- 对测试用例进行突变,生成新测试
- 运行新测试
- 若新测试能导致新覆盖,则将新测试保存在测试池中,否则抛弃该新测试
- 优点:大体上不需要配置、测试效果更好
- 缺点:输入搜索仍无关与程序内部实现,仍会错过路径
白盒fuzzing:
- 计算出能够导致特定路径执行的输入,使用该输入作为fuzzing输入
- 对测试的生成基于静态分析和符号执行
- 目标:给定一个程序和一个输入参数集,生成一系列能最大化代码覆盖率的输入
模糊测试指标
- 覆盖率:对代码路径和程序逻辑的覆盖
- 错误率:检测出错误的几率
- 时间和资源消耗:消耗的时间和资源
五、软件自我保护
软件防篡改
目标:使得软件的内部逻辑无法被篡改;当篡改发生时,完成自我诊断/修复
方法:
- 内省自检
- oblivious hashing
软件水印
目标:在软件中嵌入用于标识其版权归属的秘密信息
安全性指标:隐藏信息量、隐蔽性、抗攻击能力
水印+程序=>嵌有水印的程序
应用:反盗版——通过声明版权、通过追溯盗版母盘的来源
软件水印的形式分类:静态构造、动态构造
-
静态构造举例:
-
基本块重排序:打乱原有代码顺序,通过跳转维持原有逻辑
-
寄存器占用重分配:不按照正常的寄存器占用规则使用寄存器
-
-
动态构造举例(传统):
- 基于动态生产的图对象
- 基于执行路径上的分支行为
- 基于多线程的同步行为
传统构造的问题:
- 与主程序的关联性很弱
- 往往具有显著的模型/特征
- 很难予以隐藏或伪装
特别设计:基于抽象解释的水印——正常维度服务于软件的原本功能,预设秘密维度展示出隐藏信息
动态构造举例(改进):
- 利用返回导向编程
- 利用代码混淆
- 利用神经网络
仍然存在的问题:
- 没有真正意义上实现隐蔽性
- 缺乏有效的定性/定量评估标准
- 数据嵌入率很差
- 没有解决工业化、自动化实施的问题
软件胎记
目标:对指定程序功能抽象出本质性的特征组合,作为该程序的唯一标识
与软件水印的区别:
- 水印是人为嵌入的声明信息(信息由嵌入者决定)
- 胎记是对软件的某种侧写(信息由软件本身决定)
应用价值:反代码剽窃、检测恶意代码、检测重包装
构造形式分类:静态构造、动态构造
- 静态构造举例
- 基于java的栈行为模式
- 动态构造举例
- 基于执行路径
- 基于程序内的系统调用
软件胎记的不足之处:
- 基于相似度,不完全准确
- 需要有标准样本作为比较依据
- 离线工作,防范存在滞后性
六、渗透测试
SQL注入
Web应用的基本交互过程都是由前端页面、后台服务器处理代码以及数据库三部分组成的
定义:攻击者通过把sql命令插入到Web表单或页面请求的查询字符串中,欺骗服务器执行恶意的sql命令。
防范:输入过滤与验证、参数化查询、最小权限原则
XSS
定义:攻击者嵌入恶意脚本代码到正常用户访问的页面中,当正常用户访问该页面时,被嵌入的恶意代码执行,从而导致攻击被执行。
分类:反射型XSS、存储型XSS、DOM型XSS
防范:设置cookie的http-only属性、CSP内容安全策略、对输入输出进行过滤
七、恶意代码的机理及其防护
木马
定义:一段能实现有用的或必须的功能的程序,但是同时还完成一些不为人知的功能,是被用作网络系统入侵的重要工具和手段。
木马的危害:管理对方文件资源、监控对方主机行为、控制对方键盘鼠标
木马分类:破坏型、密码发送型、远程访问型
木马特点:有效性、隐蔽性、顽固性、易植入性
木马实现原理:
- 向目标主机植入木马
- 启动和隐藏木马
- 目标主机和客户端建立连接
- 进行远程控制
植入:分为主动植入和被动植入
主动植入:本地安装、远程安装
被动植入:网页浏览植入、电子邮件植入、网络下载植入
自动加载:修改启动文件夹、替换系统dll、修改计划任务
隐藏技术:设置窗口不可见、将木马程序注册为服务、使用系统服务端口、替换系统驱动
远程监控功能:获取目标机器信息、记录用户事件、远程操作
木马实例:back orifice、subseven、国产冰河
病毒、蠕虫、木马差异:
- 病毒需要有宿主软件、能自我繁殖、以破坏为主要目的
- 蠕虫不需要宿主软件、能自我繁殖、以破坏为主要目的
木马分类:破坏型、密码发送型、远程访问型
木马特点:有效性、隐蔽性、顽固性、易植入性
木马实现原理:
- 向目标主机植入木马
- 启动和隐藏木马
- 目标主机和客户端建立连接
- 进行远程控制
植入:分为主动植入和被动植入
主动植入:本地安装、远程安装
被动植入:网页浏览植入、电子邮件植入、网络下载植入
自动加载:修改启动文件夹、替换系统dll、修改计划任务
隐藏技术:设置窗口不可见、将木马程序注册为服务、使用系统服务端口、替换系统驱动
远程监控功能:获取目标机器信息、记录用户事件、远程操作
木马实例:back orifice、subseven、国产冰河
病毒、蠕虫、木马差异:
- 病毒需要有宿主软件、能自我繁殖、以破坏为主要目的
- 蠕虫不需要宿主软件、能自我繁殖、以破坏为主要目的
- 木马不能自我繁殖,目的多样性