【推荐】【转载】治疗安软综合症, 从这里开始

=======================================================================================

声明:这篇文章是由卡饭的eLiT3CH_KSP童鞋撰写的,感谢他的分享,转载到此的目的在于让更多的人看到。

原帖地址:http://bbs.kafan.cn/thread-1239607-1-1.html

严重声明:欢迎转载和分享,但如果你将用于商业用途,请与原作者取得联系和征得原作者的同意。

=======================================================================================

 


 

 

-------------------------------------------------------------第一楼开始-------------------------------------------------------------

前言 / 声明
0. 本文是 个人计算机安全, 从入门开始 的续篇. 在阅读本文之前, 推荐阅读前篇.

1. 和前篇文章不一样, 这不是一篇标准的教程. 本文仅尝试让大家从计算机安全专业的角度去看待现实生活中各种不同的软件和安全问题.

2. 本文主要针对一般的计算机用户. 所以其侧重点在易读性. 部分例子可能会和真实的过程有偏差. 但是原理并无异

3. 学会从不同角度分析问题非常重要. 人不是计算机一样只有 "0" 和 "1" 的东西. 现实中大多数问题的答案也不只有一个. 比你学历高专业好的人不一定比你厉害. 只是人们相信他们有更大的几率比你优秀.

4. 我不是商学院学生. 文中的经济学部分不一定准确. 希望有相关专业背景的同学予以指正. 谢谢

-------------------------------------------------------------第一楼结束-------------------------------------------------------------

 


 

-------------------------------------------------------------第二楼开始 -------------------------------------------------------------

他山之石, 可以攻玉 --- 从经济学开始 

  天下熙熙, 皆为利行; 天下攘攘, 皆为利往 --- 一切利益团体的目的都是为了最大化收益. 无论什么行业, 基本如此. 学经济学的同学应该有更好的语句来诠释这个含义. 这里略去不表.

不知道有多少版友翻看过你机器上安装的各种软件的用户许可协议. 如果你这么做过, 你应该会比较熟悉如下语句:
(1) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING ...
(2) 使用本“服务及软件”由用户自己承担风险,___ 及合作单位对本“服务及软件”不作任何类型的担保,不论是明示的、默示的或法令的保证和条件,包括但不限于本“服务及软件”的适销性、适用性、无病毒、无 疏忽或无技术瑕疵问题、所有权和无侵权的明示或默示担保和条件,对在任何情况下因使用或不能使用本“软件”所产生的直接、间接、偶然、特殊及后续的损害及 风险,___ 及合作单位不承担任何责任。

由上可知, 软件厂商因为其疏忽而给用户带来的损害由用户承担.
这种直接影响他人利益却没有承担相应的义务或获得回报的行为, 经济学称之为 "外部效应" (Externality)

为了追求最大化收益, 非常多的利益团体都会 (向社会) 产生非常多的外部效应. 而绝大多数的外部效应都是负面的. 例如排放废水的工厂, 制售假药的作坊等等.
其实个人也可以看作一个小的利益体. 你可以看到每天穿行于你身边的车辆. 他们排放的废弃对你造成了影响, 但是车主并不需要支付你什么.

如果每个团体皆如此, 整个社会就乱套了.

社会本身也会有其最优状态. 而社会决策者 (一般指政府) 则尝试调整整个社会的福祉至最优状态. 例如政府可以让工厂减排污水. 虽然工厂的利益减小了, 但是周边居民的生活质量将会有更大的提升.

那么如何让工厂减排污水? 一个通常的办法就是让其对其自身产生的外部效应付出代价 --- 排污税.
税收的产生导致工厂原有的收益最大化模式被打乱. 所以要达到新的最大点, 他们就必须调整污水的排放.

在生活中, 我们可以看见非常多的这样的例子. 但是对于软件产品, 政府似乎没有太好的办法 (至少目前如此). 因为无须为自身的外部效应负责, 从整体上看, 软件本身的质量一直在下滑. (此结论来自 CMU 的软件学院于 2000 年进行的研究)

附: 也许有人会建议让软件厂商为自己的漏洞赋税. 但是这会严重影响产品创新. 同时带来其它的一些问题. 总是有很多事情是鱼和熊掌不可兼得的. 但税收的确是一个潜在的解决方案...


