Apache Pinot CVE-2024-56325 Authentication Bypass 漏洞分析
前言
前两天看到这条通告,一搜发现评分 9.8,于是分析一下具体怎么个事
https://www.openwall.com/lists/oss-security/2025/03/27/8
漏洞简介
Apache Pinot 是一个实时分布式的 OLAP 数据存储和分析系统。使用它实现低延迟可伸缩的实时分析。Pinot 从离线数据源(包括 Hadoop 和各类文件)和在线数据源(如 Kafka)中攫取数据进行分析。
Apache Pinot 存在身份认证绕过漏洞。如果路径不包含 /
且包含 .
则不需要身份验证。
影响版本
Apache Pinot < 1.3
环境搭建
参考官方文档
https://docs.pinot.apache.org/basics/getting-started/running-pinot-in-docker
这里需要开启认证机制(文档给的命令是没开的),命令中设置 -type
参数为 auth 即可
1 | docker run \ |
如需调试可以用以下操作
首先启动容器,替换 entrypoint 命令为 /bin/bash
1 | docker run -it -p 5005:5005 -p 2123:2123 -p 9000:9000 -p 8000:8000 -p 7050:7050 -p 6000:6000 --entrypoint /bin/bash apachepinot/pinot:1.2.0 |
然后自行手动启动,并添加调试的参数-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
1 | /usr/lib/jvm/java-17-amazon-corretto/bin/java --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED -Xms4G -Xmx4G -Dpinot.admin.system.exit=false -Dplugins.dir=/opt/pinot/plugins -classpath /opt/pinot/lib/*:/opt/pinot/plugins/pinot-stream-ingestion/pinot-pulsar/pinot-pulsar-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-stream-ingestion/pinot-kinesis/pinot-kinesis-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-stream-ingestion/pinot-kafka-2.0/pinot-kafka-2.0-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-batch-ingestion/pinot-batch-ingestion-standalone/pinot-batch-ingestion-standalone-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-minion-tasks/pinot-minion-builtin-tasks/pinot-minion-builtin-tasks-1.2.0.jar:/opt/pinot/plugins/pinot-segment-uploader/pinot-segment-uploader-default/pinot-segment-uploader-default-1.2.0.jar:/opt/pinot/plugins/pinot-segment-writer/pinot-segment-writer-file-based/pinot-segment-writer-file-based-1.2.0.jar:/opt/pinot/plugins/pinot-metrics/pinot-dropwizard/pinot-dropwizard-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-metrics/pinot-yammer/pinot-yammer-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-input-format/pinot-avro/pinot-avro-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-input-format/pinot-csv/pinot-csv-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-input-format/pinot-json/pinot-json-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-input-format/pinot-parquet/pinot-parquet-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-input-format/pinot-protobuf/pinot-protobuf-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-input-format/pinot-thrift/pinot-thrift-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-input-format/pinot-orc/pinot-orc-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-input-format/pinot-confluent-avro/pinot-confluent-avro-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-input-format/pinot-clp-log/pinot-clp-log-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-file-system/pinot-adls/pinot-adls-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-file-system/pinot-hdfs/pinot-hdfs-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-file-system/pinot-gcs/pinot-gcs-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-file-system/pinot-s3/pinot-s3-1.2.0-shaded.jar:/opt/pinot/plugins/pinot-environment/pinot-azure/pinot-azure-1.2.0-shaded.jar -Dapp.name=pinot-admin -Dapp.pid=1 -Dapp.repo=/opt/pinot/lib -Dapp.home=/opt/pinot -Dbasedir=/opt/pinot -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 org.apache.pinot.tools.admin.PinotAdministrator QuickStart -type auth |
访问 ip:9000 端口即可看到 Apache Pinot Controller 的认证页面
IDEA 远程调试的步骤这里不再赘述。
Apache Pinot 下载地址
https://pinot.apache.org/download/
漏洞复现
预期请求,未认证会响应 401
1 | POST /users |
构造如下 Poc 可成功绕过身份认证
1 | POST /users;. |
漏洞分析
主要关注 org.apache.pinot.controller.api 这个包
判断请求是否需要认证的方法为
org.apache.pinot.controller.api.access.AuthenticationFilter#filter
1 | public void filter(ContainerRequestContext requestContext) throws IOException { |
分析逻辑可以发现,这里的判断由两个因素组成,满足其中一个即可绕过认证,后者就是设置好的白名单,用于公共接口。
跟进org.apache.pinot.controller.api.access.AuthenticationFilter#isBaseFile
可以发现漏洞公告所提到的 “If the path does not contain / and contain . authentication is not required”。因此当这个函数返回为 True 时,filter 方法的 if 判断则为 false,即无需认证。
然后我们来看如何在必须包含.
的情况下访问正常的接口,也就是让这个点号不影响我们路由的解析。参考这篇文章可以找到 Apache Pinot 用的这款RESTful框架 —— jersey 的路由解析的逻辑和相关源码
https://blog.csdn.net/qq_30062125/article/details/83758334
在org.glassfish.jersey.server.internal.routing.RoutingStage#apply
打上断点
1 | public Stage.Continuation<RequestProcessingContext> apply(RequestProcessingContext context) { |
可以看到这里使用了前缀匹配Type.MATCHING_START
,然后继续调试跟进org.glassfish.jersey.server.internal.routing.RoutingStage#_apply
然后跟进org.glassfish.jersey.server.internal.routing.MatchResultInitializerRouter#apply
,这个方法在初始化路由匹配信息
1 | public Router.Continuation apply(RequestProcessingContext processingContext) { |
获取请求路径时参数为 false,即设置了不进行 url 解码,然后传入到 SingleMatchResult 类进行实例化
跟进 SingleMatchResult
类实例化的逻辑,可以发现这里对传入的路径进行了处理,简单说就是忽略 ;
和/
之间的内容,包括;
,如果 ;
后面没有下一个/
则忽略之后所有内容。例如 /aaa;bbb/cccc;dddd
传入该函数后会返回 /aaa/ccc
。
1 | public SingleMatchResult(String path) { |
显然这里就是让路径中包含.
号,而不影响路由解析的好办法。于是 Poc 中构造;.
在正常接口后进行绕过认证机制。
然后给处理后的路径交给org.glassfish.jersey.server.internal.routing.PathMatchingRouter#apply
匹配对应的路由规则,先匹配到 /.*
这个规则
然后进一步匹配到/users(/)?
这个规则
最终拿到对应的路由
补丁修复
可以看到这里给获取到的路径也用了路由匹配时的stripMatrixParams
方法进行处理,使点号无所遁形,也就没法构造为满足条件的路径了
结语
分析完发现这个漏洞还是有点鸡肋,需要没有/
,也就是说,只能像/aaa
这样的路由才能绕过认证,而 /api/aaa
这种就没法绕过(难道是有可以构造的方式吗)。而且这个添加用户后,用户有哪些权限呢?因为对这款产品了解不多,确实没看出来新增的用户能干嘛(希望有大佬可以解答)。不过至少获取敏感信息还是可以做到的