企业网络安全之代码审计

2019 年 6 月 18 日 计算机与网络安全

一次性付费进群,长期免费索取教程,没有付费教程。

教程列表见微信公众号底部菜单

进微信群回复公众号:微信群;QQ群:460500587


微信公众号:计算机与网络安全

ID:Computer-network

代码审计是一种提高软件安全性的方法。我们将了解一些代码审计的方法和工具,从人工到借助工具,最后会通过一些实例来看到如何通过现有的工具进行审计,以及这些工具如何帮助我们发现并消除漏洞。需要注意的是,在源代码审计阶段,一些产品安全设计上的问题可能已经较难被发现和修改,代码审计更多是发现代码实现上的错误和遗漏。


在代码量可控的情况下,并且没有很好的工具支持时,我们可以考虑通过总结经验,自己实现相关的检查工具。以找到未正确过滤数据类型的漏洞为例,定位问题可以简单分为以下几个步骤:1)标注出高危行为入口函数,2)标注数据获取来源,3)标注数据过滤函数。之后回朔调用过程,添加简单的逻辑。


1、自动化审计产品


对于代码量大的产品,人工审计明显不能满足需求,这时需要寻求工具的帮助。代码分析技术由来已久,1976年科罗拉多大学的Lloyd D.Fosdick和Leon J.Osterweil在ACM Computing Surveys上发表了著名的Data Flow Analysis in Software Reliability论文,其中就提到了数据流分析、状态机系统、边界检测、数据类型验证、控制流分析等技术。随着计算机语言的不断演进,代码分析技术也在日趋完善。目前有数量众多的开源和商业源码审计工具建立在这些分析技术之上,其中包括Foritify,Coverity,FindBugs等。这些自动化的代码审计产品能够满足对审计量和强度的要求,并且大多提供和开发环境相整合的组件,可以融入到日常的开发和编译过程当中。工具不可避免会产生漏报和误报,在处理工具生成的报告时,需要人工对生成的结果进行验证。


2、Coverity


Coverity是斯坦福大学Dawson Engler教授和他的三个学生发起完成的代码审计工具,目前为包括NASA等500多个公司或政府部门提供服务。选择其为例的原因有:1)在具体的使用中感觉误报率相对较少,2)目前有免费针对开源代码审计的服务,3)可以找到相关的原型论文。Coverity支持的语言有CC++Java,支持发现的问题类型包括:


resources leaks

dereferences of NULL pointers

incorrect usage of APIs

use of uninitialized data

memory corruptions

buffer overruns

control flow issues

error handling issues

incorrect expressions

concurrency issues

insecure data handling

unsafe use of signed values

use of resources that have been freed


C/C++为例,Coverity的分析引擎Prevent包含以下的组件和功能参见表1。

表1  Coverity的分析引擎Prevent包含的组件和功能

如果想看到实际Coverity运行的结果,可以在scan.coverity.com上浏览一些针对开源软件进行的审计结果,目前已经有为数众多的开源软件通过Coverity提高代码安全质量。图1是某个开源项目的输出结果。

图1  输出结果

https://scan.coverity.com/o/oss_success_stories中包含甄选出通过Coverity发现的漏洞列表,如图2所示。

图2  漏洞列表

点击View Defect可以看到详细问题描述,现在以一个curl(一个利用URL语法在命令行方式下工作的开源文件传输工具)中的漏洞作为例子进行查看针对某问题的具体展示结果:


https://scan.coverity.com/o/oss_success_stories/46,如图3所示。

图3  详细问题描述

这是一个存在于curl中的信息泄露漏洞。由于没有对SMB服务端返回的长度数据进行合法性检查,通过构造特定网络包数据,将导致curl客户端发送出非预期的数据,导致信息泄露。该漏洞也已被修复,分配的CVE编号为CVE-2015-3237,公告见http://curl.haxx.se/docs/adv_20150617B.html。下面是Coverity给出的函数漏洞触发流程。

696            *done = true;

697            break;

698

699          default:

700            smb_pop_message(conn);

701            return CURLE_OK; /* ignore */

702          }

703

704          smb_pop_message(conn);

705

706          return CURLE_OK;

707          }

708

709        static CURLcode smb_request_state(struct connectdata *conn, bool *done)