"用户靠边站"
上一节我们引出了用户, 软件厂商和社会决策者三个群体. 本节我们用数字来进行案例分析.

例: 某公司某软件的用户群体达到了 1000 万. 2012 年 2 月 1 日, 某研究人员发现, 该软件存在一个漏洞. 攻击者可以通过这个漏洞使用户的数据遭受损失.
假设如果用户受到攻击而导致的损失是 10 元/人. 而每过一天, 用户被攻击的几率会增加 0.02%.
已知 1000 万用户里有 1% 是签有合同的大企业优先客户, 厂商需要承担 50% 的因为其软件问题对这些客户造成的损失.
补丁研发需要投入资金. 如果厂商在 2 月 1 日发布补丁, 成本将会是 200000 元, 而每过一天, 成本将会减半.

日期 (2 月 __ 日 )
用户预期损失 厂商投入 (补丁投入 + 大客户赔偿)
社会损失 (前两项合计)
1
0 (10000000*10*0.02%*0) 200000 + 0
200000
2 20000 (10000000*10*0.02%*1)
100000 + 100
120100
3 40000 (10000000*10*0.02%*2)
50000 + 200
90200
4 60000 25000 + 300
85300
5 80000 12500 + 400
92900
...
...
...
...
11
...
195.3125 + 1000
...



从厂商的角度来看, 由于责任极小, 为了最大化收益, 理论上它会在 2 月 11 日 (有兴趣的版友可以算算看) 才发布补丁.
而从用户的角度来看, 为了减少损失, 用户希望厂商在 2 月 1 号发布补丁.
而从社会决策者的角度来看, 2 月 4 日发布补丁对社会整体造成的损失最小, 那么社会决策者将会希望补丁在 2 月 4 日被发布.

如何强迫厂商在 4 号发布补丁?
答: 如果你 4 号不发布补丁, 5 号我会让所有人知道这个漏洞...

如此, 用户被攻击的几率极大幅度提升, 导致软件厂商对大客户的赔偿金大幅提升. 而赔偿金的提升导致厂商的利益公式改变, 而最后的结果就是补丁被更早的发布了.

而公布漏洞就是通常情况下鞭策厂商的一个手段. 在美国, CERT 的期限是 45 天. 不同的国家和机构会有不同的做法.

但是我们看到了, 无论厂商怎么做, 社会决策者如何计算. 用户往往是靠后被考虑的.

作为一个用户, 你有什么看法?

功能, 功能, 更多的功能
除了外部效应之外, 还有一个名词是我们不能忽略的. 经济学上称为 "信息不对称性" (Information asymmetry).

最经典的例子是二手车辆交易. 市面上有很多优质的二手车和劣质的二手车. 对于卖家而言, 他们了解自己汽车的状况; 但是对于买家, 他们基本对车况是一无所知的. 一般来说, 优质车的卖家会标注一个比较高的价格, 劣质车的卖家会标注一个比较低的价格.

而由于买家对汽车的不了解, 他们往往会选择价格实惠的车, 这导致了优质二手车辆无人问津.

作为应对, 优质车辆卖家会将一些附加价值添加至货物里 (比如保固条款) 以显示自身货物的质量.
或者, 卖家可以去权威机构开具质量报告.
总而言之, 卖家需要给出一个信号, 而这个信号可以降低信息不对称性的影响, 从而获得更好的收益; 而买家也会做出更好的选择.

到了软件这个领域, 事情又一次的变复杂了. 由于源代码的封闭性, 软件质量标准的不透明性和无责任条款, 软件开发商不可能像实体货物卖家一样自证. 于是为了吸引客户, 他们添加了更多的功能, 设计了更炫目的界面和特效, 藉此获取更好的销售量.

当我们阅读软件新闻或浏览软件网站的时候, 我们更多的是看见销售商摆出的花花绿绿的广告和功能介绍. 但是这些组件的质量如何呢? 谁也不知道. 但有一点我们可以肯定的是, 功能的增加带来代码复杂度的增加. 而代码越复杂越多, 可能存在的漏洞就会越多.

下一章节, 我们将会查看软件中常见的安全漏洞.      

-------------------------------------------------------------第二楼结束 -------------------------------------------------------------

 

 


 

 

