这PPT值得好好理解,审计思路可拓展至任何语言的框架、前端、后端、数据库操作逻辑审计
如有翻译失误,参照原文混合阅读理解即可,pdf见附件
WordPress是一个非常流行的内容管理系统,超过34%的人使用它搭建网站。易用性、兼容性、免费性和庞大的插件库(超过5w个)使WordPress成为第一个无需任何建站知识和开发预算即可快速轻松地建立网站的系统。 WordPress可以进行自定义和优化,因此很多政府网站或者市值百亿的公司也在使用此CMS来开发管理他们的网站。
黑客们一直很热衷于对WordPress进行漏洞挖掘,他公开公平公正的bugBounty计划,吸引了各路神仙的审计与挖掘,当然少不了有些0day收藏所需导致的神秘力量也在挖呢。。得益于此,WordPress的迭代更新周期也很快,所以审计这个最受欢迎的CMS源码是一个巨大的挑战,但同时也是学习代码审计思路的优秀案例,因为他更新的真滴贼快,时效性贼强:P
当开始对WordPress核心代码进行漏洞挖掘时,我们很快意识到要找到关键漏洞,就必须脱离日常Web应用程序中查找简单漏洞的方法(黑盒),转而使用更有效的方法和手段来进行挖掘(代码审计)。
本文记录了我们将源代码分离为组件,将几个影响较小的bug组合为功能强大的特权升级和远程代码执行exp-chain的过程。文中我们陆续发现了五个漏洞,最终完成从【未验证漏洞】提升到【任意远程代码执行】的流程。所有问题均已披露给WordPress安全团队并已发出补丁。
我们相信公开此漏洞审计的文章不仅可以帮助其他研究人员优化审计方法,还可以帮助开发人员更好地了解攻击者的思维方式,并为提高安全开发意识而打下基础。
本章介绍了我们审计WordPress的方式。 首先一起回顾最常见的审核源代码的方法,了解并当前被审计框架(例如WordPress)的bug缺陷。然后,我们跟随文章案例介逐一讲解我们所使用的审计思路。
挖掘Web应用程序中漏洞的最简单方法是【用户输入】→【危险函数】。 对传入数据直接丢进【危险函数】是一个脑残的开发方式,可能使【不受信任的用户输入】被直接带入执行,执行前我们应该对任何输入内容进行过滤处理。 例如:数据库操作、文件写入或命令执行等敏感操作时,这些操作可能会被攻击者利用,通过逃逸或者反序列化等,最终导致不可控的外部命令非法注入。
此类漏洞的一个非常简单的示例如下所示:
$dir = $_GET['dir']; // something... system("ls " . $dir);
通过随机传入特殊fuzz内容,研究人员可以很快通过黑盒发现此类漏洞。当然,通过审计中层层步进的审计方式,手工验证数据是否经过处理后安全传递给危险函数(或逃逸传递逻辑),出奇制胜风味更佳。
由于WordPress等通用框架的0days价值很高,使用【用户输入】→【危险函数】这种静态审计的方法已经被老练的黑客团体、国家队和研究人员挖烂了。流行的通用框架中搞出这种“教科书式漏洞”的效率越来越低。所以如今的0day多半极其复杂,这种通用框架的漏洞,多半需要2-3个组合拳利用链才能完成整个攻击过程。这意味着越来越多的中间步骤夹杂在【用户输入】到【处理逻辑】的过程中。更恶心的是在每个步骤之间,还有许多需要精心设计才能满足的先前逻辑条件,就特么跟CTF故意的一样。艹
我们使用RIPS的静态扫描器WordPress核心代码时,着重于发现【用户输入】→【处理逻辑】中的看似低影响或没有太多直接影响的小缺陷。通常,那些总有各种传入限制或只会引发一些不痛不痒的错误的洞,被某些只想“一键日天”的人视为卵用没有。
其实许多可控的gadgets并不是真正意义的漏洞,看起来只是导致功能出现偏差(弱类转换/变量覆盖)或影响非常有局限性(self-xss/需要其他权限的操作)。同时网络犯罪分子、赏金猎人们和0day自动挖掘工具,仅感兴趣于或仅能发现表层“一发入魂”的直接造成高危影响的洞。因此,这些没卵用的bug,可能并没被注意也没被上报。那么问题就来了。艹
为了找出新高危利用方式,我们选择了全面审查代码中最牛批有效的方法:“无敌组合拳exp-chain”。单合并多个低影响力漏洞难在把他们联系起来。不同利用点要不不在一个逻辑里,要不干脆不在一个module里,要不干脆功能上没有半毛钱关联。但正是毛线关系没有,导致这种利用链很难发现(虽然你很难发现,但可喜可贺别人也难得发现,呵呵呵)。当研究人员仅尝试通过代码审计跟踪【用户输入】是否直接传递给关键【危险函数】时,由于通常无法直接绕过这些限制,研究人员通常放弃继续跟,因为看上去这个洞就是卵用没有。但是,或许可以通过不同的方式来打破限制。(联想nginx配置失误后php_pathinfo的洞)
让我们看一下这种组合洞的实例,在WordPress中发现。我们使用了两个漏洞(特性),在代码审计时并没看到他俩有卵毛线关系。所以分别单个审视时他们是不可利用的。(请注意,以下示例需要管理员权限才能进行利用并作为一个案例)尽管默认情况下管理员可以通过上传新的插件和主题来执行任意PHP代码,但这类弱点也可以在安装WordPress后进行改进修复。所以我们要找到另一个(后漏洞分析中会提到的)未认证漏洞。
我们使用静态代码分析工具发现了一个WordPress主题中的本地文件包含(LFI)的漏洞(RIPS漏洞报告)。它允许攻击者从当前目录里include()
执行任何文件。 WordPress中的主题是可能只会包含模板文件以及css文件/js文件。因此此LFI漏洞是有局限性不能直接利用的。
WordPress通过限制为当前主题的目录,来确保传递给include()
的参数是安全的,我们不可能从其他目录绕过限制引发漏洞,至少在正常的经过安全加固的WordPress中,我们无法上传其他文件到该主题的目录或修改主题目录中已存在的文件。这意味着尽管可以在当前主题目录中实行任意文件包含,我们也包含不到我们的payload,艹。
因此,这个点只能被看作是个弱点,但不是安全漏洞。(在撰写本文时这个弱点也尚未被WordPress修补)但是,如果我们找到个方法把payload传入到这个主题目录下呢?
由于WordPress限制了所包含的模板文件必须位于当前主题的目录下,所以唯一的利用方法是以某种方式修改主题文件或者将payload文件上传/移动到主题目录。找到这样一个利用点,我们决定对WordPress中的一些上传/修改文件的组件进行审计。我们减少了对插件的研究,开始看看更新功能和媒体文件上传功能,因为这些点可控也必定出现文件操作。但是,我们很快意识到,去审计WordPress的文件上传功能必定一无所获,因为各种模块里上传文件有各种奇葩限制并且还会对文件名进行过滤。
审计WordPress中间件(如:过滤器)在文件上传过程中的作用,发现上传不同文件类型时中间件的处理方式也不同。因此,我们将每种文件类型的上传功能分解为如下线性方式。例如当.txt
类型文件上传到WordPress中时,将经过:
1.清理文件名
2.从数据库相关upload_path
设置中获取.txt
类型文件应该移动到的目录
3.将文件移动到目标目录
抽象理解了.txt
文件上传过程后,我们发现了利用之前局限性LFI漏洞的方法,如果能够对upload_path
设置进行可控的任意修改,那么之后.txt
文件就能被上传到我们制定的目录。
如图,我们可以修改upload_path
指向任意目录,比如:如果WordPress使用了默认主题twentyninteen
,那么我们只要修改upload_path
为wp-content/themes/twentyninteen
就能绕过之前include()
没有办法上传文件到主题目录,但又必须包含在主题目录下文件的限制,从而继续利用了。通过结合两个弱点,我们即可完成一个任意php代码执行(RCE啊我艹)
上述案例只是一个漏洞的示例,完整流程是当两个位于不同位置的弱点,能组合利用的时候才能完整触发。因此,传统的【用户输入】→【危险函数】审计方法是不可能很好发现这种利用方法的,如果只看include()
的代码块只能找到个无法利用的弱点,虽然对于修补潜在风险发现问题马上修补这个方法已经够了,但作为漏洞研究员,我们不能直观的把这些弱点与其他组件的功能联系起来利用,所以我们需要一种更标准的审计挖掘方法(思路)帮助我们快速统计各种弱点并尝试组合,将这些低位弱点按正确的逻辑顺序拼成一个牛批的高危0day。
因此,我们重新组织挖掘方法分为四个步骤来开展审计。
由于影响较小的弱点之间的联系,通常位于【Web应用程序的逻辑】和【框架的整体结构】之间,因此将【Web应用程序】解构成【模块组件】是有意义的,每个【模块组件】相对于【整体结构】和【web应用程序】都有独特的存在目的,所以不能简单地只把整个【Web应用程序】看作是一堆的函数和类去直接审计。
例如,分出一个【主题组件】它就只处理WordPress主题相关的功能和逻辑。另一个【文件组件】可能是WordPress的文件管理组件,它只负责处理所有文件操作。考虑这些组件的另一种方法是黑盒测试:组件接收数据、进行处理、继续传递。例如【主题组件】接收数据,它就应该显示博客文章的类型,以及此处应该使用模板的数据。(稍后我们通过一个实例来理解)
当研究员审计特定功能时我们应该去细分【模块组件】,如实现“文章创建”的功能,就可以细分成由很多【模块组件】组成。
例如,在WordPress中创建文章时【用户输入】将通过多个【模块组件】传递,最后整体组成“文章评论”功能:
如上,我们可以将“文章创建”功能分解成WordPress中的五个不同【模块组件】所组成。
将功能分解为多个模块组件,我们就可以对每个参与的模块组件按顺序跟进并发问:“这个模块组件为什么在这?又TM是干嘛的?有没有可能出错?”
例如,在(二)中,当我们分析WordPress文章创建时存在一个【SEO组件】,该组件在过滤输入后,会修改文章中的HTML标签,因此关于问题“这TM是干嘛的?有没有可能出错?”,分析发现,该【SEO组件】用来分析、修改和优化文章中用于SEO的中HTML标签。 如果可以截断解析和影响修改过程则可能会出现XSS漏洞。 我们将在稍后提供更多实际示例,这些示例讲述了我们如何找到单个组件中的漏洞。
当研究人员明确了寻找目的时,他们可以更有效地搜索或扫描单个组件中的缺陷。从模块组件归类(模块组件多少取决于被审计功能目的、复杂程度等)中审计单个组件的缺陷,使我们可以发现影响较小的缺陷,否则这些小缺陷很容易被忽略。
总结前面的步骤,我们首先必须:
然后,我们可以挖掘所发现的弱点与其余功能之间的关系,思考如何将它们组合在一起变成一个完整的exp-chain。
在这最后一步中我们可能遇上一系列困难。这些弱点仍然存在于源代码中的原因通常是“总之存在某些限制”导致无法直接利用。但是,只要研究人员了解了各步骤的局限性,以及找到bypass的解决方案,他就可以开始整体审计找出最终关键的切入点。由于这些限制是开发中非常普遍的,因此在应用程序中仍然存在这种(因有限制没有被修改的)弱点的可能性非常高。
为了完整利用上文中“局限性LFI漏洞”的那个例子,我们必须找到一个新的切入点,让我们可以将任何文件上传到特定目录。由于此类错误不会直接导致远程代码执行,因此我们可以大胆假设,其他挖洞的人和官方review说不定将其忽略了。为了有效地发现一个能bypass限制的利用点,我们可以看看该模块组件存在于哪些功能里,然后在这几个使用了问题组件的功能中,去寻找能触发组件漏洞的第一个入点,这样我们审计工作量就从“大海捞针”全局搜寻,缩减为“有的放矢”的只审计包含问题组件的那几个功能了。
上文中“身份验证的远程执行代码漏洞”的示例我们就是这样做审计的。回顾一下:
清楚了这一点后,我们将研究范围减小到WordPress的插件更新组件和文件上传组件上。然后,我们使用了如上审计的步骤,将每个功能分解为一系列包含在内的组件,以便从漏洞点的上游组件中,找到允许我们将文件上传到特定目录的利用点。下一节中查看更复杂的示例。
在本节中,我们通过分析源代码功能再分解为组件,并归纳组件之间的联系,来演示审计WordPress的方法。然后,我们来分析一下此方法所找到的真实的安全漏洞。并非审计过程中的所有部分能对应到特定的一个审计公式或固定准则(也有经验之谈和fuzz瞎猜),但是我们总结的方法能让流程更简洁易懂。
下面我们将进一步介绍有关WordPress内部结构的技术细节(无需事先预习WordPress知识)。每个错误的出现都关乎我们需要的特定功能缺陷,最后建立出完整的exp-chain。我们首先在WordPress的管理员面板找到了一些小错误,并将这些小错误联系到一个需要身份验证的代码执行漏洞。但是该漏洞需要管理员权限(这不是我想要的)。为了提升威力,我们尝试降低权限在目标站点上寻找能未授权RCE的切入点。
最终,我们设法找到了一个未授权的漏洞,该漏洞使我们可以接管高权用户的会话,并利用任何已经过身份验证的用户会话来远程执行我们的payload。
( 未完,下一篇见:P )