#!/usr/bin/python
#
# MCD's Colloide v1.4
# Thessaloniki, GREECE 2017 - greekhacking.gr
# GNU General Public Lisence
# Pronounced: Kow Lawd
#
#     DO NOT USE FOR MALICIOUS PURPOSES!
#                THE DEVELOPER HAS NO RESPONSIBLITY FOR ANY DAMAGE CAUSED!
#
#
#  				 _.:' VERSION DIARY ':._
#
# In version 0.1:
#	Program is working!
# In version 0.2:
#	Minor bugs fixed	
#	Updated the links file
# In version 0.3:
#	Major bug fixed	
#	Minor bugs fixed as well	
#	Updated the links file		
#	Added option / argument parsing
#	Added legals
# In version 0.4:
#	Minor bugs fixed
#	General stracture has been updated
#	Wordlist option added - Only links.txt could be used before this update
#	Added ASCII logo
#	Added the sexy ASCII wolf
# In version 0.5 MAJOR RELEASE
#	Minor bugs fixed
#	Major bug fixed
#	Updated ASCII logo
#	Updated ASCII wolf
#	Program has been released to the masses!
# In version 0.6:
# 	Minor bugs fixed
# 	Option to save the pages into a txt page added
# 	Added some comments to help contributors read the code
# 	Added a function that checks whether the URL is valid or not
# In version 0.7:
#	One major bug fixed
#	Minor bugs fixed as well
# In version 0.8:
#	Added colors
# In version 0.8.5
#	Added status code next to links
#	Added more link wordlists, more specific to each language
# In version 0.9:
#	Added status code method
# In version 0.9.5
#	Quick robots.txt check added
# In version 1.0.0
#	Directory option added (-f) to search inside of a particular dir
#	Fixed major bug in the status method (false status code retrieved)
#	Removed status code from the urlerror method
#       Red when unauthorized or forbidden in the status method
# In version 1.0.1
#	Users are able to disable/enable ascii with -a (now -A)
# In version 1.2
#	Added link testing for 404 in Status method
# In version 1.3
#	FIXED A HUGE @$$ BUG.
# In version 1.4
#	You can chage the User-Agent with -a 
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
#   Project on GitHub:
#	http://yamechanic.com/C3vc
#   Website:
#	http://yamechanic.com/C41V
#
#   Report bugs: anivsante2@gmail.com
#   or /issues if you use github
#
# # # # # # # # # # # # # # # # # # # # # # # #

# MODULES / LIBRARIES:

from __future__ import print_function #because some print functions print quotation marks and commas
#SYSTEM MODULES#
import sys
import os
import argparse
import random
import string
#NETWORK MODULES#
import httplib
import socket
import urllib2
import requests
from urllib2 import Request, urlopen, URLError, HTTPError
#STYLE MODULES#
import colorama
from colorama import Fore, Back, Style

# VERSION
version = "v1.4"

# CODE:

def logo(): #logo - patorjk.com
	print("MCD's")
	print("_________        .__  .__         .__    .___     ")
	print("\_   ___ \  ____ |  | |  |   ____ |__| __| _/____  ")
	print("/    \  \/ /  _ \|  | |  |  /  _ \|  |/ __ |/ __ \ ")
	print("\     \___(  <_> )  |_|  |_(  <_> )  / /_/ \  ___/")
	print(" \______  /\____/|____/____/\____/|__\____ |\___  >")
	print("        \/                                \/    \/ ")
def checkasciilogo():
	check_value = open("ascii_disable_option_value.txt","r")
	checkvalue = check_value.read(1)
	if checkvalue == "0":
		logo()
	check_value.close()
def banner(): #banner with logo - patorjk.com
	checkasciilogo()
	print("MCD's")
	print("Colloide %s" % version)
	print("MD 2017")
	print("www.greekhacking.gr\n\n")
