漏洞分析#

这个XSS漏洞发生在一个文档上传功能里,

上传文档后通过/emissary/Document.action/{uuid}接口来获取文档的信息,uuid参数对应的不同的文档。当uuid不存在时,返回一个XML格式的内容,提示uuid不存在, 但传入的uuid参数未经任何的安全过滤就原样显示在回显文本中,可导致反射型XSS。关键代码如下图:

图片展示的是与CVE - 2021 - 32092漏洞相关的关键代码。代码中定义了一个获取文档信息的接口“/Document.action/{uuid}”,使用@PathParam注解获取uuid参数。当通过该接口获取文档信息,且对应的uuid不存在时,代码会返回状态码为400的响应,响应实体内容包含提示“uuid not found”,并且传入的uuid参数未经安全过滤就原样显示在回显文本中。这与上文提到的文档上传功能中因uuid参数未过滤导致反射型XSS漏洞的分析相呼应,直观呈现了漏洞产生的代码位置及原因。

漏洞复现#

首先编译项目#

在项目编译页面,直接选中路径开始编译将会自动确认项目名为:文件名(时间) ,但是我们也可以手动设置文件名,这里使用的是emissary 5.9.0版本于是手动设置项目名为: emissary-5.9.0

图片展示的是项目编译页面中的编译项目弹窗。其中项目路径显示为“/Users/wlz/Developer/Target/yakssaExample/emissary” ,在“参数组:information”下的项目名手动设置为“emissary-5.9.0”,项目描述栏提示“请输入”,此外还有额外参数设置区域以及“取消”和“开始编译”按钮。该图片与文档中“首先编译项目”部分的内容对应,直观呈现了手动设置项目名进行编译项目的操作界面。

漏洞分析#

漏洞来源于上传文档后通过/emissary/Document.action/{uuid}接口来获取文档的信息,

首先 通过代码审计页面的搜索功能,可以快速确定到对应代码位置。

代码搜索功能将会在近期发布的SAST产品中一同和大家见面!

图片展示了代码审计页面的搜索功能操作。左侧文件列表中打开了“DocumentAction.java”文件,点击搜索图标后,出现搜索栏。搜索“Document.action”后,结果列表中显示相关代码位置。选中“DocumentAction.java”63行的结果,可点击“跳转”显示代码预览,双击或右键跳转则会在代码编辑器中打开。该功能可快速定位CVE - 2021 - 32092漏洞相关的代码位置,辅助漏洞分析。

可以找到漏洞相关信息如下:

该函数接受Get请求并且接受路径参数uuid, 尝试通过获取文件,如果没有文件的话将会进行报错,但是此时的报错直接将未经任何处理的用户输入uuid拼接返回,并且该函数定义标注了将会输出XML类型的相应。

将会导致反射XSS漏洞。

图片展示的是与CVE - 2021 - 32092漏洞相关的代码。代码中函数接受GET请求和路径参数uuid,标注了将输出XML类型响应(@Produces(MediaType.APPLICATION_XML))。当尝试获取文件失败时,报错会将未经处理的用户输入uuid拼接返回(Response.status(400).entity("<error>uuid=" + uuid + " not found</error>").build())。图片与上下文漏洞分析部分紧密相关,直观呈现了文中提到的存在反射XSS漏洞的代码位置及相关关键代码。

syntaxflow规则编写#

获取所有请求处理函数

通过Path注解和GET或POST方法可以定位请求处理函数,其中的参数为用户输入的参数。

于是我们想要搜索的目标就是: 同时被Path和GET注解的函数:

在Syntaxflow中,

  • ${annotation}.__ref__ as $instance 通过__ref__可以从注解到实例
  • ${instance}.annotation.* as ${annotation}通过anotation 可以通过实例访问到注解。

于是我们可以编写如下的规则:

// 通过Path搜索注解,通过`__ref__`从注解定位到被注解的值  过滤只定位函数
Path.__ref__?{opcode: function} as $path_handler
// 将获取到的所有注解了`Path`的数据 搜索其中的注解 
$path_handler?{.annotation.*?{have:"GET"}} as $get_path_handler 

图片展示了Syntaxflow中编写规则以获取同时被Path和GET注解函数的示例。左侧为打开的编辑器文件列表,选中的是HealthCheckAction.java。中间是该文件代码,右侧审计结果中get_path_handler下显示了相关文件及行数。底部规则编写区域有具体规则代码,通过Path.__ref__获取函数实例,再通过实例判断是否有GET注解,展示了在Syntaxflow中定位目标函数的操作及代码呈现,辅助CVE - 2021 - 32092漏洞复现中的规则编写。

