didi / didi/KnowStreaming

Missing server-side authorization allows any authenticated user to escalate to platform administrator

Open
#1,263 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
7.2k
Forks
1.3k
PR merge metrics
No merged PRs in 30d

Description

v3.4.0

### Summary
KnowStreaming ships a role/permission model (roles, permission points, resource scopes) that its own
web UI uses to decide which menus and buttons to show a user. That model is never checked on the
server. The single interceptor that guards every REST endpoint under `/api/v3/**` and
`/logi-security/api/v1/**` only verifies that the caller has a valid authenticated session — it does
not look at which permission points, roles, or resources that session is entitled to. Consequently any
authenticated user, regardless of assigned role, can call any endpoint with full effect, including the
identity-management endpoints that create users and assign roles. This lets a user holding the lowest
possible privilege level (e.g. a single view-only permission point) grant themselves the built-in
administrator role, or mint a brand-new administrator account, from the REST API directly, bypassing
the UI entirely.

### Details

The only authorization gate for the whole API is `PermissionInterceptor.preHandle()`:

`km-rest/src/main/java/com/xiaojukeji/know/streaming/km/rest/interceptor/PermissionInterceptor.java:74`
```java
return loginService.interceptorCheck(request, response, classRequestMappingValue, whiteMappingValues);
```

`loginService` is `com.didiglobal.logi.security.service.impl.LoginServiceImpl`, which forwards to
`com.didiglobal.logi.security.extend.impl.DefaultLoginExtendImpl.interceptorCheck()` (decompiled from
`logi-security-spring-boot-starter-2.10.13.jar`, the exact version pinned in KnowStreaming v3.4.0's
`pom.xml`):

```java
public boolean interceptorCheck(HttpServletRequest request, HttpServletResponse response,
String requestMappingValue, List whiteMappingValues) throws IOException {
if (StringUtils.isEmpty(requestMappingValue)) { ...; return false; }
for (String mapping : whiteMappingValues) {
if (requestMappingValue.contains(mapping)) return true;
}
if (!this.hasLoginValid(request)) {
this.logout(request, response);
return false;
}
String operator = HttpRequestUtil.getOperator(request);
User user = this.userService.getUserByUserName(operator);
if (user == null) throw new LogiSecurityException(ResultCode.USER_NOT_EXISTS);
this.initLoginContext(request, response, operator, user.getId());
return true;
}
```

`hasLoginValid()` only checks that the `X-SSO-USER` session/cookie identifies an existing user row.
Nothing in this method (or anywhere else reachable from `PermissionInterceptor`) reads the caller's
`logi_security_role`, `logi_security_role_permission`, or `logi_security_user_resource` rows and
compares them against the requested URL or resource id. No controller in the `km-rest` module carries
any permission-checking annotation either — a full grep of the source tree for
`@RequiresPermission|@PreAuthorize|@CheckPermission|@Aspect` returns zero matches. The permission-point
tree seeded in `km-persistence/src/main/resources/sql/dml-logi.sql` (e.g. permission id 1637
"用户管理-新增角色" / "user-mgmt: add role", id 1641 "用户管理-分配用户角色" / "user-mgmt: assign role
to user") exists solely to drive which buttons the Vue frontend renders for a given role.

The identity-management endpoints that make this exploitable are themselves unguarded, and the vendor's
own Swagger annotation on one of them documents the gap directly (decompiled from the same jar,
`com.didiglobal.logi.security.controller.v1.UserController`):

```java
@PutMapping(value={"/add"})
@ApiOperation(value="用户新增接口,暂时没有考虑权限", notes="") // "user-add endpoint, permission not yet considered"
public Result add(HttpServletRequest request, @RequestBody UserDTO param) {
return this.userService.addUser(param, HttpRequestUtil.getOperator(request));
}
```

`RoleController.assign()` (`POST /logi-security/api/v1/role/assign`) is equally unguarded and, per
`RoleServiceImpl.assignRoles()`, replaces a target user's role set wholesale when `flag=true` and
`id` is set to that user's own id — allowing self-service role assignment with no ownership or
elevation check.

### PoC
(available upon request)

### Impact

Any user KnowStreaming grants an account to — even one deliberately restricted to a single read-only
permission point, the minimum the platform allows — can silently become a full platform administrator
in one HTTP request, or create a hidden administrator backdoor account. Because KnowStreaming's
administrator role controls every registered Kafka cluster it manages (topic create/delete/truncate,
consumer group offset reset, ACL management, cluster registration, and all other user/role
administration), this is a complete compromise of the platform and, transitively, of every Kafka
cluster it has been given credentials to manage. The same root cause (the interceptor performs
authentication only, never authorization) applies uniformly to every controller in the `km-rest`
module — Topic, Cluster, ACL, Connect, and Group management endpoints carry no permission annotations
either — so the identity-escalation path documented here is simply the most direct and severe instance
of a defect affecting the entire API surface.

Contributor guide

Open the contributing guide

Research direction

Start with km-rest/src/main/java/com/xiaojukeji/know/streaming/km/rest/interceptor/PermissionInterceptor.java:74 and trace LoginServiceImpl and DefaultLoginExtendImpl.interceptorCheck. Review the permission data in km-persistence/src/main/resources/sql/dml-logi.sql and the role and user endpoints described in the report. Done means low-privilege authenticated users can no longer perform role assignment, user creation, or other unauthorized REST operations.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, kafka
Domain
authorization, backend-api-design, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.