def opts():
	print("  --robots        Check for robots.txt file")
	print("    -u --URL      The URL to the website")
	print("    -c --content  Display contents of robots.txt")
	print("    -d --dump     Dump the contents to a text file\n")
	print("  --status        Use the HTTP status code method (Faster)")
	print("  --urlerror      Use the HTTP/URL error method (More reliable)")
	print("    -h --help     Display the help panel (Shown right now)")
	print("    -u --URL      The URL to the website")
	print("    -f --folder   Directory to search in")
	print("    -p --pages    Path to the wordlist with the page names / links")
	print("    -s --save     Save pages on a text file (name of the file)")
	print("    -l --limit    Add limit to the pages (Integer)")
	print("    -v --verbose  Show all attempts")
	print("    -a --agent    Change User-Agent headers\n")
	print("    -L --legals   License & legal disclaimer")
	print("    -A --ascii    Enable/Disable ASCII\n\n")
def legals():
	#License
	print("MCD's Colloide version %s is free software. It can be re-distributed " % version)
	print("and / or modified under the terms of the GNU General Public License")
	print("as published by the Free Software Foundation; For more information")
	print("read the GNU General Public License that comes")
	print("along with this program.\n\n")
	#Disclaimer
	print("[!] Legal Disclaimer [!]")
	print("Hacking without prior written permission is illegal")
	print("The developer has no responsibility for any damage caused by")
	print("malicious misuse of this program.\n")
def wolf():
	#prints the ASCII colloide wolf
	print(" ___________________      ,     ,")
	print("[ COLLOIDE MISSION! ]     |\---/|       __--__")
	print("                         /      [     ,:',.  (`. ")
	print("                    __.-'|      /    |  `'_.   .|")
	print("           __ ___.-'         \__)   |   _ : () _ |")
	print("        .-'  '        :   :  _/      |    .  .  |")
	print("       / ,    .        .   _ |        ':_)  ,_|'")
	print("      :  ;    :        :   _/             --   ")
	print("      |  |   .'     __:   /      ")
	print("      |  :   /'----'| \  |              __________")
	print("      \  |\  |      | /| |_______,-----'")
	print("       '.'| /__,----| \ | ")
	print("_______| /|.'       '.l \\\_")
	print("       || ||             '-'")
	print("       '-''-'\n")
def checkasciiwolf():
	check_value = open("ascii_disable_option_value.txt","r")
	checkvalue = check_value.read(1)
	if checkvalue == "0":
		wolf()
	else:
		print(" ___________________ ")
		print("[ COLLOIDE MISSION! ]\n\n")
	check_value.close()
def robots_check():
	print("MCD's Colloide %s" % version)
	print("Report bugs: /MichaelDim02/colloide.py/issues")
	print("Robots.txt check function\n")
	print("INFO:")
	print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
	print("URL           =       ", URL)
	robots_link = "http://"+URL+"/robots.txt"
	robots_value = False #robots_value - if it has been found or not
	try:
		IP = socket.gethostbyname(URL)
		print("IP            =       ", IP)
		robot = urllib2.urlopen(robots_link)
		print("robots        =        True")
		robots_value = True
		#robots_value - if it has been found or not

	#if it encouters url error or httperror / exception robots.txt doesn't exist and it prints false
	#WARNING: This could also mean the given URL is not correct
	#so it is prevented like so:
	except socket.gaierror:
		print("[!] Incorrect URL address")
	except URLError as e:
		print("robots        =        False")
		robots_value = False
	except HTTPError as e:
		print("robots        =        False")
		robots_value = False
	print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n")
	if robots_value == True:
		contentspage = robot.read()
		#the content that will be dumped / displayed
		if content:
			print("CONTENT:")
			print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n")
			print(contentspage)
			print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
		if dump:
			dump_name = "robots_%s.txt" % URL
			rf = open(dump_name, "w+")
			rf.write(str(contentspage))
			rf.close()
			print("\nCONTENT DUMPED AT: ", dump_name)

def scan_start():
	print(Fore.RED + Style.BRIGHT + "[!] Report bugs: /MichaelDim02/colloide.py/issues \n" + Style.RESET_ALL)

	print(Fore.RED + Style.BRIGHT + "[!] Press Ctrl + C to terminate the process.\n" + Style.RESET_ALL)