-------------------------------------------------------------第三楼开始 -------------------------------------------------------------

千里之堤, 毁于蚁穴 --- Control Flow Attacks   

前言
好了, 现在我们开始技术部分.
看过本文前篇的版友可能记得关于入侵的一段文字:

入侵不是魔法. 不可能在没有载体的前提下完成 --- 任何的入侵都需要通过一个存在弱点并可以获取足够权限的媒介. 例如 ARP 协议 (ARP 欺骗), TCP 协议 (SYN Flood), Windows 系统漏洞等等
入侵者通过利用这些协议 (或者服务) 本身的弱点, 发送特定的数据来完成入侵.


本节主要探讨这个过程. 首先要说明的是, 此过程本身, 既可以用来入侵 (入站), 亦可用于泄露 (出站). 只是用于泄露的时候, 入侵者需要:
(1) 确保你计算机上存在他的攻击代码可以攻击到的存在弱点的程序
(2) 攻击代码可以得到执行

举个比较容易理解且大家比较感兴趣的例子: 现在很多恶意 DLL 通过带有有效数字签名的程序来进行恶意活动. 带数字签名程序本身就是存在弱点的程序, 因为其没有对库文件进行验证. 而恶意 DLL 含有攻击代码, 可以在程序运行时被执行. 此二者缺一不可. 如果没有可执行程序本身, DLL 无法运行; 如果你换一个用来攻击另一个程序的恶意 DLL 并执行原来的程序, 那么什么也不会发生.

需要注意的是, 在这里执行数据不等于读取数据. 在计算机里, 针对数据大致有三种操作: 读, 写和执行. 这类似于从网上下载病毒样本 (网络接口读取), 如果你不执行样本, 计算机是不会被感染的.

问题的根源
很多看过柯南的同学应该记得某一集男女主在湖心的船上通过音调远程控制电话机拨号. 这就是此类攻击的一个例子. 导致这个问题的原因是电话机本身的控制信号 (拨号) 和数据信号 (通话) 都在同一个渠道里传播.

到了计算机里, 这个问题依然存在. 由于计算机底层执行的是机器语言 (0 和 1), 所以控制指令和数据看上去是完全一样的 --- 而很多数据本身来源于用户 --- 所以, 如果可以让计算机执行用户输入的数据, 那么就会导致很大的安全问题.

打开程序时, 到底发生了什么
简单来说, 当你打开一个程序时, 处理器会从文件系统读取程序运行必要的指令和数据加载到内存中, 然后执行程序指令. 而对于内存, 我们可以将其看成一栋非常高的楼房. 内存地址我们可以看做楼层. 对应上一节所说的, 数据和指令都驻扎在同一栋楼里的不同地方. 彼此之间是相互可及的.

数据驻扎的楼层大致被分为两个部分. 第一部分成为 Stack. CPU 在分配此类数据时, 会从上向下分配 (高层向低层); 第二部分称为 Heap, CPU 在分配此类数据时, 会从下向上分配 (低层向高层). 但无论是 Stack 还是 Heap, CPU 在写数据的时候, 始终是从低层到高层的.

为简单起见, 这里我们暂时针对 Stack 进行描述

随着程序指令被 CPU 执行, 可能有数据被移出这栋楼, 也可能有新的数据入住. 不过在一般情况下, 数据是会被依次安排 "入住" 的. 也就是说当 CPU 执行某个过程 (函数) 的时候, 其会在原来的基础上分配一块区域, 然后数据会依次添加进相应的楼层. 当函数执行完毕后, 这块区域会被清除掉.

利用算式和大楼, 理解 Control Flow Attacks
首先看看这个简单的算式:
10+(20-30)

拿一张有方格的纸画个表:

楼层 5 4 3
2
1  
内容



  



然后我们来填第二行的空格:
1. 开始是 10. 我们在 1 楼写上 10.
2. 我们看到了加号, 这是一个指令. 在计算机中存储指令的内存位置在另外的地方 (楼层), 我们假设为 n. 另外, 指令是顺序存储的.
2. 当我们看见每一个左括号的时候, 我们可以看作我们开始了一个过程. 在过程开始之前, 我们需要记下我们之前的位置. 所以我们在 2 楼写上 n.
3. 括号中有两个数字. 这两个数字由于是事先定义的. 所以 CPU 很容易知道需要分配两层楼的空间. 而反括号代表之前距离最近的, 以正括号开始的过程的结束. 我们空出3, 4 层, 在第五层写上 "回到 n"
4. 进入过程. 我们在 3, 4 层依次写上 20, 30.