获取返回数据

在emissary项目中,我们可以看到大量使用的Response.serverError().entity(xxx).build(); 或 Response.ok().entity(xxx).build(); 这种链式调用的形式,

我们可以寻找如下的代码结构:

Response (中间链式调用省略) .entity(target) (中间链式调用过程省略) .build()

在syntaxflow中,链式调用处理语法是: ..., 表示省略中间的函数调用以及成员访问,于是我们可以编写简单的代码访问到期望的代码结构:

图片展示了Syntaxflow中对emissary项目代码的审计情况。左侧是文件列表,其中“HeartbeatAction.java”被选中。审计结果中“HeartbeatAction.java”第45行有未定义问题,右侧代码区显示了该行代码“return Response.ok().entity(thePlace.toString()).build();”。下方规则编写处有代码“Response...entity...build() as $builded_response”。图片是对上文寻找“Response.serverError().entity(xxx).build()或Response.ok().entity(xxx).build()”这种链式调用代码结构及使用条件过滤语法获取entity相关内容的示例呈现。

进一步,使用条件过滤语法,我们可以得到所有最后将会被构建的entity的位置,并且得到期望的entity的参数:

// 查找到后续被build的entity
Response...entity?{*...build()} as $builded_entity 
// 获取entity的参数 
$builded_entity(, * as $target)   

示例如图所示:

图片展示了在Syntaxflow中进行漏洞复现的代码示例。左侧是文件列表及审计结果,其中“target”类别下显示了多个Java文件及相关问题,如“HealthCheckAction.java”的问题描述。右侧是“HealthCheckAction.java”文件代码,箭头指向代码中一处使用Response.serverError().entity().build()的位置。下方规则编写区域有代码“$builded_entity,(* as $target)”,这是用于获取返回数据时编写的简单代码,以访问期望的代码结构,通过条件过滤语法得到期望的entity参数。

数据流分析并得到结果#

再次确认一下入口,所有的get请求处理函数的参数中被注解为QueryParam/PathParam/FromParam/HeaderParam是请求输入的参数,或被Context注解的的请求。在这里我们只获取到参数。

// 获取请求处理函数的参数
$get_path_handler(, * ?{opcode: param} as $param)
// 过滤注解中存在*Param的参数 
$param ?{.annotation.* ?{have: "*Param"}} as $source 

于是我们得到完整的审计代码:

// 通过Path搜索注解,通过`__ref__`从注解定位到被注解的值  过滤只定位函数
Path.__ref__?{opcode: function} as $path_handler
// 将获取到的所有注解了`Path`的数据 搜索其中的注解 
$path_handler?{.annotation.*?{have:"GET"}} as $get_path_handler 
// 获取请求处理函数的参数
$get_path_handler(, * ?{opcode: param} as $param)
// 过滤注解中存在*Param的参数 
$param ?{.annotation.* ?{have: "*Param"}} as $source 

// 查找到后续被build的entity
Response...entity?{*...build()} as $builded_entity 
// 获取entity的参数 
$builded_entity(, * as $target)   

$target #{
    include:"* & $source"
}-> as $xss  

搜索得到四个结果:

我们可以成功复现cve目标: "/Document.action/{uuid}":

图片展示了在Syntaxflow中对emissary项目进行漏洞审计的代码示例及数据流分析图。左侧代码中,@GET和@Path注解的函数documentShow,其@PathParam("uuid") String uuid参数被突出显示,下方构建返回实体时也使用了uuid参数。右侧为数据流分析图,展示了uuid参数在数据处理过程中的流向及相关操作。该图片是对上文提到的通过Syntaxflow进行数据流分析并获取请求处理函数参数、实现CVE - 2021 - 32092漏洞复现内容的示例说明。

还有一个非常相似的xss"/TransferDirectory.action":

图片展示的是一段Java代码示例。代码中定义了一个被GET和Path注解的函数dumpDirectory,Path注解参数为"/TransferDirectory.action" 。函数接收一个@QueryParam注解的字符串类型参数dirname。代码通过判断dirname是否为空进行不同处理,若不为空则进行查找操作,根据查找结果返回不同的Response,若查找失败返回状态码404且entity包含dirname参数内容。该示例与上文提到的xss漏洞"/TransferDirectory.action"相关,展示了获取期望entity参数的代码结构。

误报以及排除#