def check_names(infile):    #Checking the path to the wordlist
	if os.path.exists(infile):
		if status_method:
			banner()    #calls the banner function
			checkasciiwolf()      #calls the sexy ASCII wolf wallpaper
			scan_start()
			statusfindAdmin() #calls the function that basically does the job
			print(Fore.RED + Style.BRIGHT + "\n[+] Rock bottom;\n" + Style.RESET_ALL)
		elif error_method:
			banner()
			checkasciiwolf()
			scan_start()
			findAdmin()
			print(Fore.RED + Style.BRIGHT + "\n[+] Rock bottom;\n" + Style.RESET_ALL)
	else: #in case wordlist cant be found
		banner()
		opts()
		print(Fore.RED + Style.BRIGHT + "[-] Invalid path to the wordlist. File could not be found.\n" + Style.RESET_ALL)
# THIS IS THE STATUS CODE METHOD
def statusfindAdmin():
	if txt:
		tfilename = txt
		f = open(str(tfilename) ,'w+')
		f.write("MCD's Colloide %s\n" % version)
		print("\n")
	try:
		IP = socket.gethostbyname(URL)
		print(Fore.RED + Style.BRIGHT + "[!] Attacking host: ", IP, " - ", URL, "\n" + Style.RESET_ALL)
		print(Fore.RED + Style.BRIGHT + "[+] Status method\n" + Style.RESET_ALL) 
		if txt:
			f.write("Attacking:\n")
			f.write("\n")
			f.write(str(URL))
			f.write("\n")
			f.write(str(IP))
			f.write("\n\n")
	except socket.gaierror:
		print(Fore.RED + Style.BRIGHT + "[!] Invalid URL address. Connection could not be established;\n" + Style.RESET_ALL)
		sys.exit(0)
	#### 404 TESTING #### # # # # # # # #  #  #  #  #  #  #   #   #   #   #    #    #     #      #       #          # 
	print(Fore.GREEN + "[-] Testing a random string;" + Style.RESET_ALL)
	random_digits_for_test = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10))
	test_string = "colloide-" + random_digits_for_test
	test_con_link = "http://" + URL + "/" + test_string
	test_con_ = requests.head(test_con_link)
	test_int_status_code_ = test_con_.status_code
	if test_int_status_code_ != 404:
		print(Fore.RED + Style.BRIGHT + "[!] Testing link " + test_con_link + " did not return 404;" + Style.RESET_ALL)
		test_option_ = str.lower(raw_input(Fore.RED + Style.BRIGHT + "[?] Do you want to Switch methods (recommended), Abort or Force? [S/A/F] " + Style.RESET_ALL))
		if test_option_ == "a":
			print(Fore.RED + Style.BRIGHT + "[!] Aborting...\n" + Style.RESET_ALL)
			exit(0)
		elif test_option_ == "f":
			print(Fore.RED + Style.BRIGHT + "[!] Forcing the Status method. This might not work.\n" + Style.RESET_ALL)
		elif test_option_ == "s":
			print(Fore.RED + Style.BRIGHT + "[!] Switching method.\n" + Style.RESET_ALL)
			findAdmin()
			print(Fore.RED + Style.BRIGHT + "\n[+] Rock bottom;\n" + Style.RESET_ALL)				
			exit(0)
	elif test_int_status_code_ == 404:
		print(Fore.GREEN + "[+] Testing link " + Style.NORMAL + test_con_link + " returned 404" + Style.NORMAL + ";" + Style.RESET_ALL)
		print(Fore.GREEN + "[+] The Status method is possible; Starting now..\n" + Style.RESET_ALL)
	##################### # # # # # # # #  #  #  #  #  #  #   #   #   #   #    #    #     #      #       #          #  
	fi = open(links,"r");
	found = 0
	while (found <= int(limit)):
		try:
			sub_link = fi.readline().strip() #Page name
			if not sub_link:
				break
			link = URL #website name
			req_link = "http://"+link+"/"+sub_link #Final link for attempt
			if directory:
				req_link = "http://"+link+directory+sub_link
			if agent:
				user_agent = "Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0"
				headers = { "User-Agent": user_agent}
				con_ = requests.head(req_link, headers=headers)
			else:
				con_ = requests.head(req_link)
			int_status_code_ = con_.status_code
			status_code_ = str(int_status_code_)
			notworking = "[ATTEMPT] - "+ status_code_ + " - " + req_link
			unauth = "[ANAUTHORIZED] - "+ status_code_+ " - " + req_link
			forbid = "[FORBIDDEN] - " + status_code_ + " - " + req_link
			if status_code_.startswith("4"):
				if ver:
					if int_status_code_ == 401:
						print(Fore.RED + unauth.rstrip() + Style.RESET_ALL)
					elif int_status_code_ == 403:
						print(Fore.RED + forbid.rstrip() + Style.RESET_ALL)
					else:
						print(Fore.GREEN + notworking.rstrip() + Style.RESET_ALL)
					#notworking.rstrip() So it does not print lines between attempt output line
					#identifies links that show up HTTP error
			else:
				if ver != True:
					print("[+] Link Found" + " - " + status_code_ +" -"+ " -> " + req_link + "")
					# if verbose if off
				if ver:
					print("\n[+] Link Found" + " - " + status_code_ +" -"+ " -> " + req_link + "\n")
					#because in verbose mode failed attempts don't have \n at the end
				found = found + 1
				if txt:
					f = open(str(tfilename) ,'a')
					f.write(req_link + "\n")
				else:
					pass
		except KeyboardInterrupt:
			print(Fore.RED + Style.BRIGHT + "\n[!] Process has been terminated - Ctrl + C has been pressed.\n" + Style.RESET_ALL)
			if txt:
				print("All working pages have been saved at: ", tfilename, "\n")
			sys.exit(0)
	if found > int(limit):
		print(Fore.RED + Style.BRIGHT + "[!] Process has been terminated due to the limitation that has been set\n" + Style.RESET_ALL)
	if txt:
		print("[+] All working pages have been saved at: ", tfilename, "\n")
		f.close()
