当前位置: 首页 > news >正文

小程序在哪个网站做工程造价信息网官网首页

小程序在哪个网站做,工程造价信息网官网首页,江苏网站建设系统服务,商丘网站设计译者#xff1a;飞龙 协议#xff1a;CC BY-NC-SA 4.0 MIT 6.858 计算机系统安全笔记 2014 秋季 2014 年由Nickolai Zeldovich 教授和James Mickens 教授教授授课的 6.858 讲座笔记。这些讲座笔记略有修改#xff0c;与 6.858 课程网站上发布的内容略有不同。 第1讲#x… 译者飞龙 协议CC BY-NC-SA 4.0 MIT 6.858 计算机系统安全笔记 2014 秋季 2014 年由Nickolai Zeldovich 教授和James Mickens 教授教授授课的 6.858 讲座笔记。这些讲座笔记略有修改与 6.858 课程网站上发布的内容略有不同。 第1讲介绍什么是安全意义何在没有完美的安全策略威胁模型假设机制缓冲区溢出 第2讲控制劫持攻击缓冲区溢出栈金丝雀边界检查电子围栏胖指针影子数据结构Jones Kelly松松边界检查 第3讲更多松松边界和返回导向编程边界检查的成本不可执行内存地址空间布局随机化ASLR返回导向编程ROP堆栈读取盲目 ROP小工具 第4讲OKWS特权分离Linux 自主访问控制DACUIDsGIDssetuid/setgid文件描述符进程Apache web 服务器chroot 监狱远程过程调用RPC 第5讲渗透测试 嘉宾讲座 由 Paul YouniSEC Partners 第6讲Capsicum混淆副手问题环境权限能力沙盒自主访问控制DAC强制访问控制MACCapsicum 第7讲Native Client (NaCl)沙盒化 x86 本机代码软件故障隔离可靠的反汇编x86 分段 第8讲Web 安全第一部分现代网络浏览器同源策略框架DOM 节点cookies跨站请求伪造CSRF攻击DNS 重绑定攻击浏览器插件 第9讲Web 安全第二部分跨站脚本XSS攻击XSS 防御SQL 注入攻击Django会话管理cookiesHTML5 本地存储HTTP 协议的歧义隐蔽通道 第10讲符号执行 嘉宾讲座 由 Armando Solar-Lezama 教授MIT CSAIL 第11讲Ur/Web 嘉宾讲座 由 Adam Chlipala 教授MITCSAIL 第12讲TCP/IP 安全威胁模型序列号和攻击连接劫持攻击SYN 洪水攻击带宽放大攻击路由 第13讲KerberosKerberos 架构和信任模型票证认证者票证授予服务器更改密码复制网络攻击前向保密性 第14讲ForceHTTPS证书HTTPS在线证书状态协议OCSPForceHTTPS 第15讲医疗软件 嘉宾讲座 由 Kevin Fu 教授密歇根大学 第16讲时序攻击侧信道攻击RSA 加密RSA 实现模指数运算赛里斯剩余定理CRT重复平方蒙哥马利表示卡拉茨巴乘法RSA 蒙蔽其他时序攻击 第17讲用户认证你拥有什么你知道什么你是谁密码挑战-响应可用性部署性安全性生物特征多因素认证MFA万事达卡的 CAP 读卡器 第18讲私密浏览私密浏览模式本地和网络攻击者虚拟机级隐私操作系统级隐私浏览器实现了什么浏览器扩展 第19讲Tor 客座讲座由 Nick Mathewson 主讲Tor 项目 2012 年的 6.858 课程笔记关于匿名通信洋葱路由Tor 设计Tor 电路Tor 流Tor 隐藏服务阻止 Tor用餐密码学家网络DC-nets 第20讲手机安全Android 应用程序活动服务内容提供者广播接收器意图权限标签参考监视器广播意图 第21讲信息流跟踪TaintDroidAndroid 数据泄漏信息流控制污点跟踪污点标志隐式流x86 污点跟踪TightLip 第22讲麻省理工学院信息服务与技术部 客座讲座由 Mark Silis 和 David LaPorte 主讲麻省理工学院信息服务与技术部 第23讲安全经济学网络攻击的经济学垃圾邮件价值链广告点击支持实现CAPTCHA僵尸网络支付协议道德 2015 年的新笔记 第8讲英特尔软件保护扩展SGX隔离Iago 攻击飞地证明Haven 论文 我们阅读的论文列表papers/ 松松垮垮的边界检查 盲目黑客 OKWS 困惑的代理或者为什么可能发明了能力 辣椒 (capabilities) 本地客户端沙箱化 x86 代码 OWASP 前十名最关键的 Web 应用程序安全风险 KLEE符号执行 Ur/Web面向 Web 的函数式编程 回顾“TCP/IP 协议套件中的安全问题” 凯伯罗斯用于开放网络系统的认证服务 ForceHTTPs 值得信赖的医疗设备软件 远程时序攻击是可行的 替代密码的探索 私密浏览模式 Tor第二代洋葱路由器 了解 Android 安全 TaintDroid一种用于智能手机实时隐私监控的信息流跟踪系统 点击轨迹垃圾邮件价值链的端到端分析 “新” 论文 Iago Attacks: 为什么系统调用 API 是一个糟糕的不受信任的 RPC 接口 其他论文 赛里斯防火墙安全政策 介绍 **注意**这些讲座笔记略有修改来源于 2014 年 6.858 课程网站上发布的内容课程网站。 什么是安全性 在对手存在的情况下实现某个目标。 许多系统连接到互联网而互联网上存在对手。 因此许多系统的设计可能需要考虑安全性。 即在有对手的情况下系统是否能正常工作 高层次的安全性思考计划 **政策**您想要实现的目标。 例如只有艾丽丝应该读取文件F。 *常见目标*保密性完整性可用性。 **威胁模型**对攻击者可能做什么的假设。 例如可以猜测密码但无法物理抓取文件服务器。 最好假设攻击者可以做某事。 **机制**系统提供的旋钮帮助维护政策。 例如用户帐户密码文件权限加密。 **最终目标**在威胁模型内部对手无法违反政策。 请注意目标与机制无关。 为什么安全性很难这是一个负面目标。 对比检查是否实现了积极目标很容易例如艾丽丝实际上可以读取文件F。更难的是检查没有可能的方式让艾丽丝读取文件F。 你甚至如何开始列举艾丽丝可能读取文件的所有可能方式艾丽丝可以利用多少层次的漏洞来获取文件F的访问权限。 需要保证政策假设威胁模型。 很难想象攻击者可能如何入侵的所有可能方式。 现实的威胁模型是开放式的几乎是负面模型。 最弱的环节很重要。 迭代过程设计根据需要更新威胁模型等。 如果我们无法实现完美的安全性那有什么意义呢 在这门课程中我们将推动每个系统的边界看看它何时会崩溃。 每个系统可能都会有一些导致妥协的破坏点。 这并不一定意味着系统没有用这取决于上下文。 重要的是了解系统能做什么以及系统不能做什么。 实际上必须管理安全风险与收益。 更安全的系统意味着某些妥协的风险或后果更小。 不安全的系统可能需要手动审计以检查攻击等。 攻击成本越高将有更多的对手被阻止。 更好的安全性通常使新功能变得实用和安全。 假设你想在系统上运行某些应用程序。 大公司有时会禁止用户在其台式机上安装未经批准的软件部分原因是出于安全考虑。 浏览器中的 Javascript 是隔离的这使得运行新代码/应用程序而无需手动检查/批准变得可以接受或虚拟机或本地客户端或更好的操作系统隔离机制。 同样VPN 使得减轻允许员工从互联网的任何地方连接到公司网络的风险变得实际可行。 出现问题的原因之一政策问题 例子 萨拉·佩林的电子邮件账户。 雅虎电子邮件账户有用户名、密码和安全问题。 用户可以通过提供用户名和密码登录。 如果用户忘记密码可以通过回答安全问题来重置。 安全问题有时比密码更容易猜到。 一些对手猜到了萨拉·佩林的高中、生日等信息。 政策总结为可以使用密码或安全问题登录。 无法强制执行“只有用户忘记密码然后…” 例子 马特·霍南在亚马逊、苹果、谷歌等处的账户 Gmail 密码重置向备用电子邮件地址发送验证链接。 谷歌贴心地打印了备用电子邮件地址的一部分。 马特·霍南的备用地址是他的苹果me.com账户。 苹果密码重置需要账单地址信用卡的最后 4 位数字。 地址可能很容易获取但如何获取用户信用卡号的 4 位数字 亚马逊可以向账户添加信用卡无需密码。 亚马逊密码重置提供用户的任意一张信用卡号。 哈哈。 亚马逊不会打印信用卡号… 但会打印最后 4 位数字 例子 Twitter 的N账户劫持。 对于合法用户来说有时很难证明他们拥有一个账户 如何解决 仔细思考政策声明的含义。 一些政策检查工具可以帮助但需要一种指定不良内容的方法。 在分布式系统中很困难不知道每个人在做什么。 出了什么问题 #2威胁模型/假设问题 例子 未考虑人为因素。 钓鱼攻击。 用户收到一封要求续订电子邮件账户、转账或…的电子邮件。 技术支持接到一个声音很像用户的电话要求重置密码。 “橡皮管解密” 例子 计算假设随时间变化。 麻省理工学院的 Kerberos 系统使用 56 位 DES 密钥自 20 世纪 80 年代中期以来。 当时似乎可以假设对手无法检查所有 2⁵⁶ 个密钥。 不再合理现在大约需要 100 美元。 几年前6.858 期末项目表明可以在一天内获取任何密钥。 例子 所有 SSL 证书 CA 都是完全受信任的。 要连接到启用 SSL 的网站Web 浏览器会验证证书。 证书是服务器主机名和加密密钥的组合 由某些受信任的证书颁发机构CA签名。 浏览器信任的证书颁发机构CA长列表数百个。 如果任何 CA 受到损害对手可以拦截 SSL 连接。 为任何服务器主机名创建一个“伪造”证书。 2011 年两个 CA 受到损害为许多域谷歌、雅虎、Tor 等签发了伪造证书显然在伊朗使用。 en.wikipedia.org/wiki/DigiNotar en.wikipedia.org/wiki/Comodo_Group 2012 年一家 CA 无意中签发了一个适用于任何域的根证书。 www.h-online.com/security/news/item/Trustwave-issued-a-man-in-the-middle-certificate-1429982.html *例子*假设你的硬件是可信的。 如果 NSA 是你的对手结果可能并不是一个好的假设。 www.schneier.com/blog/archives/2013/12/more_about_the.html *例子*假设密码学中有良好的随机性。 需要高质量的随机性来生成无法被猜测的密钥。 问题嵌入式设备、虚拟机可能没有太多的随机性。 结果许多密钥相似或容易被猜测攻击。 挖掘你的 P 和 Q检测网络设备中普遍存在的弱密钥 *例子*颠覆军事操作系统安全。 在 80 年代军方鼓励研究安全操作系统。 操作系统被破坏的一个意想不到的方式 对手获取了开发系统的访问权限修改了操作系统代码。 *例子*颠覆防火墙。 对手可以连接到防火墙后面的不安全无线网络。 对手可以欺骗防火墙后面的用户来禁用防火墙。 可能只需点击链接http://firewall/?actiondisable就足够了。 或者也许在 CNN.com 购买广告指向那个 URL有效 *例子*断开与互联网连接的机器安全吗 Stuxnet 蠕虫通过 USB 驱动器上的特制文件传播。 如何解决 更明确的威胁模型以了解可能存在的弱点。 更简单、更通用的威胁模型。 更好的设计可能会消除/减少对某些假设的依赖。 例如不依赖完全信任 CA 的替代信任模型。 例如不容易受到钓鱼攻击的身份验证机制。 问题出在哪里 #3机制问题–漏洞 安全机制中的漏洞例如操作系统内核会导致漏洞。 如果应用程序正在执行安全性应用程序级别的错误会导致漏洞。 例子苹果的 iCloud 密码猜测速率限制。 人们经常选择弱密码通常可以在几次尝试1K-1M后猜中。 大多数服务包括苹果的 iCloud会对登录尝试进行速率限制。 苹果的 iCloud 服务有许多 API。 一个 API“查找我的 iPhone”服务忘记实现速率限制。 对手可以多次尝试猜测密码。 可能和他们发送数据包的速度一样快 M/day. 例子花旗集团信用卡网站缺失访问控制检查。 花旗银行允许信用卡用户在线访问他们的账户。 登录页面要求输入用户名和密码。 如果用户名和密码正确将重定向到账户信息页面。 账户信息页面的 URL 包含一些数字。 结果这些数字与用户的账号相关。 更糟糕的是服务器没有检查您是否已登录到该帐户。 对手尝试不同的数字获取不同人的账户信息。 可能是错误的威胁模型与现实世界不匹配 如果对手通过浏览器浏览网站系统是安全的。 如果对手自己合成新的 URL系统就不安全了。 很难说开发人员是否有错误的威胁模型或者有错误的机制。 例子 安卓的 Java SecureRandom弱点导致比特币被盗。 拥有所有者私钥的任何人都可以花费比特币。 许多安卓上的比特币钱包应用使用了 Java 的SecureRandom API。 结果系统有时忘记了给 PRNG 种子 结果一些比特币密钥很容易被猜到。 对手搜索可猜测的密钥花费任何相应的比特币。 例子 沙箱中的漏洞NaClJavascriptJava 运行时。 允许对手逃离隔离执行他们本不应执行的操作。 例子 Moxie 的 SSL 证书名称检查漏洞 空字节与长度编码。 例子 缓冲区溢出见下文。 案例研究缓冲区溢出 考虑一个网络服务器。 通常情况下网络服务器的代码负责安全性。 例如检查可以访问哪些 URL检查 SSL 客户端证书… 因此服务器代码中的漏洞可能导致安全妥协。 威胁模型是什么策略是什么 假设对手可以连接到网络服务器提供任何输入。 策略有点模糊只执行程序员意图的操作 例如不希望对手窃取数据绕过检查安装后门。 考虑来自某个网络服务器的简化示例代码 webserver.c: int read_req(void) {char buf[128];int i;gets(buf);i atoi(buf);return i;} 编译器在内存布局方面生成了什么 x86 栈 栈向下增长。 %esp指向栈上最后最底部有效的内容。 %ebp指向调用者的%esp值。 read_req() stack layout: ------------------entry %ebp ---- | .. prev frame .. || || |------------------entry %esp ---- | return address |------------------new %ebp ------ | saved %ebp |------------------| buf[127] || ... || buf[0] |------------------| i |new %esp ------ ------------------| ... |------------------ 调用者的代码比如main() 调用read_req() read_req()的汇编代码 push %ebpmov %esp - %ebpsub 168, %esp # stack vars, etc...mov %ebp - %esppop %ebpret 对手如何利用这段代码 提供长输入覆盖超出缓冲区的栈上数据。 有趣的数据返回地址被ret使用。 可以将返回地址设置为缓冲区本身在其中包含一些代码。 对手如何知道缓冲区的地址 如果一台机器的内存是另一台的两倍会发生什么 幸运的是对手虚拟内存使事情更加确定。 对于给定的操作系统和程序地址通常是相同的。 如果栈向上增长而不是向下会发生什么 查看gets()的栈帧。 一旦对手执行代码他们可以做什么 使用进程的任何权限。 经常利用溢出来更容易地进入系统。 最初在 Unix 上运行 shell /bin/sh因此称为“shell 代码”。 如果进程以 root 或 Administrator 运行可以做任何事情。 即使不是仍然可以发送垃圾邮件读取文件Web 服务器数据库… 可以攻击防火墙后面的其他机器。 为什么程序员会写出这样的代码 旧代码未暴露在互联网上。 程序员没有考虑安全性。 许多标准函数曾经是不安全的strcpygetssprintf。 即使是安全版本也有陷阱strncpy 不会在末尾加上空字符。 更一般地说任何内存错误都可能转化为漏洞。 在释放后继续使用内存释放后使用。 如果写入覆盖新的数据结构例如函数指针。 如果读取可能会调用一个已损坏的函数指针。 两次释放相同的内存双重释放。 可能会导致 malloc() 之后再次返回相同的内存。 将栈指针递减到栈的末尾之外进入其他内存。 利用运行在 Linux 上的 Xorg 服务器中的大内存管理漏洞 一个字节的错误写入可能导致受损。 glibc __gconv_translit_find() 漏洞 甚至可能不需要覆盖返回地址或函数指针。 可以足以读取敏感数据如加密密钥。 可以通过改变一些位来满足需求例如int isLoggedInint isRoot。 如何避免机制问题 减少安全关键代码的数量。 不要依赖整个应用程序来执行安全性。 将在实验 2 中进行。 避免安全关键代码中的错误。 例如不要使用 gets()而是使用 fgets() 可以限制缓冲区长度。 使用常见、经过充分测试的安全机制“机制的经济性”。 审计这些常见的安全机制有很多动力这样做。 避免开发可能存在错误的新的一次性机制。 良好的机制支持多种用途、策略更有动力进行审计。 常见机制的示例 操作系统级别的访问控制但是通常可以更好。 网络防火墙但是通常可以更好。 加密加密协议。 缓冲区溢出松散的边界 注意 这些讲座笔记略有修改来自 2014 年 6.858 课程网站。 缓冲区溢出攻击回顾 在上一讲中我们看了执行缓冲区溢出攻击的基础知识。该攻击利用了几个观察结果 系统软件通常用 C 编写操作系统、文件系统、数据库、编译器、网络服务器、命令外壳和控制台实用程序 C 本质上是高级汇编语言所以… 暴露原始指针到内存 不对数组执行边界检查因为硬件不这样做而 C 希望让你尽可能接近硬件 攻击还利用了关于 x86 代码如何工作的架构知识 栈增长的方向 栈变量的布局尤其是数组和函数的返回地址 read_req.c: void read_req() {char buf[128];int i;gets(buf);//. . . do stuff w/buf . . .} 编译器在内存布局方面生成了什么x86 栈看起来像这样 %esp points to the last (bottom-most) valid thing onthe stack.%ebp points to the callers %esp value.------------------entry %ebp ---- | .. prev frame .. || | || | | stack grows down------------------ |entry %esp ---- | return address | v------------------new %ebp ------ | saved %ebp |------------------| buf[127] || ... || buf[0] |------------------new %esp ------ | i |------------------ 对手如何利用这段代码 提供长输入覆盖缓冲区后的栈数据。 关键观察 1 攻击者可以覆盖返回地址使程序跳转到攻击者选择的位置 关键观察 2 攻击者可以将返回地址设置为缓冲区本身在其中包含一些 x86 代码 攻击者在执行代码后可以做什么 利用进程的任何权限如果进程以 root 或管理员身份运行它可以在系统上做任何想做的事情。即使进程不以 root 身份运行它也可以发送垃圾邮件、读取文件有趣的是攻击或破坏防火墙后面的其他机器。 嗯但为什么操作系统没有注意到缓冲区已经溢出 就操作系统而言没有发生任何奇怪的事情请记住粗略地说操作系统只在 Web 服务器进行 IO 或 IPC 时才被调用。除此之外操作系统基本上只是坐下来让程序执行依靠硬件页表防止进程篡改彼此的内存。然而页表保护无法防止进程“针对自身”发起的缓冲区溢出因为溢出的缓冲区、返回地址和所有相关内容都在进程的有效地址空间内。 在本讲座的后面我们将讨论操作系统可以采取的措施使缓冲区溢出更加困难。 修复缓冲区溢出 方法 #1 避免 C 代码中的错误。 难以或不可能实现。 程序员应仔细检查缓冲区、字符串、数组等的大小。特别是程序员应使用考虑到缓冲区大小的标准库函数strncpy() 而不是 strcpy()fgets() 而不是 gets() 等。 现代版本的 gcc 和 Visual Studio 在程序使用不安全函数如 gets()时会发出警告。一般来说你不应该忽略编译器警告。 将警告视为错误 好处 首先避免问题 坏处 很难确保代码是无错误的特别是如果代码库很大。此外应用程序本身可能定义不使用fgets()或strcpy()作为基本操作的缓冲区操作函数。 方法 2 构建工具来帮助程序员找到错误。 例如我们可以使用静态分析在编译之前找到源代码中的问题。想象一下如果你有这样一个函数 foo.c void foo(int *p) {int offset;int *z p offset;if(offset 7){bar(offset);}} 通过静态分析控制流我们可以知道 offset 在未初始化的情况下被使用。 if语句还限制了我们可能传播到 bar 的偏移量。 我们将在后续讲座中更多地讨论静态分析。 提供随机输入的“模糊器”可以有效地发现错误。请注意模糊化可以与静态分析结合以最大化代码覆盖率 坏处 很难证明完全没有错误尤其是对于像 C 这样的不安全代码。 好处 即使是部分分析也是有用的因为程序应该变得更少有错误。例如松散的边界检查可能无法捕捉所有内存错误但它可以检测到许多重要类型。 方法 3 使用内存安全语言JavaScriptCPython。 好处 通过不向程序员暴露原始内存地址并通过自动处理垃圾回收来防止内存损坏错误。 坏处 低级运行时代码确实使用原始内存地址。因此运行时核心仍然需要正确。例如堆喷射攻击 NOZZLE防御堆喷射代码注入攻击 撰写利用教程第 11 部分堆喷射揭秘 坏处 仍然有很多使用不安全语言FORTRAN 和 COBOL的遗留代码。 坏处 也许你需要访问低级硬件功能例如你正在编写设备驱动程序 坏处 性能比调优良好的 C 应用程序差 过去是一个更大的问题但硬件和高级语言变得更好了。 JIT 编译万岁 asm.js的性能接近本机 C性能的 2 倍 使用谨慎的编码来避免关键路径中的垃圾回收抖动。 也许你是一个不懂得如何选择合适工具的坏人/ 语言沙文主义者。如果你的任务是 I/O 绑定的原始计算速度就不那么重要了。另外不要成为那个用 C 语言编写文本处理程序的笨蛋。 上述 3 种方法都是有效且广泛使用的但在实践中缓冲区溢出仍然是一个问题。 大量/复杂的用 C 语言编写的遗留代码非常普遍。 即使是用 C/C编写的新代码也可能存在内存错误。 尽管存在有缺陷的代码我们如何减轻缓冲区溢出 在“传统”缓冲区溢出中发生了两件事 对手控制执行程序计数器。 对手执行一些恶意代码。 这两个步骤存在哪些困难 需要覆盖一个代码指针稍后被调用。常见目标是使用堆栈上的缓冲区的返回地址。在实践中任何内存错误都可能起作用。函数指针C vtables异常处理程序等。 需要一些有趣的代码在进程的内存中。这通常比#1 更容易因为 在缓冲区中放置代码很容易因此 进程中已经包含了许多可能被利用的代码。 然而攻击者需要将这段代码放在可预测的位置以便攻击者可以将代码指针设置为指向恶意代码 缓解方法 1金丝雀例如StackGuardgcc 的 SSP 理念覆盖代码指针是可以接受的只要我们在调用之前捕捉到它。 较早的一个系统StackGuard 在进入时在堆栈上放置一个金丝雀在返回前检查金丝雀值。 通常需要源代码编译器插入金丝雀检查。 Q 堆栈图中的金丝雀在哪里 A 金丝雀必须放在堆栈上返回地址的“前面”这样任何溢出重写返回地址也将重写金丝雀。 堆栈布局 | |------------------entry %esp ---- | return address | ^------------------ |new %ebp ------ | saved %ebp | |------------------ || CANARY | | Overflow goes------------------ | this way.| buf[127] | || ... | || buf[0] | |------------------| | Q 假设编译器总是将金丝雀设为 4 个字节的a字符。这有什么问题吗 A 对手可以在缓冲区溢出中包含适当的金丝雀值 因此金丝雀必须要么难以猜测要么可以容易猜测但仍然能够抵御缓冲区溢出。以下是这些方法的示例。 “终结符金丝雀”四个字节0CRLF-1 理念许多 C 函数将这些字符视为终结符例如gets()sprintf()。因此如果金丝雀与这些终结符之一匹配那么进一步的写入将不会发生。 在程序初始化时生成随机金丝雀今天更常见但是你需要良好的随机性。 堆栈金丝雀不会捕捉到哪些类型的漏洞 在金丝雀之前覆盖函数指针变量。 攻击者可以覆盖数据指针然后利用它进行任意内存写入。 数据指针示例 int *ptr ...;char buf[128];gets(buf); // Buffer is overflowed, and overwrites ptr.*ptr 5; // Writes to an attacker-controlled address!// Canaries cant stop this kind of thing. 堆对象溢出函数指针C vtables。 malloc/free溢出 malloc 示例 int main(int argc, char **argv) {char *p, *q;p malloc(1024);q malloc(1024);if(argc 2)strcpy(p, argv[1]);free(q);free(p);return 0;} 假设属于p和q的两个内存块在内存中是相邻/附近的。 假设malloc和free表示内存块如下 malloc 内存块 ---------------- | | | App data | | | Allocated memory block---------------- | size | ---------------- ----------------| size |----------------| ...empty... |---------------- | bkwd ptr | ---------------- | fwd ptr | Free memory block---------------- | size | ---------------- 因此在p的缓冲区溢出将覆盖q内存块中的大小值为什么这是一个问题 当free()合并两个相邻的空闲块时需要操作bkwd和fwd指针… …并且指针计算使用大小来确定空闲内存块结构的位置 free()内部 p get_free_block_struct(size);bck p-bk;fwd p-fd;fwd-bk bck; // Writes memory!bck-fd fwd; // Writes memory! 空闲内存块表示为 C struct通过破坏大小值攻击者可以强制free()在位于攻击者控制的内存中的伪造struct上操作并具有攻击者控制的值用于前向和后向指针。 如果攻击者知道free()如何更新指针他可以使用该更新代码将任意值写入任意位置。例如攻击者可以覆盖返回地址。 实际细节更加复杂如果你对血腥细节感兴趣请访问这里 高层次的观点是栈金丝雀不会阻止这种攻击因为攻击者正在“越过”金丝雀并直接写入返回地址 缓解方法 2: 边界检查 总体目标 通过检查指针是否在范围内来防止指针误用。 挑战 在 C 语言中很难区分有效指针和无效指针。例如假设一个程序分配了一个字符数组… char x[1024]; … 以及该数组中的某个位置的指针例如char *y x[107]; 增加y以访问后续元素是否可以 如果x代表一个字符串缓冲区也许是。 如果x代表一个网络消息也许不。 如果程序使用联合体情况会变得更加复杂。 联合体示例: union u{int i;struct s{int j;int k;};};int *ptr (u.s.k); // Does this point to valid data? 问题 是在 C 语言中指针不会编码关于该指针的预期使用语义的信息。 因此很多工具不会尝试猜测这些语义。相反这些工具的目标并不像“完全正确”的指针语义那样高远这些工具只是强制执行堆对象和栈对象的内存边界。在高层次上这是目标 对于从p派生出的指针pp只能被解引用以访问属于p的有效内存区域。 强制执行内存边界是一个比强制“完全正确”指针语义更弱的目标。程序仍然可以通过恶意方式践踏其内存而自掘坟墓例如在联合体示例中应用程序可能会写入指针尽管未定义。 然而边界检查仍然很有用因为它可以防止任意内存覆写。程序只能践踏其内存如果该内存实际上已分配这在 C 语言世界中被认为是进步。 边界检查的一个缺点是通常需要对编译器进行更改并且必须使用新编译器重新编译程序。如果只能访问二进制文件则这是一个问题。 有哪些实现边界检查的方法 边界检查方法 #1: 电子围栏 这是一个旧方法其优点在于简单。 思路 将每个堆对象与一个守卫页对齐并使用页表确保对守卫页的访问导致故障。 电子围栏: ---------| Guard || | ^--------- | Overflows cause a page exception| Heap | || obj | |--------- 这是一种方便的调试技术因为堆溢出会立即导致崩溃而不是悄无声息地破坏堆并在未来某个不确定的时间导致失败。 重要优势无需源代码即可运行无需更改编译器或重新编译程序 你确实需要重新链接它们以便它们使用实现电子围栏的新版本的malloc。 主要缺点巨大的开销每页只有一个对象并且您有一个未用于“真实”数据的虚拟页面的开销。 摘要电子围栏可以作为调试技术很有用并且可以防止堆对象的一些缓冲区溢出。然而电子围栏无法保护堆栈并且内存开销太高无法在生产系统中使用。 **边界检查方法2**胖指针 **想法**修改指针表示以包含边界信息。现在指针包括关于生存在该内存区域中的对象的边界信息。 示例 Regular 32-bit pointer -----------------| 4-byte address |-----------------Fat pointer (96 bits)------------------------------------------------------| 4-byte obj_base | 4-byte obj_end | 4-byte curr_address |------------------------------------------------------ 您需要修改编译器并重新编译程序以使用胖指针。编译器生成的代码会在它解引用地址超出自己的base ... end范围的指针时中止程序。 示例 int *ptr malloc(sizeof(int) * 2);while(1){*ptr 42; ----------|ptr; |} |______________________________||This line checks the current address of the pointer andensures that its in-bounds. Thus, this line will failduring the third iteration of the loop. **问题1**检查所有指针解引用可能很昂贵。C 社区讨厌昂贵的东西因为 C 就是关于速度速度速度。 **问题2**胖指针与许多现有软件不兼容。 你不能将胖指针传递给未修改的库。 你不能在固定大小的数据结构中使用胖指针。例如sizeof(that_struct)将会改变 对胖指针的更新不是原子的因为它们跨越多个字。一些程序假设指针写入是原子的。 边界检查方法3影子数据结构 **想法**使用影子数据结构来跟踪边界信息Jones and KellyBaggy bounds。 对于每个分配的对象存储对象的大小。例如 记录传递给 malloc 的值 char *p malloc(mem_size); 对于静态变量值由编译器确定 char p[256]; 对于每个指针我们需要对两个操作进行干预 指针算术char *q p 256; 指针解引用char ch *q; Q 为什么我们需要对解引用进行干预不能只进行算术吗 A 无效指针并不总是一个错误例如数组最后一个元素之外的一个元素的指针可能被用作循环中的停止测试。应用程序还可以执行一些愚蠢的操作如 模拟从 1 开始的数组 计算 p(a-b)为(pa)-b 生成稍后检查有效性的 OOB 指针 因此仅仅创建无效指针不应该导致程序失败。 Q 为什么我们需要对算术进行干预不能只进行解引用吗 A 干预算术是允许我们跟踪指针的来源并设置 OOB 位的原因。没有 OOB我们将无法确定派生指针何时超出其基本对象的边界。 挑战 1: 我们如何找到常规指针即在边界内的指针的边界信息 天真: 使用哈希表或区间树将地址映射到边界。 好: 空间高效仅存储正在使用的指针的信息而不是所有可能的地址。 不好: 查找慢每次查找多次内存访问。 天真: 使用数组存储每个内存地址的边界信息。 好: 快速 不好: 内存开销很高。 挑战 2: 我们如何强制越界指针解引用失败 天真: 对每个指针解引用进行检测。 好: 哦它有效。 不好: 昂贵。我们必须为每次解引用执行额外的代码 Baggy bounds 方法5 个技巧 Trick 1: 将每个分配向上舍入为 2 的幂并将分配的起始对齐到该 2 的幂。 Trick 2: 将每个范围限制表示为log_2(alloc_size)。 对于 32 位指针只需要 5 位来表示可能的范围我们只能分配 32 种不同大小的对象2¹ 2 字节2² 4 字节...2³¹ 字节或 2³² 字节并且我们存储分配大小的以 2 为底的对数这是一个介于 1 和 32 之间的数字因此我们只需要 5 位来表示它。 Trick 3: 在线性数组中存储限制信息每个条目一个字节的快速查找。此外我们可以使用虚拟内存按需分配数组 Trick 4: 以插槽粒度例如16 字节分配内存更少的数组条目。 这意味着最小分配大小为 16 字节 …而且由于指针将对齐到其分配大小边界这意味着指针的最后 4 位都是零 示例: slot_size 16p malloc(16); -- table[p/slot_size] 4;p malloc(32); -- table[p/slot_size] 5;\- table[(p/slot_size) 1] 5; Trick 4 (续): 现在给定一个已知的好指针p和一个派生指针p我们可以通过检查这两个指针的地址位中是否有相同的前缀并且它们只在它们的e个最低有效位上有所不同其中e等于分配大小的对数来测试p是否有效。 示例: C code------ p p i;Bounds check------------size 1 table[p log_of_slot_size];base p ~(size - 1);(p base) ((p - base) size)Optimized bounds check----------------------(p^p) table[p log_of_slot_size] 0 Trick 5: 使用虚拟内存系统来防止越界解引用在 OOB 指针中设置最高有效位然后将地址空间上半部分的页面标记为不可访问。这样我们就不必对指针解引用进行检测以防止错误的内存访问 示例代码假设slot_size16 char *p malloc(44); //Note that the nearest power of 2 (i.e.,//64 bytes) are allocated. So, there are//64/(slot_size) 4 bounds table entries//that are set to log_2(64) 6.char *q p 60; //This access is ok: Its past ps object//size of 44, but still within the baggy//bounds of 64.char *r q 16; //r is now at an offset of 601676 from//p. This means that r is (76-64)12 bytes//beyond the end of p. This is more than//half a slot away, so baggy bounds will//raise an error.char *s q 8; //s is now at an offset of 60868 from p.//So, s is only 4 bytes beyond the baggy//bounds, which is les than half a slot//away. No error is raised, but the OOB//high-order bit is set in s, so that s//cannot be dereferenced.char *t s - 32; //t is now back inside the bounds, so//the OOB bit is cleared. 对于 OOB 指针高位被设置如果 OOB 在半个插槽内。- 通常操作系统内核位于上半部分通过分页硬件保护自身。- 问: 为什么越界是半个插槽 那么作业问题的答案是什么 char *p malloc(256);char *q p 256;char ch *q; // Does this raise an exception?// Hint: How big is the baggy bound for p? Baggy bounds 论文勘误 Baggy bounds 论文中的一些错误 图 3显式边界检查应该生成如下大小 size 1 table[p log_of_slot_size] 图 3优化的边界检查应该是 (p^p) table[p log_of_slot_size] 0 图 5 和 18指针算术代码应该是以下之一 char *p buf[i]; char *p buf i; Baggy bounds续 注意 这些讲座笔记是从 2014 年 6.858 课程网站上发布的笔记中稍作修改而来。 示例代码假设slot_size 16 char *p malloc(44); // Note that the nearest power of 2 (i.e.,// 64 bytes) are allocated. So, there are// 64/(slot_size) 4 bounds table entries// that are set to log_2(64) 6.char *q p 60; // This access is ok: Its past ps object// size of 44, but still within the baggy// bounds of 64.char *r q 16; // ERROR: r is now at an offset of 601676// from p. This means that r is (76-64)12// beyond the end of p. This is more than// half a slot away, so baggy bounds will// raise an error.char *s q 8; // s is now at an offset of 60868 from p.// So, s is only 4 bytes beyond the baggy// bounds, which is less than half a slot// away. No error is raised, but the OOB// high-order bit is set in s, so that s// cannot be derefernced.char *t s - 32; // t is now back inside the bounds, so// the OOB bit is cleared. 对于越界指针高位被设置如果在半个插槽内越界。 通常操作系统内核位于上半部分通过分页硬件保护自身。 Q: 为什么要为越界分配半个插槽 那么作业问题的答案是什么 char *p malloc(255);char *q p 256;char ch *q; // Does this raise an exception?// Hint: How big is the baggy bound for p? 宽松边界检查是否必须检测每个内存地址计算和访问 不静态分析可以证明某些地址始终是安全的。但是某些地址计算是“不安全”的因为无法静态确定其值的边界。这些不安全的变量需要检查。 处理函数调用参数有点棘手因为 x86 调用约定是固定的即硬件期望栈上的某些内容放在特定位置。 但是我们可以将不安全的参数复制到一个单独的区域并确保复制的参数对齐和受保护。 Q: 我们是否必须在函数返回时用复制的值覆盖原始参数 A: 不因为在 C 语言中一切都是按值传递的 宽松边界检查如何确保与现有库的二进制兼容性 特别是宽松边界代码如何与由未经检测的代码分配的内存指针交互 解决方案 边界表中的每个条目都初始化为值 31这意味着相应的指针具有 2³¹ 的内存边界这是所有可寻址内存。 在经过检测的代码中进行内存分配时边界条目如前所述设置并在释放内存时重置为 31。 分配给未经检测的代码的内存永远不会改变边界表条目的默认值 31因此当经过检测的代码与这些指针交互时边界错误永远不会发生 例子 Contiguous range ofmemory used for theheap-------------------| || || Heap allocated by || uninstrumented |---| code | \ Bounds table| | \------------------- \ -----------| | -| || | | Always 31 || Heap allocated by | | || instrumented code | -----------| | | Set using || |---------| baggy bnds|------------------- ----------- 这一切意味着什么 无法检测在未经检测的代码中生成的越界指针。 无法检测传递给库的越界指针何时再次进入边界内。 Q: 为什么 A: 因为未经检测的代码中没有指针检查可以清除高位越界位 Q: 为什么要检测strcpy()和memcpy() A: 否则这些函数就是未经检测的代码并且会遇到我们刚刚讨论过的相同问题。例如现成的strcpy()不能确保目标有足够的空间来存储源 宽松位如何利用 64 位地址空间 可以摆脱存储边界信息的表并将其放入指针中。 Regular pointer ----------------------------------------------| zero | size | supported addr space |----------------------------------------------21 5 38OOB pointer---------------------------------------------| offset | size | zero | supported addr space |---------------------------------------------13 5 8 38 这类似于一个胖指针但具有以下优点…… 标记指针与常规指针大小相同 对它们的写入是原子的 ……以便不破坏程序员的期望并且数据布局保持不变。 还要注意使用标记指针我们现在可以跟踪远离基本指针多得多的越界指针。这是因为现在我们可以使用偏移量标记指针指示它们距离基本指针有多远。在 32 位世界中如果没有额外的数据结构我们无法跟踪越界偏移量 在 baggy bounds 系统中仍然可以发动缓冲区溢出攻击吗 是的因为这个世界充满了悲伤。 可能会利用未经检测的库中的漏洞。 可能会利用时间漏洞使用后释放。 混合缓冲区和代码指针 例子 struct {char buf[256];void (*f) (void);} my_type; 请注意*f不是分配的类型因此在调用期间与其解引用相关联的边界检查不存在。因此如果s.buf溢出例如由未经检测的库中的错误引起并且s.f被损坏那么对f的调用不会导致边界错误 重新排列 f 和 buf 会有帮助吗 可能会破坏依赖结构布局的应用程序。 如果这是一个struct my_type数组可能不会有帮助。 一般来说边界检查的成本是什么 边界信息的空间开销胖指针或 baggy bounds 表。 Baggy bounds 还会为 buddy 分配器使用的额外填充内存增加空间开销尽管所有流行的动态内存分配算法都会有一定程度的开销。 指针算术和解引用的 CPU 开销。 虚警 未使用的越界指针。 临时超出边界指针超过slot_size/2。 指针到整数的转换和反向转换。 将越界指针传递给未经检查的代码高地址位被设置因此如果未经检查的代码使用该指针进行算术运算可能会导致混乱。 需要大量编译器支持。 因此baggy bounds 检查是一种减轻有缺陷代码中缓冲区溢出的方法。 实现边界检查的更多方法 方法 4非可执行内存AMD 的 NX 位Windows DEPW^X 等 现代硬件允许为内存指定读取、写入和执行权限。R、W 权限很久以前就有了执行权限是最近才有的。 可以将堆栈标记为不可执行这样对手就无法运行他们的代码。 更一般地一些系统执行“W^X”意味着所有内存要么可写要么可执行但不能同时。 当然既不可写也不可执行也是可以的。 优势 可能无需进行任何应用程序更改即可运行。 优势 硬件一直在监视你不像操作系统。 缺点 动态生成代码更困难尤其是使用 W^X。 像 Java 运行时、Javascript 引擎这样的 JIT 会即时生成 x86 代码。 可以通过先写入然后更改为可执行来解决问题。 方法 5随机化内存地址ASLR堆栈随机化等 观察许多攻击在 shellcode 中使用硬编码地址[攻击者抓取一个二进制文件并使用 gdb 来找出东西的位置。] 因此我们可以使攻击者难以猜测有效的代码指针。 堆栈随机化将堆栈移动到随机位置并/或在堆栈变量之间放置填充。这使得攻击者更难确定 当前帧的返回地址位于何处 攻击者的 shellcode 缓冲区将位于何处 随机化整个地址空间地址空间布局随机化随机化堆栈、堆、DLL 的位置等。 依赖于很多代码是可重定位的这一事实。 动态加载器可以为每个库、程序选择随机地址。 对手不知道 system()等函数的地址。 这仍然可以被利用吗 对手可能猜测随机性。特别是在 32 位机器上没有太多的随机位例如1 位属于内核/用户模式划分12 位不能被随机化因为内存映射页面需要与页面边界对齐等。 例如攻击者可能进行缓冲区溢出并尝试用usleep(16)的地址覆盖返回地址然后查看连接是否在 16 秒后挂起或者是否崩溃在这种情况下服务器会使用相同的 ASLR 偏移量 fork 一个新的 ASLR 进程。 usleep()可能在 2¹⁶ 或 2²⁸ 个地方之一。更多细节。 ASLR 在 64 位机器上更实用很容易有 32 位的随机性。 对手可能提取随机性。 程序可能生成包含指针的堆栈跟踪或错误消息。 如果对手可以运行一些代码他们可能能够提取真实地址JIT 编译的代码。 Flash 的字典哈希表中的可爱地址泄漏 让受害者访问您的 Flash 启用页面例如购买广告。 哈希表在内部计算键的哈希值。 整数的哈希值是整数本身。 对象的哈希值是其内存地址。 遍历哈希表是从最低哈希键到最高哈希键进行的。 因此攻击者创建一个字典插入一个包含 shellcode 的字符串对象然后向字典中插入一堆数字。 通过遍历字典攻击者可以确定字符串对象位于何处看看对象引用落在哪些整数之间 现在用 shellcode 地址覆盖代码指针并绕过 ASLR 对手可能不关心确切要跳转到哪里。 例如“堆喷洒”填充内存以便随机跳转是可以的 对手可能利用一些未随机化的代码如果存在这样的代码。 随机化的一些其他有趣用途 系统调用随机化每个进程有自己的系统调用号码。 指令集随机化以便攻击者不能轻易确定特定程序实例的shellcode是什么样子。 例子 想象一下处理器有一个特殊的寄存器来保存“解码密钥”。每个特定应用程序的安装都与一个随机密钥相关联。应用程序中的每条机器指令都与该密钥进行异或运算。当操作系统启动进程时它设置解码密钥寄存器处理器使用此密钥解码指令后再执行它们。 实际上使用了哪些缓冲区溢出防御措施 gcc 和 MSVC 默认启用栈保护。 Linux 和 Windows 默认包含 ASLR 和 NX。 由于 性能开销 需要重新编译程序 误报安全工具中的常见主题误报阻止了工具的采用通常一些遗漏但没有误报比零遗漏但有误报更好。 返回导向编程ROP ASLR 和 DEP 是非常强大的防御技术。 DEP 防止攻击者执行自己选择的栈代码。 ASLR 可以防止攻击者确定 shellcode 或返回地址的位置。 但是如果攻击者能够找到位于已知位置的具有已知功能的预先存在的代码呢那么攻击者可以调用该代码来做坏事。 当然预先存在的代码并不是故意恶意因为它是应用程序的正常部分。 但是攻击者可以传递意外的参数给该代码或者跳转到代码的中间并仅执行该代码的所需部分。 这种攻击称为返回导向编程或ROP。为了理解 ROP 的工作原理让我们看一个具有安全漏洞的简单 C 程序。示例改编自此处。 void run_shell(){system(/bin/bash);}void process_msg(){char buf[128];gets(buf);} 假设系统不使用 ASLR 或栈保护但使用了 DEP。process_msg()存在明显的缓冲区溢出但攻击者无法利用此溢出在buf中执行 shellcode因为 DEP 使栈不可执行。然而run_shell()函数看起来很诱人… 攻击者如何执行它 攻击者反汇编程序并找出run_shell()的起始地址在哪里。 攻击者发起缓冲区溢出并用run_shell()的地址覆盖process_msg()的返回地址。砰攻击者现在可以访问以应用程序权限运行的 shell。 例子 ------------------entry %ebp ---- | .. prev frame .. || | | |------------------entry %esp ---- | return address | ^ --Gets overwritten ------------------ | with address ofnew %ebp ------ | saved %ebp | | run_shell()------------------ || buf[127] | || ... | || buf[0] | |new %esp ------ ------------------ 这是我们已经看过的缓冲区溢出的直接扩展。但是我们如何向我们要跳转到的函数传递参数呢 char *bash_path /bin/bash;void run_cmd(){system(/something/boring);}void process_msg(){char buf[128];gets(buf);} 在这种情况下我们要传递的参数已经位于程序代码中。程序中还存在一个对system()的调用但该调用并未传递我们想要的参数。 我们知道system()必须与我们的程序链接。因此使用我们可靠的朋友 gdb我们可以找到system()函数的位置以及 bash_path 的位置。 要使用bash_path参数调用system()我们必须设置堆栈以便在跳转到它时system()期望堆栈上有这些内容 | ... |------------------| argument | The system() argument.------------------%esp ---- | return addr | Where system() should ------------------ ret after it hasfinished. 因此缓冲区溢出需要设置一个看起来像这样的堆栈 ------------------entry %ebp ---- | .. prev frame .. || || || * - - - - - - - | ^| | | Address of bash_path * - - - - - - - | || | | Junk return addr for system()------------------ |entry %esp ---- | return address | | Address of system()------------------ | new %ebp ------ | saved %ebp | | Junk------------------ || buf[127] | || ... | | Junk| buf[0] | |new %esp ------ ------------------ | 本质上我们所做的是为system()调用设置了一个虚假的调用帧换句话说我们模拟了编译器如果真的想要设置一个对system()的调用会做什么。 如果字符串/bin/bash不在程序中怎么办 我们可以将该字符串包含在缓冲区溢出中然后使system()的参数指向该字符串。 | h\0 | ^| * - - - - - - - | || /bas | || * - - - - - - - | || /bin | | --------------------| * - - - - - - - | | || | | Address of bash_path-- * - - - - - - - | || | | Junk return addr from system()------------------ | entry %esp ---- | return address | | Address of system()------------------ | new %ebp ------ | saved %ebp | | Junk------------------ || buf[127] | || ... | | Junk| buf[0] | | new %esp ------ ------------------ | 请注意在这些示例中我一直假设攻击者使用了来自system()的无用返回地址。然而攻击者也可以将其设置为有用的内容。 实际上通过将其设置为有用的内容攻击者可以链接调用在一起 **目标**我们想要多次调用system(/bin/bash)。假设我们找到了三个地址 system()的地址 字符串/bin/bash的地址 这些 x86 操作码的地址 pop %eax //Pops the top-of-stack and puts it in %eaxret //Pops the top-of-stack and puts it in %eip 这些操作码是“小工具”的一个示例。小工具是预先存在的指令序列可以串联在一起创建一个利用。请注意有一些用户友好的工具可以帮助您从现有二进制文件中提取小工具例如msfelfscan。 | | ^ * - - - - - - - || | | Address of bash_path - Fake calling * - - - - - - - | | frame for(4) | | | Address of pop/ret * system() * - - - - - - - | (3) | | | Address of system() * - - - - - - - |(2) | | | Address of bash_path - Fake calling * - - - - - - - | | frame for(1) | | | Address of pop/ret * system()------------------ |entry %esp ---- | return address | | Address of system()------------------ | new %ebp ------ | saved %ebp | | Junk------------------ || buf[127] | || ... | | Junknew %esp ------ | buf[0] | |------------------ | 那么这是如何工作的呢记住返回指令弹出栈顶并将其放入%eip。 溢出的函数通过发出ret来终止。ret弹出栈顶system()的地址并将%eip设置为它。system()开始执行%esp现在在1并指向pop/ret小工具。 system()执行完毕并调用ret。%esp从1-2因为ret指令弹出栈顶并将其分配给%eip。%eip现在是pop/ret小工具的开始。 pop/ret小工具中的 pop 指令从栈中丢弃bash_path变量。%esp现在在3。我们仍然在pop/ret小工具中 pop/ret小工具中的ret指令弹出栈顶并将其放入%eip。现在我们再次在system()中并且%esp在4。 等等。 基本上我们创建了一种新类型的机器它由堆栈指针驱动而不是常规指令指针随着堆栈指针沿着堆栈移动它执行的小工具的代码来自预先存在的程序代码数据来自缓冲区溢出创建的堆栈数据。 这种攻击规避了 DEP 保护–我们没有生成任何新代码只是调用了现有的代码 栈读取打败金丝雀 假设 远程服务器存在缓冲区溢出漏洞。 服务器崩溃并重新启动如果金丝雀值设置为不正确的值。 当服务器重新启动时canary 不会重新随机化ASLR 也不会重新随机化例如因为服务器使用 Linux 的 PIE 机制并且使用 fork() 来创建新的工作进程而不是 execve()。 因此要确定一个 8 字节的 canary 值 char canary[8];for(int i 1; i 8; i){ //For each canary byte . . .for(char c 0; c 256; c){ //. . . guess the value.canary[i-1] c;server_crashed try_i_byte_overflow(i, canary);if(!server_crashed){//Weve discovered i-th byte of the//the canary!break;}}} 此时我们已经有了 canary但请记住该攻击假设服务器在崩溃后使用相同的 canary。 猜测一个字节的正确值平均需要 128 次猜测因此在 32 位系统上我们只需要 4*128512 次猜测来确定 canary在 64 位系统上我们需要 8*1281024 次。 比在 canary 上进行暴力破解攻击要快得多在具有 16/28 位 ASLR 随机性的 32/64 位系统上预期猜测次数为 2¹⁵ 或 2²⁷。 暴力破解攻击可以使用我们之前讨论过的 usleep(16) 探测。 Canary 读取可以扩展到读取缓冲区溢出可以覆盖的任意值 因此我们已经讨论了如果服务器在重新生成时不更改 canaries我们如何能够击败随机化的 canaries。我们还展示了如何使用 gdb 和 gadgets 来执行程序中预先存在的函数使用攻击者控制的参数。但是如果服务器使用 ASLR 呢这将阻止您使用离线分析来找到预先存在的函数的位置 这就是今天讲座论文讨论的内容。该论文假设我们使用的是 64 位机器所以从现在开始在本讲座中我们也将假设如此。在这次讨论中主要的变化是函数参数现在是通过寄存器传递而不是通过栈传递。 盲目返回导向编程 步骤 1找到一个 stop gadget stop gadget 是指指向会挂起程序但不会崩溃的代码的返回地址。 一旦攻击者能够击败 canaries他就可以覆盖溢出函数的返回地址并开始猜测 stop gadget 的位置。如果客户端网络连接突然关闭猜测的地址不是 stop gadget。如果连接保持打开那么该 gadget 就是 stop gadget。 步骤 2找到弹出栈中条目的 gadgets 一旦你有了一个 stop gadget你可以用它来找到其他将栈中条目弹出并存入寄存器的 gadgets。 定位栈弹出 gadgets 的三个构建块 probe: 潜在的栈弹出 gadget 的地址 stop: stop gadget 的地址 crash: 非可执行代码的地址0x0 示例: 找到一个弹出栈中一个元素的 gadget。 sleep(10)^ ^--- pop rax / \| ret / \| \---[stop] 0x5.... 0x5....| [trap] 0x0 0x0 ---------------------------[probe] 0x4...8 0x4...c --xor rax, rax | Crash!ret |\__________| 当你这样做很多次后你将拥有一系列弹出栈中一个元素然后返回的 gadgets。然而你不会知道这些 gadgets 将弹出的值存储在哪个 寄存器 中。 你需要知道哪些寄存器用于存储数据以便您可以发出系统调用。每个系统调用都期望其参数在一组特定的寄存器中。 请注意我们也不知道 syscall() 库函数的位置。 步骤 3找到 syscall() 并确定 pop gadgets 使用哪些寄存器 pause()是一个不带参数的系统调用因此忽略寄存器中的所有内容。 要找到pause()攻击者在栈上链接所有的pop x; ret小工具将pause()的系统调用号作为每个小工具的参数推送进去。在链的底部攻击者放置了syscall()的猜测地址。 | | ^ * - - - - - - - || | | Guessed addr of syscall() * - - - - - - - | | | | ... * - - - - - - - | | | | Sys call # for pause * - - - - - - - || | | Address of pop rsi; ret //Gadget 2 * - - - - - - - | | | | Sys call # for pause------------------ | entry %esp ---- | return address | | Address of pop rdi; ret //Gadget 1------------------ | new %ebp ------ | saved %ebp | | Junk------------------ || buf[127] | || ... | | Junk new %esp ------ | buf[0] | |------------------ | 因此在这个链的末端弹出小工具已经将pause()的系统调用号放入了一堆寄存器中希望包括rax这是syscall()查找系统调用号的寄存器。 一旦这个超级小工具引发了暂停我们就知道已确定了syscall()的位置。现在我们需要确定哪个小工具将栈顶弹出到rax中。攻击者可以通过逐步尝试一个小工具并查看是否可以调用pause()来弄清楚这一点。 要识别任意的pop x; ret小工具可以使用与您试图找到的x寄存器相关的其他系统调用的技巧。 因此这个阶段的结果是知道pop x; ret小工具syscall()的位置。 第 4 步调用 write() 现在我们想要在服务器与攻击者客户端之间的网络套接字上调用写入调用。我们需要以下小工具 pop rdi; ret (socket)pop rsi; ret (buffer)pop rdx; ret (length)pop rax; ret (write syscall number)syscall 我们必须猜测套接字的值但这在实践中相当容易因为 Linux 将进程限制为同时打开 1024 个文件描述符并且新文件描述符必须是可用的最低文件描述符因此猜测一个小文件描述符在实践中效果很好。 要测试我们是否猜对了文件描述符只需尝试写入并查看是否收到任何内容 一旦我们有了套接字号码我们发出一个写入请求发送的数据是指向程序的.text段的指针这使得攻击者可以读取程序的代码虽然已随机化但现在完全为攻击者所知。现在攻击者可以直接找到更强大的小工具并利用这些小工具打开一个 shell。 防御 BROP 攻击 每次崩溃后重新随机化 canaries 和地址空间 使用exec()代替fork()来创建进程因为fork()会将父进程的地址空间复制给子进程。 有趣的是Windows 不容易受到 BROP 攻击的影响因为 Windows 没有fork()的等效功能。 崩溃后休眠 现在 BROP 攻击是一种拒绝服务攻击 边界检查 高达 2 倍的性能开销… 有关 ROP 和 x86 调用约定的更多信息 x86 调用约定简介 返回导向编程简介 深入了解 ROP返回导向编程的快速介绍 返回导向编程系统、语言和应用 OKWS 注意 这些讲座笔记略有修改来自 2014 年 6.858 课程网站上发布的笔记。 今天的讲座如何在 Unix 上构建一个安全的 Web 服务器。我们实验室 Web 服务器 zookws 的设计灵感来自于 OKWS。 特权分离 大型安全理念 将系统分割成各自具有特权的模块 想法 如果一个模块被破坏那么其他模块就不会被破坏 经常使用 虚拟机例如在自己的虚拟机中运行网站 SSH分离 sshd、agent 挑战 模块需要共享 需要操作系统支持 需要仔细使用操作系统正确设置事物 性能 OKWS 特权分离的有趣案例研究 服务之间有很多共享 严格的分区不起作用 大量的代码 在 OKcupid 之外并不广泛使用 许多网站都有他们的特权分离计划 但没有描述他们计划的论文 背景Unix 中的安全和保护 典型的主体用户 ID、组 ID32 位整数。 每个进程都有一个用户 IDuid和一组组 IDgid grouplist。 出于大多是历史原因一个进程有一个 gid 额外的组列表。 超级用户主体root由uid0表示绕过大多数检查。 Unix 中的对象操作是什么操作系统如何进行访问控制 文件、目录。 文件操作读、写、执行、更改权限… 目录操作查找、创建、删除、重命名、更改权限… 每个 inode 都有一个所有者用户和组。 每个 inode 对于用户、组、其他人都有读、写、执行权限。 通常表示为写入基数 8八进制的位向量 八进制很好用因为每个数字是 3 位读、写、执行。 谁可以更改文件的权限 只有用户所有者进程 UID。 对文件进行硬链接需要对文件有写权限。 可能的理由配额。 可能的理由防止将/etc/passwd硬链接到具有全局可写/var/mail的/var/mail/root。 目录的执行意味着能够查找名称但不能 ls。 检查进程打开文件/etc/passwd 必须能够在/中查找etc在/etc中查找passwd。 必须能够打开/etc/passwd读或读写。 假设你想要文件对 group1 和 group2 的交集可读。 在 Unix 中是否可能实现这一点 文件描述符。 文件打开时执行的文件访问控制检查。 一旦进程有一个打开的文件描述符就可以继续访问。 进程可以传递文件描述符通过 Unix 域套接字。 进程。 你可以对一个进程做什么 调试ptrace发送信号等待退出并获取状态 调试发送信号必须具有相同的 UID几乎。 各种例外在实践中这变得棘手。 等待/获取退出状态必须是该进程的父进程。 内存。 一个进程通常不能命名另一个进程的内存。 例外调试机制。 例外内存映射文件。 网络。 操作。 绑定到一个端口。 连接到某个地址。 读/写连接。 发送/接收原始数据包。 规则 只有 rootUID 0可以绑定到低于 1024 的端口 例如任意用户不能在端口 80 上运行 Web 服务器。 只有 root 可以发送/接收原始数据包。 任何进程都可以连接到任何地址。 只能读取/写入进程具有文件描述符的连接上的数据。 此外防火墙施加自己的检查与进程无关。 进程的主体是如何设置的 系统调用setuid()、setgid()、setgroups()。 只有 rootUID 0可以调用这些系统调用粗略估计。 用户 ID、组 ID 列表从哪里获取 在典型的 Unix 系统上登录程序以 rootUID 0身份运行。 检查提供的用户密码是否与/etc/shadow中的匹配。 根据/etc/passwd找到用户的 UID。 根据/etc/group找到用户的组。 在运行用户的 shell 之前调用setuid()、setgid()、setgroups()。 在切换到非 root 用户后如何重新获得权限 可以使用文件描述符传递但必须编写专门的代码 内核机制setuid/setgid 二进制文件。 当执行二进制文件时将进程 UID 或 GID 设置为二进制文件所有者。 通过文件权限中的特殊位指定。 例如su / sudo 二进制文件通常是 setuid root。 即使您的 shell 不是 root也可以运行su otheruser。 su 进程会检查密码如果正确则以otheruser身份运行 shell。 Unix 上有许多这样的程序因为通常需要 root 权限。 为什么 setuid 二进制文件在安全方面可能是个坏主意 对手二进制调用者操纵进程的许多方法。 在 Unix 中执行的进程会继承环境变量、文件描述符等。 setuid 程序可能使用的库不够谨慎 历史上存在许多漏洞例如传递$LD_PRELOAD… 如何防止恶意程序利用 setuid-root 二进制文件 内核机制chroot 通过路径名打开文件时更改/的含义。 不能在 chroot 树之外命名文件例如 setuid 二进制文件。 例如OKWS 使用 chroot 将程序限制在/var/okws/run中… 内核还确保/../不允许从 chroot 中逃脱。 为什么只允许 root 使用 chroot setuid 二进制文件如su可能会混淆/etc/passwd的内容。 许多内核实现无意中允许递归调用chroot()以从 chroot 监狱中逃脱因此 chroot 对于以 root 身份运行的进程来说不是一种有效的安全机制。 为什么 chroot 没有被修复以限制根进程在该目录中 Root 可以写入内核内存加载内核模块访问磁盘扇区… 背景传统的 Web 服务器架构Apache Apache 运行N个相同的进程处理 HTTP 请求。 所有进程都以用户www身份运行。 应用程序代码例如 PHP通常在每个N个 Apache 进程中运行。 任何对操作系统状态文件、进程等的访问都由www的 UID 执行。 存储SQL 数据库通常一个连接具有对整个数据库的完全访问权限。 数据库主体是整个应用程序。 问题如果任何组件被攻破对手将获得所有数据。 Web 应用可能会发生哪种攻击 无意中的数据泄露获取页面源代码隐藏文件… 远程代码执行例如Apache 中的缓冲区溢出 有 bug 的应用程序代码难以编写安全的 PHP 代码例如 SQL 注入 对 Web 浏览器的攻击跨站脚本攻击 回到 OKWS他们的应用/动机是什么 约会网站担心数据保密性。 不太担心对手闯入并发送垃圾邮件。 大量的服务器端代码执行匹配配置文件更新… 必须在用户之间共享例如匹配-- 不能只是分区。 对整体计划的良好总结 “最容易受攻击的方面对攻击者最无用” 为什么这么难 Unix 使降低权限变得棘手chrootUID… 应用程序需要以复杂的方式共享状态。 Unix 和 SQL 数据库没有细粒度的共享控制机制。 OKWS 如何分割 Web 服务器 论文中的图 1。 这个 Web 服务器中的请求是如何流动的 okd - oklogd - pubd - svc - dbproxy - oklogd 这种设计如何映射到物理机器 可能有许多前端机器okldokdpubdoklogdsvc 几台 DB 机器dbproxyDB 这些组件如何互动 okld为每个服务设置socketpairs双向管道。 一个用于控制 RPC 请求的套接字对例如“获取新的日志套接字对”。 用于日志记录的一个套接字对okld首先通过 RPC 从oklogd获取它。 对于 HTTP 服务一个用于转发 HTTP 连接的套接字对。 对于okdHTTP 服务的套接字对的服务器端 FDHTTPRPC。 okd监听一个单独的套接字以接收控制请求repubrelaunch。 在图 1 中似乎是端口 11277但在 OKWS 代码中是 Unix 域套接字。 对于repubokd与pubd通信以生成新模板 然后通过 RPC 控制通道将生成的模板发送给每个服务。 服务通过 TCP 与 DB 代理通信通过端口号连接。 OKWS 如何在图 1 中的组件之间强制隔离 每个服务作为单独的 UID 和 GID 运行。 chroot 用于将每个进程限制在单独的目录中几乎。 组件通过管道或者说 Unix 域套接字对进行通信。 用于传递 HTTP 连接的文件描述符传递。 okld的目的是什么 为什么okld不同于okd 为什么okld需要以 root 身份运行端口 80chroot/setuid。 okld启动服务需要什么 创建套接字对 获取新的oklogd套接字 forksetuid/setgidexec服务 将控制套接字传递给okd oklogd的目的是什么 pubd的目的是什么 为什么我们需要数据库代理 确保每个服务在受损时无法获取其他数据。 DB 代理协议由应用程序开发人员定义取决于应用程序的要求。 一个可能常见的代理类型是模板化的 SQL 查询。 代理强制执行整体查询结构选择、更新 但允许客户端填写查询参数。 20 字节令牌是从哪里来的 作为服务的参数传递。 谁检查令牌 DB 代理有令牌列表和允许的查询。 谁生成令牌 不清楚系统管理员手动 令牌泄露会怎样 受损组件可能会发出查询。 表 1为什么所有服务和okld都在同一个 chroot 中 这是一个问题吗 我们如何决定 那里有哪些可读写文件 可读性包含服务代码的共享库。 可写每个服务都可以写入自己的/cores/uid。 配置文件在哪里/etc/okws_config由okld保存在内存中。 oklogd和pubd有单独的 chroots因为它们具有重要状态 oklogd的 chroot 包含日志文件希望确保它没有被修改。 pubd的 chroot 包含模板希望避免泄露它们。 为什么 OKWS 需要为每个服务单独的 GID 需要执行二进制文件但文件所有权允许 chmod。 解决方案二进制文件由 root 所有服务是组所有者模式 0410。 为什么是 0410用户读取组执行而不是 0510用户读取和执行 为什么不按用户处理 每个用户是否严格更好 用户 X 服务 对于 okcupid 来说每个服务的隔离可能是有道理的。 即也许他们需要在用户之间进行大量共享 每个用户隔离需要为每个用户分配 UID使okld变得复杂。 并降低性能尽管对于某些用例可能仍然可以接受。 OKWS 是否实现了其目标 OKWS 解决了典型 Web 攻击列表中的哪些攻击以及如何解决 除了 XSS 之外的大多数问题都已解决。 通过使用专门的模板例程XSS 在某种程度上得到解决。 每个组件被损坏的影响是什么以及“攻击面”是什么 okld对 Web 服务器机器的根访问权限但也许没有对数据库的访问权限。 攻击面很小除了 svc 退出之外没有用户输入。 okd拦截/修改所有用户 HTTP 请求/响应窃取密码。 攻击面解析 HTTP 请求的第一行控制请求。 pubd损坏模板利用可能利用某些服务中的错误 攻击面从 okd 获取模板的请求。 oklogd损坏/忽略/删除/伪造日志条目。 攻击面来自 okd、okld、svcs 的日志消息。 service向用户发送垃圾访问 svc 的数据模块化 dbproxy。 攻击面来自用户的 HTTP 请求来自 okd 的控制消息。 dbproxy访问/更改其所连接的数据库中的所有用户数据。 攻击面来自授权服务的请求。 未经授权服务的请求易于丢弃。 一旦单个服务被损坏操作系统内核就成为攻击面的一部分。 Linux 内核漏洞很少见但每年仍然会出现几次。 OKWS 假设开发人员在设计层面做正确的事情也许在实现层面不是 将 Web 应用程序拆分为单独的服务而不是全部放在一个服务中。 为 DB 代理定义精确的协议否则任何服务都可以获取任何数据。 性能 似乎比大多数替代方案更好。 在负载下性能更好因此在一定程度上抵抗 DoS 攻击 OKWS 与 Apache 相比如何 总体而言更好的设计。 okld以 root 身份运行与 Apache 中没有任何东西相比但可能不重要。 两者都没有很好的解决客户端漏洞XSS 等 对手如何试图破坏类似 OKWS 系统 利用 C代码中的缓冲区溢出或其他漏洞。 在某个dbproxy中找到 SQL 注入攻击。 在服务代码中找到逻辑错误。 发现跨站脚本漏洞。 OKWS 有多成功 论文中描述的问题仍然相当普遍。 okcupid.com 仍在运行 OKWS但似乎没有被其他网站使用。 C可能不是编写 Web 应用程序的好选择。 对于许多 Web 应用程序获得 C性能可能并不关键。 设计应该适用于其他语言Python 等。 实际上6.858 实验室中的zookws受 OKWS 启发运行 Python 代码。 对于典型的 Web 应用程序DB 代理的想法并没有起飞。 但是 DB 代理对于限制服务可以访问的数据至关重要。 为什么 需要开发人员定义这些 API额外的工作会妨碍。 很难提前精确定义允许的 DB 查询。 (尽管如果很困难可能是安全策略模糊的标志。) Apache 的特权分离工作尽管仍然难以使用。 Unix 使非根用户难以操作用户 ID。 性能是一个问题为每个请求运行一个单独的进程。 scripts.mit.edu有类似的设计以不同的 UID 运行脚本。 主要担心将用户相互隔离。 偏执的 Web 应用程序开发人员可以为每个组件创建单独的锁。 敏感系统在更粗粒度上进行分区。 信用卡处理公司将信用卡数据与其他所有数据分开。 使用虚拟机或物理机器隔离来分割应用程序、数据库等。 你如何将现代 Web 应用程序框架与 OKWS 集成 需要帮助 okd 找出如何将请求路由到服务。 需要实现 DB 代理或其变体以保护数据。 取决于应用代码对静态分析的适应性。 或者需要要求程序员为服务注释可以运行的查询。 需要确保应用代码可以在单独的进程中运行可能没问题。 参考资料 css.csail.mit.edu/6.858/2014/readings/setuid.pdf httpd.apache.org/docs/trunk/suexec.html 能力和其他保护机制 注意 这些讲座笔记是从 2014 年 6.858 课程网站 上发布的笔记稍作修改而来。 混淆的副手问题 混淆的副手的作者遇到了什么问题 他们的系统有一个 Fortran 编译器/sysx/fortUnix 文件名语法 他们希望 Fortran 编译器记录使用统计信息但在哪里 创建了一个特殊的统计文件/sysx/stat。 给了/sysx/fort“家庭文件许可证”类似于关于/sysx 的 setuid 出了什么问题 用户可以调用编译器要求将输出写入/sysx/stat。 例如/sysx/fort /my/code.f -o /sysx/stat 编译器打开提供的路径名并成功因为它的许可证。 用户本身不能写入那个/sysx/stat文件。 为什么/sysx/fort只是编译器中的一个错误 原则上可以通过在各个地方添加检查来解决这个问题。 问题需要在几乎所有打开文件的地方添加检查。 完全正确的代码一旦成为 setuid 二进制文件的一部分就会变得有 bug。 那么什么是“混淆的副手” 编译器代表两个主体运行 用户主体用于打开用户的文件 编译器主体用于打开编译器的文件 不清楚在任何给定时间应该使用主体权限。 我们能在 Unix 中解决这个混淆的副手问题吗 假设 gcc 想要在/etc/gcc.stats中保留统计信息 可以有一个特殊的 setuid 程序只能写入该文件 不太方便不能像打开其他文件那样简单地打开文件。 如果我们让 gcc 成为某个非根用户统计文件所有者的 setuid会怎样 难以访问用户的原始文件。 如果 gcc 是 setuid-root坏主意但让我们弄清楚为什么… 大量潜在的缓冲区溢出可能导致 root 访问权限。 需要在 gcc 可能打开文件的每个地方进行检测。 当 gcc 打开文件时我们应该执行什么检查 如果是“内部”文件例如/etc/gcc.stats也许不需要检查。 如果是用户提供的文件需要确保用户可以访问它。 可以查看相关文件的权限。 还需要检查导致该文件的目录的权限。 潜在问题竞争条件。 如果文件在我们检查和使用之间发生更改会怎么样 常见的漏洞攻击者用符号链接替换合法文件 符号链接可能指向比如/etc/gcc.stats或/etc/passwd或… 被称为“检查时间到使用时间”的错误TOCTTOU。 对这个问题有几种可能的思考方式 环境权限 进程自动使用的权限是问题所在。任何权限都不应该自动使用。对象的名称也应该是访问它的权限。 复杂的权限检查 特权应用程序难以复制。通过简化的检查特权应用程序可能能够正确检查另一个用户是否应该访问某个对象。 什么是环境权限的例子 Unix 用户 ID组 ID。 防火墙IP 地址与访问权限 HTTP cookies例如访问 http://gmail.com 这样的 URL 通过能力给对象命名有什么帮助 传递文件描述符而不是传递文件名。 除非调用者被授权打开该文件否则无法传递有效的 FD。 我们能否使用文件描述符解决通过 setuid gcc 设置的问题 类似可以使编译器仅通过 FD 传递接受文件。 或者可以创建一个 setuid 辅助程序打开/etc/gcc.stats文件将一个打开的文件描述符传递回我们的编译器进程。 然后可以继续像处理任何其他文件一样使用这个打开的文件。 如何确保只有 gcc 可以运行这个辅助程序 使 gcc 设置为某个特殊组的 setgid。 使辅助程序仅对该特殊组可执行。 确保该组没有其他授予的特权。 Capsicum 作者试图通过能力解决什么问题 在各种应用程序中降低不可信代码的特权。 总体计划 将应用程序分解为较小的组件。 减少最容易受攻击的组件的特权。 仔细设计接口以便一个组件无法危害另一个组件。 为什么这么困难 在传统的 Unix 系统中难以降低代码的特权“沙盒”。 难以为沙盒化代码提供有限的访问权限对文件、网络等。 什么样的应用程序可能会使用沙盒化 OKWS 处理网络输入的程序 将输入处理代码放入沙盒中。 复杂操作数据的程序gzipChromium媒体编解码器浏览器插件… 将复杂且可能有错误的部分放入沙盒中。 从互联网下载的任意程序怎么样 稍微不同的问题需要隔离未修改的应用程序代码。 一个选择程序员编写他们的应用程序以在沙盒中运行。 在某些情况下有效JavascriptJavaNative Client… 需要在沙盒代码上制定一个环境标准。 另一个选择对现有代码施加新的安全策略。 可能需要保留程序员正在使用的所有 API。 需要对现有 API 施加检查在那种情况下。 不清楚访问文件、网络等的策略应该是什么。 希望避免被欺骗误用特权的应用程序 假设两个 Unix 用户Alice 和 Bob正在某个项目上工作。 两者都在某个组G中并且项目dir允许该组访问。 假设 Alice 从项目目录向某人发送一个文件。 风险Bob 可能用符号链接替换文件为 Alice 的私人文件。 Alice 的进程将隐式使用 Alice 的环境特权来打开。 可以将这看作对单个文件操作进行沙盒化。 有哪些沙盒化计划机制存在优势限制 操作系统通常提供某种安全机制“原语”。 例如在 Unix 中的用户/组 ID正如我们在上一堂课中看到的。 今天我们将研究操作系统级别的安全原语/机制。 当您关心保护操作系统管理的资源时通常是一个很好的选择。 例如文件进程粗粒度内存网络接口等。 许多操作系统级别的沙箱机制在进程级别工作。 适用于可以作为一个单元进行隔离的整个进程。 可能需要重新设计应用程序以创建用于隔离的进程。 其他技术可以提供更细粒度的隔离例如在 proc 中的线程。 语言级别的隔离例如Javascript。 二进制仪器化例如Native Client。 为什么我们需要这些其他的沙箱技术 更容易控制对非操作系统/更细粒度对象的访问。 或者也许可以以与操作系统无关的方式进行沙箱化。操作系统级别的隔离通常与更细粒度的隔离结合使用。 更细粒度的隔离通常很难做到正确JavascriptNaCl。例如Native Client 同时使用了细粒度沙箱和操作系统级别的沙箱。 将在后续讲座中更详细地讨论这些问题。 计划 0虚拟化所有内容例如VMs。 在虚拟化环境中运行不可信代码。 许多示例x86 qemuFreeBSD jailsLinux LXC… 几乎是一种不同类别的机制严格隔离。 优势VM 内部的沙箱代码几乎与外部没有交互。 优势可以沙箱未经修改的代码不期望被隔离。 优势一些 VM 可以由任意用户启动例如qemu。 优势通常与其他隔离技术可组合提供额外层次。 缺点难以允许一些共享没有共享进程管道文件。 缺点虚拟化所有内容通常会使 VM 相对较重。 每个沙箱都会带来非常重要的 CPU/内存开销。 计划 1自主访问控制DAC。 每个对象都有一组权限访问控制列表。 例如Unix 文件Windows 对象。 “自主”意味着应用程序在对象上设置权限例如chmod。 每个程序都以某些主体的权限运行。 例如Unix 用户/组 IDWindows SIDs。 当程序访问对象时检查程序的权限以决定。 “环境特权”每次访问都隐式使用的权限。 Name Process privileges| |V VObject - Permissions - Allow? 如何在 DAC 系统上例如Unix沙箱化程序 必须分配一个新的主体用户 ID 否则现有主体的权限将被隐式使用 防止进程读取/写入其他文件 在整个文件系统上更改权限繁琐不切实际需要 root 权限。 即使如此新程序也可以创建重要的可全球写入文件。 替代方案chroot同样必须是 root。 允许进程读/写某个文件 如果可能的话适当设置文件的权限。 将文件链接/移动到沙箱的chroot目录中 防止进程访问网络 Unix 中没有真正的答案。 可能配置防火墙但不是真正针对进程的。 允许进程访问特定的网络连接 如上所述在 Unix 中没有很好的计划。 控制沙盒可以杀死 / 调试 / 等的进程 可以在相同的 UID 下运行但可能特权太多。 该 UID 也可能具有其他特权… **问题**在大多数 DAC 系统上只有 root 可以创建新的主体。 例如UnixWindows。 **问题**一些对象可能没有明确可配置的访问控制列表。 Unix进程网络… **问题**文件上的权限可能与沙盒所需的策略不匹配。 可以通过使用chroot对文件进行某种程度的解决但很麻烦。 **相关问题**使用子特权执行某些操作。 回想一下 Alice 通过电子邮件将文件发送到共享组目录的示例。 “混淆副手问题”程序是多个主体的“副手”。 *一个解决方案*检查组权限是否允许访问手动容易出错。 *替代方案*明确为每个操作指定特权。 权限可以帮助能力例如fd结合了对象 特权。 一些 Unix 功能与纯能力设计不兼容按名称创建符号链接。 计划 2强制访问控制MAC。 在 DAC 中安全策略由应用程序自身设置chmod 等。 MAC 试图帮助用户/管理员为应用程序指定策略。 *“强制”*意味着应用程序无法更改此策略。 传统的 MAC 系统试图强制执行军事机密级别。 *示例*确保绝密程序无法泄露机密信息。 Name Operation caller process| |V VObject -------- Allow?^|Policy ----------- *注意*许多系统在其中具有 DAC MAC 的方面。 例如Unix 用户 ID 是“DAC”但可以争论防火墙是“MAC”。 并不重要–了解设计空间中的极端点是很好的。 Windows 强制完整性控制MIC/ FreeBSD 中的 LOMAC。 为每个进程跟踪“完整性级别”。 文件与其关联的最低完整性级别。 进程无法写入高于其完整性级别的文件。 Windows Vista 中的 Internet Explorer 以低完整性运行无法覆盖系统文件。 FreeBSD LOMAC 还跟踪进程读取的数据。 类似于许多基于信息流的系统。 当进程读取低完整性数据时它也变得低完整性。 传递性防止对手间接篡改文件。 对于沙盒化不是立即有用只有固定数量的级别。 SElinux *想法*系统管理员指定系统范围的安全策略。 策略文件指定是否应允许或拒绝每个操作。 为了帮助决定是否允许/拒绝文件标记为“类型”。 另一个整数值与 inode 中的 uid、gid 等一起存储。 Mac OS X 沙盒“Seatbelt”和 Linux seccomp_filter。 应用程序为是否允许/拒绝每个系统调用指定策略。 在 MacOSX 的机制中用 LISP 编写或者在 Linux 中用 BPF 编写。 根据参数确定系统调用的安全影响可能很困难。 路径名指的是什么符号链接硬链接竞争条件… 尽管 MacOSX 的沙盒提供了更多信息 优势: 任何用户都可以对任意代码片段进行沙盒化 限制: 程序员必须分别编写策略和应用代码。 限制: 有些操作只能以粗粒度进行过滤。 例如在 MacOSX 的过滤语言中的 POSIX shm根据 Capsicum 论文。 限制策略语言可能使用起来很尴尬无状态等。 例如如果应用程序应该与某个服务器建立精确的一个连接 注意: seccomp_filter 与常规/旧版 seccomp 有很大不同而 Capsicum 论文讨论的是常规/旧版 seccomp。 将策略与应用代码分离是否是个好主意 取决于总体目标。 如果用户/管理员想查看或更改策略可能会很有用。 如果应用程序开发人员需要同时维护代码和策略这将成为问题。 对应用程序开发人员来说可能有助于澄清策略。 较少集中的“MAC”系统Seatbelt、seccomp提供了一种折衷方案。 待办事项: 还要看看《赛里斯墙安全策略》 计划 3能力Capsicum。 不同的访问控制计划能力。 如果进程有某个对象的句柄“能力”就可以访问它。 能力 -- 对象 没有特权、访问控制列表、策略等的单独问题。 例如Unix 上的文件描述符是文件的能力。 程序无法制造未经合法获取的文件描述符。 为什么不 操作系统创建和管理文件描述符。应用程序无法伪造文件描述符。它必须通过漏洞写入操作系统内存。 一旦文件打开就可以访问它检查发生在打开时。 可以将打开的文件传递给其他进程。 文件描述符也有助于解决“检查时间与使用时间”TOCTTOU漏洞。 能力通常是短暂的不是磁盘上的 inode 的一部分。 启动程序的任何内容都需要每次重新创建能力。 全局命名空间 为什么这些人如此着迷于消除全局命名空间 全局命名空间需要一些访问控制策略例如环境权限。 难以控制沙盒对全局命名空间中对象的访问。 内核更改 只是为了再次确认为什么我们需要内核更改 我们能否将所有内容都实现在一个库中并通过 LD_PRELOAD 加载 需要操作系统在进入能力模式后拒绝应用程序访问全局命名空间 将更多内容表示为文件描述符进程pdfork。 一般来说是个好主意。 能力模式: 一旦进程进入 cap 模式就无法离开包括所有子进程。 在能力模式下只能使用文件描述符 – 没有全局命名空间。 不能通过完整路径名打开文件不需要像 OKWS 中的 chroot。 仍然可以通过相对路径名打开文件给定目录的 fdopenat。 不能在路径名或符号链接中使用“…”为什么 原则上“…” 可能没问题只要“…” 不走得太远。 难以正确执行。 假设设计 禁止在根能力中查找“…”。 路径名中的非“…”组件不得比“…”多忽略“.”。 假设一个进程对 /foo 拥有能力 C1。 在单个进程中的竞争条件有 2 个线程 竞争条件示例 T1: mkdir(C1, a/b/c)T1: C2 openat(C1, a)T1: C3 openat(C2, b/c/../..) # should return a cap for /foo/aLet openat() run until its about to look up the first ..T2: renameat(C1, a/b/c, C1, d)T1: Look up the first .., which goes to /fooLook up the second .., which goes to / … Unix 权限仍然适用吗 是的 – 仅因为你对目录有一个 cap就不能访问目录中的所有文件。 但意图是沙盒不应依赖 Unix 权限。 对于文件描述符添加一个存储允许操作的包装对象。 内核在哪里检查能力 内核中的一个函数查找 fd 号码 – 修改它以检查能力。 还修改了查找路径名的 namei 函数。 良好实践 寻找窄接口否则容易忽略检查。 libcapsicum 应用程序开发人员为什么需要这个库 最大的功能在沙盒中启动新进程。 fd 列表 主要是将大量文件描述符传递给子进程的便捷方式。 通过字符串命名文件描述符而不是硬编码的 fd 号码。 cap_enter() vs lch_start() 使用 exec 而不是 cap_enter 进行沙盒化的优势是什么 内存中的残留数据例如 OpenSSL/OpenSSH 中的私钥。 应用程序忘记关闭的残留文件描述符。 论文中的图 7tcpdump 在 stdin、stdout、stderr 上具有特权。 论文中的图 10dhclient 具有原始套接字syslogd 管道租约文件。 优点 任何进程都可以创建一个新的沙盒。 即使沙盒也可以创建沙盒。 优点 对资源访问的细粒度控制如果它们映射到 FD。 文件、网络套接字、进程。 缺点 对持久文件访问跟踪的故事较弱。 缺点 禁止全局命名空间需要以不同方式编写代码。 替代性的能力设计纯能力为基础的操作系统KeyKOS 等。 内核只提供消息传递服务。 消息传递通道非常类似文件描述符是能力。 每个应用程序都必须以能力样式编写。 Capsicum 声称更加务实一些应用程序无需更改。 Linux 能力解决不同的问题。 尝试将根的特权划分为更细粒度的特权。 由各种能力表示CAP_KILL, CAP_SETUID, CAP_SYS_CHROOT, … 进程可以以特定能力运行而不是以 root 的所有特权。 参考capabilities(7) 在应用程序中使用 Capsicum 计划 确保沙盒化进程不使用路径名或其他全局 NS。 对于可能需要访问的每个目录提前打开 FD。 要打开文件请使用从这些目录 FD 开始的 openat()。 … 打开大量文件的程序可能会很麻烦。 tcpdump 2 行版本在打开所有 FD 后只需 cap_enter()。 使用 procstat 查看生成的能力。 8 行版本还限制 stdin/stdout/stderr。 为什么避免读取 stderr 日志更改终端设置… dhclient 已经进行了特权分离使用 Capsicum 来加强沙盒2 行。 gzip 分叉/执行沙盒化的子进程通过管道使用 RPC 向其提供数据。 非平凡的更改主要是为了为 RPC 编组/解组数据409 行代码。 *有趣的错误*一开始忘记传播压缩级别。 Chromium 在其他平台上已经进行了特权分离但在 FreeBSD 上没有。 ~100 行代码用于为沙盒化进程包装文件描述符。 OKWS 家庭作业问题有哪些不同的答案 Capsicum 是否实现了其目标 使用起来有多难/容易 在应用程序中使用 Capsicum 几乎总是需要应用程序更改。 许多应用程序倾向于通过路径名打开文件等。 一个例外Unix 管道应用程序过滤器只操作 FD。 对通过 FD 处理数据的流式应用程序更容易。 其他隔离需要类似的更改例如dhclientChromium。 对于现有应用程序延迟初始化似乎是一个问题。 没有通用解决方案——要么更改代码要么早期初始化。 建议的计划沙盒化并查看哪些地方出问题。 可能会有微妙之处gzip压缩级别错误。 它提供了哪些安全保证 提供给应用程序开发人员的保证沙盒只能在打开的 FD 上操作。 结果取决于应用程序开发人员如何划分应用程序、FD。 用户/管理员无法从 Capsicum 获得任何直接保证。 保证假设 FreeBSD 内核没有错误大量代码并且 Capsicum 开发人员捕获了所有通过 FD 而非资源访问的方式。 性能开销是多少CPU内存 访问文件描述符的轻微开销。 使用fork/exec设置沙盒需要花费O(1msec)非平凡的。 特权分离可能需要 RPC / 消息传递可能会引起注意。 采用情况 在 FreeBSD 的内核中现在默认启用从 FreeBSD 10 开始。 少数应用程序已经修改为使用 Capsicum。dhclienttcpdump自论文撰写以来还有几个。参考 Casper 守护程序帮助应用程序执行非能力操作。例如DNS 查找查找/etc/passwd中的条目等。参考 Capsicum 已经移植到 Linux但不在上游内核存储库中。 有哪些应用程序不适合 Capsicum 需要控制对非内核管理对象的访问的应用程序。 例如X 服务器状态DBus在 Web 浏览器中的 HTTP 来源等。 例如需要确保 DB 文件格式正确的数据库服务器。 Capsicum 将管道视为用户级服务器例如X 服务器的一个能力。 需要从沙盒连接到特定的 TCP/UDP 地址/端口的应用程序。 Capsicum 通过仅允许对现有打开的 FD 进行操作来工作。 需要其他机制来控制可以打开哪些 FD。 可能的解决方案辅助程序可以在能力模式之外运行根据策略为沙盒化程序打开 TCP/UDP 套接字。 参考 苹果沙盒指南 seccomp_filter 强制完整性控制 赛里斯防火墙安全政策
http://www.w-s-a.com/news/82549/