楼层 5 4 3
2
1  
内容 回到n
30
20
n
10



如此你就完成了类似 CPU 在处理这个算式时的存取数据部分的操作 (真实情况会复杂很多, 此处从简).

下面我们来探讨一个 "过程"
和人类不同, 计算机每个内存地址可以存储的数据长度是固定的. 所以, 如果你需要使用内存空间, 你需要事先分配一个长度. 拿上述大楼的例子来说, 你需要对计算机说, 我要 Stack 部分的 6 层楼, 我依次放入 1 到 6 六个数.

CPU 看了看总楼层, 一共 1000 楼;
Stack 区域的话, 应该从上到下依次分配, 那我看看现在最高的空置楼层是多少 (假设已经分配到了 900 层)
CPU 会把第 900, 899, 898, 897, 896, 895 楼分配给你, 然后把 1 放入 895 层, 2 放入 896 层, 以此类推, 6 放入 900 层, 然后把 895 这个楼层信息告诉你. (这个例子很重要, 请在继续阅读前理解此例!)

在现实生活中, 事情变得复杂起来了. 很多时候数据是由用户来输入的, 你没有办法得知用户会输入什么.
例如当我们登录 QQ 的时候, 我们会输入密码. 有人的密码可能只有 6 位, 而另外一些人会有 20 位. 假设一个程序员申请了 10 层的空间. 而用户输入了 11 位密码 (12345678901):

楼层 950 949 948 947
946
945 944 943 942 941 940 939 938
分配空间的时候
...
回到n










n
用户输入前10位密码后
...
回到n
0
9
8
7
6
5
4
3
2
1
n
用户输入第11位密码后
...
回到1
0
9
8
7
6
5
4
3
2
1
n



如此, 过程执行完后 CPU 就会执行 1 楼的代码. 而不是本应该执行的 n 楼的代码. 而最后的 "1" 是由用户输入的, 那么从这个角度看用户具有了改变程序执行流向的能力 (Control Flow). 如果用户在前面输入了恶意代码, 然后又在最后的位置输入了恶意代码的位置 (楼层数). 那么 CPU 就会直接去执行那段恶意代码.

以上就是我们比较常听到的缓冲区溢出攻击. 缓冲区溢出攻击其实是 Control Flow Attacks 的一类. 除缓冲区溢出攻击之外, 还存在格式化字串攻击 (Format String Attack) 等手段, 有兴趣的版友可以去 Google.

对于这类攻击, 反病毒软件基本无能为力.
对于 HIPS 而言, 如果你信任了带有漏洞的程序, HIPS 也会被击破.
而某些系统程序, 如果其有漏洞, 那么无论你如何设置 HIPS 都无济于事.    

-------------------------------------------------------------第三楼结束 -------------------------------------------------------------

 

 


 

 

-------------------------------------------------------------第四楼开始 -------------------------------------------------------------

  一尺之木, 日取其半.   

本节将通过阐述绕过 ASLR 和 NX 的基本思路来充实本节甚至是本文的一个中心思想.

绕过 ASLR / NX, 从入门开始
软件安全的形势越来越严峻, 研究人员希望建立一些安全机制来应对这些问题. 其中被广泛应用的有 (但不仅限于) Canary, ASLR 和 NX.

本节将用上一章大楼的例子简化阐述 ASLR 和 NX 的机制. 如果你有兴趣了解关于此三种方式的具体实现和说明, 网上有很多的 Paper 和文档, Just Google them.

ASLR, 全称 "Address Space Layout Randomization", 中文名称 "地址分布随机化". 使用楼层分配的例子来描述, 就是在每次分配楼层的时候, 起始楼层的数字均会是楼层范围内的随机数. 这将导致攻击者无法预测表格中的 n 的位置, 从而导致攻击失败.