# THIS IS THE HTTP/URL ERROR METHOD
def findAdmin():
	if txt:
		tfilename = txt
		f = open(str(tfilename) ,'w+')
		f.write("MCD's Colloide %s\n" % version)
		f.write("greekhacking.gr\n")
		print("\n")
	try:
		IP = socket.gethostbyname(URL)
		print(Fore.RED + Style.BRIGHT + "[!] Attacking host: ", IP, " - ", URL, "\n" + Style.RESET_ALL)
		print(Fore.RED + Style.BRIGHT + "[+] URL Error method\n" + Style.RESET_ALL)
		if txt:
			f.write("Attacking:\n")
			f.write("\n")
			f.write(str(URL))
			f.write("\n")
			f.write(str(IP))
			f.write("\n\n")
	except socket.gaierror:
		print(Fore.RED + Style.BRIGHT + "[!] Invalid URL address. Connection could not be established;\n" + Style.RESET_ALL)
		sys.exit(0)
	fi = open(links,"r");
	found = 0
	while (found <= int(limit)):
		try:
			sub_link = fi.readline() #Page name
			if not sub_link:
				break
			link = URL #website name
			req_link = "http://"+link+"/"+sub_link #Final link for attempt
			if directory:
				req_link = "http://"+link+directory+sub_link
			if agent:
				user_agent = "Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0"
				headers = { "User-Agent": user_agent}
				req = Request(req_link, headers=headers)
			else:
				req = Request(req_link)
			if ver:
				notworking = "[ATTEMPT] - " + req_link
				#notworking.rstrip() So it does not print lines between attempt output line
			#identifies links that show up HTTP error
			try:
				response = urlopen(req)
			except HTTPError as e:
				if ver:
					print(Fore.GREEN + notworking.rstrip() + Style.RESET_ALL) 
				continue
			except URLError as e:
				if ver:
					print(Fore.GREEN + notworking.rstrip() + Style.RESET_ALL)
				continue
			else: #prints working link
				if ver != True:
					print("[+] Link Found" + " -> " + req_link + "")
					# if verbose if off
				if ver:
					print("\n[+] Link Found" + " -> " + req_link + "")
					#because in verbose mode failed attempts don't have \n at the end
				found = found + 1
				if txt:
					f = open(str(tfilename) ,'a')
					f.write(req_link + "\n")
				else:
					pass
		except KeyboardInterrupt:
			print(Fore.RED + Style.BRIGHT + "\n[!] Process has been terminated - Ctrl + C has been pressed.\n" + Style.RESET_ALL)
			if txt:
				print("All working pages have been saved at: ", tfilename, "\n")
			sys.exit(0)
	if found > int(limit):
		print(Fore.RED + Style.BRIGHT + "[!] Process has been terminated due to the limitation that has been set\n" + Style.RESET_ALL)
	if txt:
		print("[+] All working pages have been saved at: ", tfilename, "\n")