710        {

711          struct smb_request *req = conn->data->req.protop;

712          struct smb_header *h;

713          enum smb_req_state next_state = SMB_DONE;

714          unsigned short len;

715          unsigned short off;

716          CURLcode result;

717          void *msg = NULL;

718

719          /* Start the request */

< 1. Condition "req->state == SMB_REQUESTING", taking false branch

720

721          if(req->state == SMB_REQUESTING) {

722            result = smb_send_tree_connect(conn);

723            if(result) {

724              connclose(conn, "SMB: failed to send tree connect message");

725              return result;

726    }

727

728            request_state(conn, SMB_TREE_CONNECT);

729          }

730

731          /* Send the previous message and check for a response */

result = smb_send_and_recv(conn, &msg);

< 2. Condition "result", taking false branch

732          if(result && result != CURLE_AGAIN) {

733            connclose(conn, "SMB: failed to communicate");

734            return result;

735          }

< 3. Condition "!msg", taking false branch

737          if(!msg)

738            return CURLE_OK;

739

740          h = msg;

< 4. Switch case value "SMB_DOWNLOAD"

switch(req->state) {

742          case SMB_TREE_CONNECT:

743            if(h->status) {

744              req->result = CURLE_REMOTE_FILE_NOT_FOUND;

745              if(h->status == smb_swap32(SMB_ERR_NOACCESS))

746                req->result = CURLE_REMOTE_ACCESS_DENIED;

747              break;

748            }

749            req->tid = smb_swap16(h->tid);

750            next_state = SMB_OPEN;

751            break;

752

753          case SMB_OPEN:

754            if(h->status) {

755              req->result = CURLE_REMOTE_FILE_NOT_FOUND;

756              next_state = SMB_TREE_DISCONNECT;

757              break;

758            }

759            req->fid = smb_swap16(((struct smb_nt_create_response *)msg)->fid);

760            conn->data->req.offset = 0;

761            if(conn->data->set.upload) {

762              conn->data->req.size = conn->data->state.infilesize;

763              Curl_pgrsSetUploadSize(conn->data, conn->data->req.size);

764              next_state = SMB_UPLOAD;

765            }

766            else {

767              conn->data->req.size =

768                smb_swap64(((struct smb_nt_create_response *)msg)->end_of_file);

769              Curl_pgrsSetDownloadSize(conn->data, conn->data->req.size);

770              next_state = SMB_DOWNLOAD;

771            }

772            break;

773

774          case SMB_DOWNLOAD:

775

< 5. Condition "h->status", taking false branch

776

777            if(h->status) {

778              req->result = CURLE_RECV_ERROR;

779              next_state = SMB_CLOSE;

780              break;

}

<<< CID 1299430: Insecure data handling TAINTED_SCALAR

<<< 6. Function "Curl_read16_le" returns tainted data.

<< 7. Assigning: "len" = "Curl_read16_le", which taints "len".

781

782            len = Curl_read16_le(((unsigned char *) msg) +

783                                 sizeof(struct smb_header) + 11);

785            off = Curl_read16_le(((unsigned char *) msg) +

sizeof(struct smb_header) + 13);

<< 8. Casting narrower unsigned "len" to wider signed type "int" effectively tests its lower bound.

< 9. Condition "len > 0", taking true branch

<< 10. Checking lower bounds of unsigned scalar "len" by "len > 0".

785

if(len > 0) {

<<< CID 1299430: Insecure data handling TAINTED_SCALAR

<<< 11. Passing tainted variable "len" to a tainted sink.

786

787              result = Curl_client_write(conn, CLIENTWRITE_BODY,

788                                         (char *)msg + off + sizeof(unsigned int),

789                                         len);

790              if(result) {

791                req->result = result;

792                next_state = SMB_CLOSE;

793                break;

794              }

795            }

796            conn->data->req.bytecount += len;

797            conn->data->req.offset += len;

798            Curl_pgrsSetDownloadCounter(conn->data, conn->data->req.bytecount);

799            next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;

break;

800

801          case SMB_UPLOAD:

802            if(h->status) {

803              req->result = CURLE_UPLOAD_FAILED;

804              next_state = SMB_CLOSE;

805              break;

806            }

807    len = Curl_read16_le(((unsigned char *) msg) +

808                                 sizeof(struct smb_header) + 5);

809            conn->data->req.bytecount += len;

810            conn->data->req.offset += len;

811            Curl_pgrsSetUploadCounter(conn->data, conn->data->req.bytecount);

812            if(conn->data->req.bytecount >= conn->data->req.size)

可以看到在满足以上代码数字开头的条件后,即可触发该漏洞,简化的流程如下:


smb_request_state→ len = Curl_read16_le(…)→ Curl_client_write(…, len)


Curl客户端从服务端接到的数据包中提取出len的值,之后没有经过检查便将len作为长度参数传入Curl_client_write函数:


CURLcode Curl_client_write(struct connectdata *conn,

int type,

char *ptr,

size_t len)


Curl_client_write会将ptr指针后的len字节数据发送给服务端,即使len超过预期的数据包长度。


Curl新版本中针对这个漏洞的补丁中增加了对len的长度检查,计算后的值不能超过已收到的smb数据包的边界(smb->got)。(http://curl.haxx.se/CVE-2015-3237.patch):


---

lib/smb.c | 12 +++++++++---

1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/lib/smb.c b/lib/smb.c

index 8cb3503..d461a71 100644

--- a/lib/smb.c

+++ b/lib/smb.c

@@ -781,13 +781,19 @@ static CURLcode smb_request_state(struct connectdata *conn,

bool *done)

len = Curl_read16_le(((unsigned char *) msg) +sizeof(struct smb_header) + 11);

off = Curl_read16_le(((unsigned char *) msg) +sizeof(struct smb_header) + 13);

if(len > 0) {

-      result = Curl_client_write(conn, CLIENTWRITE_BODY,

-                                 (char *)msg + off + sizeof(unsigned int),

-                                 len);

+      struct smb_conn *smbc = &conn->proto.smbc;

+      if(off + sizeof(unsigned int) + len > smbc->got) {

+        failf(conn->data, "Invalid input packet");

+        result = CURLE_RECV_ERROR;

+      }

+      else

+        result = Curl_client_write(conn, CLIENTWRITE_BODY,

+                                   (char *)msg + off + sizeof(unsigned int),

+                                   len);

if(result) {

req->result = result;

next_state = SMB_CLOSE;

break;

}

--


至此,从这个漏洞分析过程,我们可以了解到Coverity审计的实际效果。

微信公众号:计算机与网络安全

ID:Computer-network

【推荐书籍】

登录查看更多
1

相关内容

【2020新书】实战R语言4,323页pdf
专知会员服务
98+阅读 · 2020年7月1日
干净的数据:数据清洗入门与实践,204页pdf
专知会员服务
160+阅读 · 2020年5月14日
Python计算导论,560页pdf,Introduction to Computing Using Python
专知会员服务
69+阅读 · 2020年5月5日
算法与数据结构Python,369页pdf
专知会员服务
160+阅读 · 2020年3月4日
智能交通大数据最新论文综述-附PDF下载
专知会员服务
103+阅读 · 2019年12月25日
【电子书】C++ Primer Plus 第6版,附PDF
专知会员服务
83+阅读 · 2019年11月25日
Python数据分析案例实战
炼数成金订阅号
5+阅读 · 2019年5月9日
防代码泄漏的监控系统架构与实践
FreeBuf
5+阅读 · 2019年4月30日
大数据安全技术浅析
计算机与网络安全
14+阅读 · 2019年4月24日
【数字孪生】数字化孪生“双胞胎”技术及应用
产业智能官
21+阅读 · 2018年8月12日
【区块链】区块链是什么?20问:读懂区块链
产业智能官
8+阅读 · 2018年1月10日
【入门】数据分析六部曲
36大数据
17+阅读 · 2017年12月6日
TensorFlow、Caffe、Torch 三大深度学习框架被存在安全漏洞
威胁情报浅析
计算机与网络安全
7+阅读 · 2017年11月15日
网络安全态势感知浅析
计算机与网络安全
16+阅读 · 2017年10月13日
33款可用来抓数据的开源爬虫软件工具 (推荐收藏)
数据科学浅谈
6+阅读 · 2017年7月29日
A Survey on Bayesian Deep Learning
Arxiv
60+阅读 · 2020年7月2日
Arxiv
91+阅读 · 2020年2月28日
Arxiv
11+阅读 · 2018年5月13日
Arxiv
6+阅读 · 2018年4月3日
Arxiv
20+阅读 · 2018年1月17日
Arxiv
3+阅读 · 2012年11月20日
VIP会员
相关VIP内容
【2020新书】实战R语言4,323页pdf
专知会员服务
98+阅读 · 2020年7月1日
干净的数据:数据清洗入门与实践,204页pdf
专知会员服务
160+阅读 · 2020年5月14日
Python计算导论,560页pdf,Introduction to Computing Using Python
专知会员服务
69+阅读 · 2020年5月5日
算法与数据结构Python,369页pdf
专知会员服务
160+阅读 · 2020年3月4日
智能交通大数据最新论文综述-附PDF下载
专知会员服务
103+阅读 · 2019年12月25日
【电子书】C++ Primer Plus 第6版,附PDF
专知会员服务
83+阅读 · 2019年11月25日
相关资讯
Python数据分析案例实战
炼数成金订阅号
5+阅读 · 2019年5月9日
防代码泄漏的监控系统架构与实践
FreeBuf
5+阅读 · 2019年4月30日
大数据安全技术浅析
计算机与网络安全
14+阅读 · 2019年4月24日
【数字孪生】数字化孪生“双胞胎”技术及应用
产业智能官
21+阅读 · 2018年8月12日
【区块链】区块链是什么?20问:读懂区块链
产业智能官
8+阅读 · 2018年1月10日
【入门】数据分析六部曲
36大数据
17+阅读 · 2017年12月6日
TensorFlow、Caffe、Torch 三大深度学习框架被存在安全漏洞
威胁情报浅析
计算机与网络安全
7+阅读 · 2017年11月15日
网络安全态势感知浅析
计算机与网络安全
16+阅读 · 2017年10月13日
33款可用来抓数据的开源爬虫软件工具 (推荐收藏)
数据科学浅谈
6+阅读 · 2017年7月29日
相关论文
A Survey on Bayesian Deep Learning
Arxiv
60+阅读 · 2020年7月2日
Arxiv
91+阅读 · 2020年2月28日
Arxiv
11+阅读 · 2018年5月13日
Arxiv
6+阅读 · 2018年4月3日
Arxiv
20+阅读 · 2018年1月17日
Arxiv
3+阅读 · 2012年11月20日
Top
微信扫码咨询专知VIP会员