ASLR 的弱点之一就是在理论上, 其仍留给了攻击者一点空间. 虽然任何数据的起始位置 (楼层) 被随机化了, 但是数据的结构不会被随机化. 例如一个字串 "abcdef", 在 ASLR 开启的时候, 这个数据可能被塞到 600 楼, 可能被塞到 1009 楼, 但无论如何, "a" 的楼上肯定会是 "b", "b" 的楼上肯定会是 "c", 以此类推. 由此可知, 被填入 "目标楼层" 的地方与数据的 "起始楼层" 的位置差不会变化.

所以, 如同彩票一样, 如果攻击者恰好猜对了 n (攻击代码所在的楼层), 那么 ASLR 就被绕过了.

下面是一个例子.
假设某个 32 位的操作系统被发现存在一个缓冲区溢出漏洞. 这个漏洞可以通过覆写返回地址的方法加以利用.
设全世界有一亿计算机使用该系统, 而该系统开启了 ASLR. 已知该系统的地址会有一半被随机化.
那么同一条攻击指令每次可以成功攻击多少台使用此系统的计算机?
答案: 100000000/(2^(32/2)), 约等于 1526 台. 请注意是每次. 如果攻击者发送多次攻击代码 (仅修改 n 的值), 那么这个数字将会成倍增长.
(如果是 64 位系统呢? 100000000/(2^(64/2)), 约等于 0.02 台. 由此我们看到, 在 64 位系统下, ASLR 会有更好的作用.)

另外, ASLR 并不会随机化所有的内存空间 (楼层区间), 而被随机化了的内存空间 (楼层区间) 并不是所有数位都会被随机化 (比如上面的例子), 所以攻击者也可以针对那些没有被随机化的位置发动攻击.

NX (No eXecute) 在不同的平台上有不同的演绎方式 (例如 Windows 中的数据执行保护 DEP). 其基本思路是标记所有数据部分不可被执行. 你可以简单理解为 "可以被用户写入的内存区域, 无法被直接执行".

这个思路并非无懈可击.

由于代码部分的数据是可以被执行的. 攻击者可以通过软件编写者编写的存在漏洞的函数, 调用攻击者提供的数据作为参数来达到入侵的目的.

为了便于理解, 这里举一个例子.
假如有一个类似 QQ 的程序. 涉及登录的部分包括以下过程:

1. 获取用户名
2. 获取密码
3. 验证密码
4. 调用登陆过程



用户输入的用户名和密码将会被存储在数据段, 并且在验证密码的过程中被使用.

假设验证密码的过程如下:

1. 网络传送过程, 使用用户名作为传入参数 (CPU 会把用户输入的用户名放在已经开辟的楼层区段中)
2. 网络传送过程, 使用密码作为传入参数 (CPU 会把这些数据放在已经开辟的楼层区段中)
3. 接收服务器传回的结果, 查看是否登陆成功



首先, 攻击者精心构造一个用户名, 这个用户名超出了 CPU 分配的楼层区段造成溢出. 这个用户名可以覆盖第二个过程在代码段的入口, 将其替换为另一个过程的入口.
在第二步的时候, CPU 执行的过程不再是网络传送过程, 而是攻击者指定的, 存在于程序中的过程. 如果这个过程的作用是执行参数所包含的命令, 那么攻击者可以将攻击命令作为密码传入, 如此完成攻击.

要实现这个攻击, 必须具备两个条件.
1. 首先程序里必须具有攻击者需要的过程, 而程序是开发商制造的.
2. 程序必须存在可以被利用的漏洞.

可能大家还记得 <<他山之石, 可以攻玉>> 这章的内容 --- 现在软件的功能越来越多, 体积越来越大. 而随着软件的功能增加, 达成这两个条件可以说是越来越容易.

但不可否认的是, 类似 ASLR 和 NX 技术的出现, 极大的增加的攻击的难度 --- 每一种新技术的出现, 它们都增加了攻击成功所需要的前提条件. 因此, 系统的漏洞会越来越小.

控制, 而非消灭
看到这里大家可能比较清楚了. 安全的实现并不是像大家所想象的一样毕其功于一役, 而是逐渐的加大攻击难度. 无限的趋近于安全. 没有完全的解决办法, 没有百分之百的安全.     

-------------------------------------------------------------第四楼结束 -------------------------------------------------------------

 


 

 