def change_ascii():
	ascii_file = open("ascii_disable_option_value.txt", "r")
	checkascii = ascii_file.read(1)
	ascii_file.close()
	changeascii = open("ascii_disable_option_value.txt", "w")
	if checkascii == "1":
		changeascii.write("0")
		print("[+] ASCII enabled")
	elif checkascii == "0":
		changeascii.write("1")
		print("[+] ASCII disabled")

#Argument parsing
parser = argparse.ArgumentParser()
parser.add_argument("--robots", action="store_true", help="Check for robots.txt file")
parser.add_argument("-d", "--dump", action="store_true", help="Dump the contents to a text file")
parser.add_argument("-c", "--content", action="store_true", help="Display the contents of robots.txt")
#robots exclusive
parser.add_argument("--status", action="store_true", help="Use the HTTP status code method")
parser.add_argument("--urlerror", action="store_true", help="Use the HTTP/URL error method")
parser.add_argument("-u", "--URL", help="The URL to the website")
parser.add_argument("-f", "--folder", help="Directory to search in (must end and start with '/')")
parser.add_argument("-p", "--pages", help="Path to the wordlist with the page names / links")
parser.add_argument("-L", "--legals", action='store_true', help="License & legal disclaimer")
parser.add_argument("-s", "--save", help="Save all working pages on a text file")
parser.add_argument("-l", "--limit", help="Add limit to the pages. Integer", default="10")
parser.add_argument("-v", "--verbose", help="Show all attempts", action="store_true")
parser.add_argument("-a", "--agent", help="Change User-Agent headers", action="store_true")
parser.add_argument("-A", "--ascii", help="Enable/Disable ASCII", action="store_true")

#Declaring Argument Variables
args = parser.parse_args()
status_method = args.status
error_method = args.urlerror
links = args.pages
URL = args.URL
directory = args.folder
agent = args.agent
txt = args.save
limit = args.limit
ver = args.verbose
ascii = args.ascii
#robots exclusive options / arguments
robots = args.robots
dump = args.dump
content = args.content

if error_method:
	status_method = False
	robots = False
elif error_method == False:
	status_method = True
	robots = False
elif error_method == False and status_method == False:
	robots = True

if args.URL and args.pages:
	check_names(links)
elif args.robots and args.URL:
	robots_check()
elif args.legals:
	banner()
	legals()
elif args.ascii:
	change_ascii()
else:
	banner()
	opts()
print("Usage:  python colloide100.py --[method] -u [URL] -p [WORDLIST] -s [TEXT FILE] -l [NUMBER] -v -a")
print("Robots: python colloide100.py --robots -u [URL] -d -c")

#
#   MCD's
#   Colloide v1.4
#   Can be modified
#   Can be distributed commercially
#   Can be distributed non-commercially
#   Under the terms of the GNU general public license (2007)
#   Project on GitHub:
#	http://yamechanic.com/C3vc
#   Website:
#	http://yamechanic.com/C41V
#
# # # # # # # # # # # # # # # # # # # # # # # # #