另外可以发现一个类似,但是输出为MediaType.TEXT_PLAIN的位置,这其实是没有问题的,在规则中我们没有进行过滤:

图片展示的是一段代码片段,其中有两处内容被红色框高亮显示。一处是第26行代码中的“@Produces(MediaType.TEXT_PLAIN)”,另一处是第38行“Could not find " + name”和第41行“Namespace did not find " + name”。有红色箭头指向这两处高亮部分。结合上下文可知,此图片对应文中提到的输出为MediaType.TEXT_PLAIN的位置,该位置在规则中未进行过滤,是后续搜索、过滤及更新检查规则所针对的内容。

先来对注解进行搜索,在get_path_handler的列表中通过.annotation获取注解,在通过.value获取注解内的数据:

// 获取Produces注解, 并且通过`.value`获取其中的数据,筛选所有的`TEXT_PLAIN` 
$get_path_handler.annotation.Produces.value?{have:"TEXT_PLAIN"}  as $produces

运行发现可以获取到所有TEXT_PLAIN注解:

图片展示了代码审计界面,左侧是文件列表,中间审计结果显示 Env.java 的第39行存在 Undefined-Produces.value(valid) 问题。右侧代码区域中,@Produces(MediaType.TEXT_PLAIN) 代码行被红色箭头指向并提示“这里的代码有点问题”。下方规则编写部分,有代码 $get_path_handler.annotation.Produces.value?{have:"TEXT_PLAIN"} as $produces 被红框标注。该图与上下文关系为,通过在 get_path_handler 列表中搜索注解获取数据,展示运行后获取到所有 TEXT_PLAIN 注解的情况。

那么我们可以将这个搜索转换为过滤并且更新检查规则:

// 通过Path搜索注解,通过`__ref__`从注解定位到被注解的值  过滤只定位函数
Path.__ref__?{opcode: function} as $path_handler
// 将获取到的所有注解了`Path`的数据 搜索其中的注解 
$path_handler?{.annotation.*?{have:"GET"}} as $get_path_handler 
// 获取Produces注解, 并且通过`.value`获取其中的数据,筛选所有的`TEXT_PLAIN` 
$get_path_handler?{! .annotation.Produces.value?{have:"TEXT_PLAIN"}}  as $get_path_handler_could_xss
// 获取请求处理函数的参数
$get_path_handler_could_xss(, * ?{opcode: param} as $param)
// 过滤注解中存在*Param的参数 
$param ?{.annotation.* ?{have: "*Param"}} as $source 

// 查找到后续被build的entity
Response...entity?{*...build()} as $builded_entity 
// 获取entity的参数 
$builded_entity(, * as $target)   

$target #{
    include:"* & $source"
}-> as $xss  

运行发现刚刚的漏报已经解决:

图片展示了代码审计界面,左侧是文件列表,选中的是“DocumentAction.java”。右侧是该文件的代码内容,其中第65 - 84行代码区域被显示有“这里的代码有点问题”提示。下方还有规则编写的相关内容,如通过注解定位、筛选等规则语句。图中红框和箭头突出显示了代码问题提示和关键代码行,结合上下文,这是在对CVE - 2021 - 32092漏洞复现过程中,对代码进行检查和规则编写以解决漏报问题时所呈现的界面情况。

保存到规则列表并进行扫描#

保存到规则列表,编写相关信息,并且使用alert $xss 报告错误到漏洞与风险

图片展示的是Syntaxflow中新建规则的界面,用于编写XSS检测规则。左侧是基础信息填写区域,规则名为“XSS检测 GET to Response.entity.build”,语言选择Java。右侧规则内容部分,代码以注释形式对风险类型、规则目的、风险等级等进行说明,如风险类型为xss,规则目的是检测漏洞,风险等级为高。代码还通过一系列路径搜索、注解筛选等操作查找XSS漏洞,最后使用alert报告检测到的漏洞信息。此图片对应文档中保存到规则列表并编写相关信息以扫描漏洞风险的内容。

运行规则 可以扫描得到漏洞风险相关信息:

图片展示了运行规则扫描后得到的漏洞风险相关信息。上方显示规则执行状态为1/1,执行成功率为100%,漏洞/风险状态数量为2。下方风险与漏洞列表中,有两条“XSS检测 GET to Response.entity.build”记录,所属项目均为emissary(2025-0312-15:26)。再下方展示了其中一条记录的相关代码,代码中“@QueryParam(TARGET_DIR_PARAM) String dirname”部分以黄色高亮显示。图片与上文提到的运行规则扫描得到漏洞风险信息相呼应。