在最近的一次安全漏洞事件处理中,需要验证某上市公司网站的一个安全漏洞,该漏洞通过黑盒漏洞扫描即可发现,并清晰的写明在漏洞扫描报告中。但由于漏洞信息的不完整,需要人工基于报告内容进行二次漏洞定位、复现。在这个过程中,我们发现存在的一个典型的关于软件外包的供应链安全问题。
假设该上市公司是A公司,在漏洞二次定位过程中,发现漏洞位置存在于该站点的404报错页面,报错页面右下方友好的提示了该公司网站开发的主体B公司,通过链接可以跳转至B公司的官网。
B公司是一家专门从事网站、小程序和App开发的公司,无独有偶,该公司官网的页面结构和URL链接与A公司的站点类似,通过刻意构造404页面,发现该页面中右下角的主体公司并非是B公司自己,而是链接到GitHub的某个项目。
该项目是GitHub上开源的YZMPHP轻量级开发框架,支持PHP5.2~PHP8.3,特点是简洁轻巧、模块式开发和容易上手,是由某个人开发者从2018年开始维护,并一直持续至今。在GitHub平台有14个关注和7个分支。同时基于该框架还有衍生开发的YzmCMS内容管理系统,主要用于个人和企业网站建设。
根据A公司官网中上述漏洞是在网站报错页面出现的,通过代码结构和内容检索,刻意定位错误输出函数halt的位置如下:
根据halt的定义接着查找该函数被调用的文件和函数,其中一个文件和代码如下所示:
在上述调用事件函数init和method_exists函数的参数ROUTE_A可以猜测,如果路由名称ROUTE_A不存在,会输出'Action does not exist : '.ROUTE_A)。而该参数是当前类定义的宏,在构造函数__construct中定义。
而宏定义中ROUTE_A是通过param.class.php文件中route_a函数从参数a获取的。
与此类似,其他两个宏定义中的参数获取方式也如此。至此XSS(跨站脚本)漏洞已显而易见,在本地搭建测试环境,通过构造简单的POC即可验证漏洞的存在。
文章开头的A公司站点的漏洞位置便在此处。
在这个案例中,A公司采用B公司外包,而B公司采用开源的开发框架进行开发,即框架特点所说,简洁轻巧、容易上手能够让开发工作的成本降低,但同时该框架从设计和实现均没有充分考虑安全性,导致同一个框架中漏洞点多且漏洞利用简单。当供应链环节没有专业的安全的评审介入时,便会因为层层采用导致最终站点的安全漏洞,因此对于软件项目,每一层的采购环节都有必要增加必要的安全检测和代码审计,既规避安全风险,又可以降低维护成本和安全代价。