-------------------------------------------------------------第五楼开始 -------------------------------------------------------------

 设计者的困局

   如果一切可以重来
提到互联网, 就不得不谈谈设计互联网的核心思想.
当科学家设计互联网的时候, 其宗旨就是要让用户可以自由的访问网络上的任何地方.

最初的互联网由于没有向公众开放, 所以并没有太多的安全机制. 主机的标识主要通过 "自报家门" 或者 "预定义" 的方式实现. 这种途径至今仍然被广泛使用, 并且造成了相当多的问题.

谈到 "自报家门", 其中的典型代表可能就要算是 TCP/IP 协议和 ARP 协议了. "自报家门" 是一种通过自我申明身份的方式提供身份信息的过程. 例如当局域网中的路由器接到一个目的为 192.168.1.10 的数据包的时候, 路由器就会向所有子网中的计算机询问 "谁是 192.168.1.10 ? 告诉我!", 然后 IP 符合这个特征的计算机会把自己的 MAC 地址回送给路由器, 然后路由器会向其发送该数据包.

在各个计算机都循规蹈矩的时候, 这个方法是没有问题的. 但是当攻击者存在的时候, 无论它是不是 192.168.1.10, 他都会说自己是. 而且, ARP 协议或者 IP 协议都没有相应的方式来证明他确实就是 192.168.1.10.

于是网络管理员出现了. 他在路由器上逐一绑定了 IP 地址和 MAC 地址. 问题解决. 而这种方式可以看作是一种 "预定义".

预定义解决了问题, 但是又带来了新的问题. 那就是, 如果网络中的计算机数量很大, "预定义" 会造成非常大的维护成本. 况且, 不是任何局域网里都会有网络管理员.

在这里, 网络管理员的设置其实可以看作是一个 "可以信任的实体". 在互联网中, 确保信任和如何信任是安全人员一直在研究的课题. 目前实现信任的主要方式就是通过预定义. 比如浏览器中内置的默认被信任的公钥, 白名单等等". 如果没有一个可以信任的基础, 那么互联网安全就无从谈起.



TCP/IP 协议是架构互联网的基础之一. 我们不可能因为它的不足无法修复就将其全盘推翻. 这样相当于你将整个互联网拆了重建. 而在这种无法修复协议缺陷, 又没办法集中管理的情况下, 我们只能使用一些临时性的解决方案.

最典型而大家又最熟悉的例子可能就是 ARP 防火墙了. ARP 防火墙的机制并非修复协议漏洞. 对于本地, ARP 防火墙试图获取正确的网关地址并绑定; 对于远端, 其不断的向网络发送正确的自身标识 (发送的频率取决于用户设置. 但是你必须保证发送频率不小于攻击者的攻击频率). 当攻击频率增加的时候, 如此竞争会耗废大量有限的网络带宽和本地资源, 可以说是伤敌一千自损八百.

这也就是为什么国外的防火墙一般不设计复杂的 ARP 防护. 因为你没办法解决下层协议的漏洞.

推广至互联网, 我们也很容易得知为什么我们无法制止 DDoS 之类的攻击 / 欺骗的根源: 因为互联网是开放的, 谁都可以尝试去访问你. 而 IP 协议本身没有验证机制, 所以在没有上层协议辅助和预定义的前提下, 你可以伪装成任何人.

互联网是人类伟大的发明, 但是和任何项目一样, 互联网也有其自身的弱点. 由于被广泛使用的协议, 人们无法直接替换之, 我们所做的, 就是在现有基础上让其更加的安全可靠.

如果互联网可以重新被设计, 它会是什么样子?

CIA

 

-------------------------------------------------------------第五楼结束 -------------------------------------------------------------

 

 


 

 

-------------------------------------------------------------第六楼开始 -------------------------------------------------------------

 

 拒绝为他人的错误埋单

安全就像一条链, 任何环节的断裂都可能导致防线被攻破. 你自己可以足够小心, 但是你无法防止他人犯错.

 

-------------------------------------------------------------第六楼结束 -------------------------------------------------------------

 

 

 

未完待续。。。。

 

posted @ 2012-08-14 10:10  r3call  阅读(738)  评论(0编辑  收藏  举报