相关文章:

  • 建网站要定制还是第三方系统传奇网站模板psd
  • 免费搭建企业网站什么叫网站定位
  • 网站建设cms程序员培训班
  • 网站seo技术wordpress editor ios
  • 红酒网站设计成立公司需要哪些手续
  • 广州做网站哪个好网站建网站建设网站站网站
  • 如何快速提升网站pr短剧个人主页简介模板
  • 上海网站建设 永灿百度权重3的网站值多少
  • 公司展示网站模板模板工
  • 网站建设收费详情舟山公司做网站
  • 深圳宝安区住房和建设局网站html模板大全
  • 和田哪里有做网站的地方wordpress地址更改
  • 恒通建设集团有限公司网站企业网站百度指数多少算竞争大
  • 雅虎网站收录提交入口如何使用wordpress搭建网站
  • 微商城网站建设怎么样发稿是什么意思
  • dz建站与wordpress群晖做网站服务器速度快吗
  • 做手机网站的公司网站建设 app开发 图片
  • 网站开发技术背景介绍wordpress数据库重置密码
  • 开发建设网站的实施过程是一个logo设计品牌
  • 做360pc网站排名首页工程造价信息网官网首页
  • 产品销售网站模块如何设计大数据和网站开发
  • 现在帮别人做网站赚钱不济南做网站建设公司
  • 嘉兴网站建设哪家好最近三天的国际新闻大事
  • 安丘网站建设制作做网站口碑比较好的大公司
  • 成都专业做网站公司哪家好优化大师下载安装免费
  • 防蚊手环移动网站建设广东深圳有几个区
  • 网站建设找哪些平台宜兴网站开发
  • 免费网站应用软件wordpress添加动态图标
  • 中小企业网站建设客户需求调查问卷昆明网站建设一条龙
  • 网站内容的特点wordpress 移动端网页