Skip to content

Commit

Permalink
Merge pull request #10 from Someniak/updated-to-python3
Browse files Browse the repository at this point in the history
Updated Chamaleon to support Python3 and new analyser APIs
  • Loading branch information
mdsecactivebreach authored Jul 21, 2020
2 parents 01025b8 + 81c791c commit d222ce6
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 94 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
```
# Description

Chameleon is a tool which assists red teams in categorising their infrastructure under arbitrary categories. Currently, the tool supports arbitrary categorisation for Bluecoat, McAfee Trustedsource and IBM X-Force. However, the tool is designed in such a way that additional proxies can be added with ease.
Chameleon is a tool which assists red teams in categorising their infrastructure under arbitrary categories.
Currently, the tool supports arbitrary categorisation for Bluecoat, McAfee Trustedsource and IBM X-Force.
However, the tool is designed in such a way that additional proxies can be added with ease.

# Usage

Expand Down
36 changes: 20 additions & 16 deletions chameleon.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,73 @@
import sys
from modules import *


class Chameleon:

def __init__(self):
pass

def validate_args(self):
parser = argparse.ArgumentParser(description = "")
parser.add_argument("--proxy", metavar="<proxy>", dest = "proxy", default = None, help = "Proxy type: a = all, b = bluecoat, m = mcafee, i = IBM Xforce")
parser.add_argument("--check", action='store_true', help = "Perform check on current category")
parser.add_argument("--submit", action='store_true', help = "Submit new category")
parser.add_argument("--domain", metavar="<domain>", dest = "domain", default = None, help = "Domain to validate")
parser = argparse.ArgumentParser(description="")
parser.add_argument("--proxy", metavar="<proxy>", dest="proxy", default=None,
help="Proxy type: a = all, b = bluecoat, m = mcafee, i = IBM Xforce")
parser.add_argument("--check", action='store_true', help="Perform check on current category")
parser.add_argument("--submit", action='store_true', help="Submit new category")
parser.add_argument("--domain", metavar="<domain>", dest="domain", default=None, help="Domain to validate")
args = parser.parse_args()

if not args.proxy:
print "[-] Missing --proxy argument"
print("[-] Missing --proxy argument")
sys.exit(-1)
if not args.domain:
print "[-] Missing --domain argument"
print("[-] Missing --domain argument")
sys.exit(-1)
if not args.check and not args.submit:
print "[-] Missing --check or --submit argument"
print("[-] Missing --check or --submit argument")
sys.exit(-1)
return args

def show_banner(self):
with open('banner.txt', 'r') as f:
data = f.read()
print "\033[92m%s\033[0;0m" % data
print("\033[92m%s\033[0;0m" % data)

def run(self, args):
if args.proxy == 'm' or args.proxy == 'a':
print "\033[1;34m[-] Targeting McAfee Trustedsource\033[0;0m"
print("\033[1;34m[-] Targeting McAfee Trustedsource\033[0;0m")
ts = trustedsource.TrustedSource(args.domain)
if args.check:
ts.check_category(False)
elif args.submit:
ts.check_category(True)

if args.proxy == 'b' or args.proxy == 'a':
print "\033[1;34m[-] Targeting Bluecoat WebPulse\033[0;0m"
print("\033[1;34m[-] Targeting Bluecoat WebPulse\033[0;0m")
if args.check:
b = bluecoat.Bluecoat(args.domain, 'https://www.bankofamerica.com')
b.check_category()
elif args.submit:
print "\033[1;31m[-] WARNING: This module must be run from the webserver you want to categorise\033[0;0m"
print "\033[1;31m[-] Proceed: Y/N\033[0;0m"
print(
"\033[1;31m[-] WARNING: This module must be run from the webserver you want to categorise\033[0;0m")
print("\033[1;31m[-] Proceed: Y/N\033[0;0m")
while True:
choice = raw_input().lower()
if choice == 'Y' or choice =='y':
choice = input().lower()
if choice == 'Y' or choice == 'y':
b = bluecoat.Bluecoat(args.domain, 'https://www.bankofamerica.com')
b.run()
break
elif choice == 'N' or choice == 'n':
break

if args.proxy == 'i' or args.proxy == 'a':
print "\033[1;34m[-] Targeting IBM Xforce\033[0;0m"
print("\033[1;34m[-] Targeting IBM Xforce\033[0;0m")
xf = ibmxforce.IBMXforce(args.domain)
if args.check:
xf.checkIBMxForce()
elif args.submit:
xf.submit_category()


if __name__ == "__main__":
c = Chameleon()
c.show_banner()
Expand Down
Binary file added modules/__init__.pyc
Binary file not shown.
Binary file added modules/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file added modules/__pycache__/bluecoat.cpython-38.pyc
Binary file not shown.
Binary file added modules/__pycache__/ibmxforce.cpython-38.pyc
Binary file not shown.
Binary file added modules/__pycache__/trustedsource.cpython-38.pyc
Binary file not shown.
95 changes: 54 additions & 41 deletions modules/bluecoat.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@

import urllib2
import requests
import sys
import re
from urlparse import urlparse
from bs4 import BeautifulSoup
import http.server
import json
import re
import socketserver
import sys
import threading
import SocketServer
import SimpleHTTPServer
from urllib.parse import urlparse
import time
import traceback
import requests
import os


class NewHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
class NewHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.path = 'webroot/index.html'
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
return http.server.SimpleHTTPRequestHandler.do_GET(self)


class ThreadedHTTPServer(object):
handler = NewHandler

def __init__(self, host, port):
self.server = SocketServer.TCPServer((host, port), self.handler)
self.server = socketserver.TCPServer((host, port), self.handler)
self.server_thread = threading.Thread(target=self.server.serve_forever)
self.server_thread.daemon = True

Expand All @@ -32,71 +32,84 @@ def stop(self):
self.server.shutdown()
self.server.server_close()


class Bluecoat:
def __init__(self, url, clonesite):
self.url = url
self.clonesite = clonesite
self.server = ''

def clone(self):
print "[-] Cloning " + self.clonesite
headers = { 'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)' }
print("[-] Cloning " + self.clonesite)
headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)'}
webContent = requests.get(self.clonesite, headers=headers).content

if not os.path.exists('webroot'):
os.makedirs('webroot')

try:
if webContent.lower().index("<base href=\""):
if webContent.lower().index(b"<base href=\""):
pass
except ValueError:
parsed_uri = urlparse( self.clonesite)
parsed_uri = urlparse(self.clonesite)
base = '{uri.scheme}://{uri.netloc}/'.format(uri=parsed_uri)
content = re.sub(r"(<head.*?>)", "\g<0>\n<base href=\"" + base + "\">" , webContent, count=1, flags=re.IGNORECASE)
webContent = content
webContent = re.sub(b"(<head.*?>)", b"\g<0>\n<base href=\"" + bytes(base, encoding='utf8') + b"\">", webContent, count=1, flags=re.IGNORECASE)

with open('webroot/index.html', 'w') as indexFile:
with open('webroot/index.html', 'wb') as indexFile:
indexFile.write(webContent)
indexFile.close()

def check_category(self):
# Category checking lifted from CatMyFish
# https://github.com/Mr-Un1k0d3r/CatMyFish/blob/master/CatMyFish.py
print "[-] Checking category for " + self.url
request = urllib2.Request("https://sitereview.bluecoat.com/resource/lookup")
request.add_header("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)")
request.add_header("Referer", "https://sitereview.bluecoat.com/lookup")
request.add_header("Content-Type", "application/json; charset=utf-8")
post_data = {"url": self.url, "captcha": ""}
response = urllib2.urlopen(request, json.dumps(post_data))
print("[-] Checking category for " + self.url)

session = requests.session()
url = "https://sitereview.bluecoat.com/resource/lookup"
cookies = {"XSRF-TOKEN": "028e5984-50bf-4c00-ad38-87d19957201a"}
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0",
"Accept": "application/json, text/plain, */*", "Accept-Language": "en_US",
"Accept-Encoding": "gzip, deflate", "Referer": "https://sitereview.bluecoat.com/",
"X-XSRF-TOKEN": "028e5984-50bf-4c00-ad38-87d19957201a",
"Content-Type": "application/json; charset=utf-8", "Connection": "close"}
data = {"captcha": "", "key": "",
"phrase": "RXZlbiBpZiB5b3UgYXJlIG5vdCBwYXJ0IG9mIGEgY29tbWVyY2lhbCBvcmdhbml6YXRpb24sIHNjcmlwdGluZyBhZ2FpbnN0IFNpdGUgUmV2aWV3IGlzIHN0aWxsIGFnYWluc3QgdGhlIFRlcm1zIG9mIFNlcnZpY2U=",
"source": "new lookup", "url": self.url}
response = session.post(url, headers=headers, cookies=cookies, json=data)

try:
json_data = json.loads(response.read())
if json_data.has_key("errorType"):
json_data = json.loads(response.content)
if "errorType" in json_data:
if json_data["errorType"] == "captcha":
print "[-] BlueCoat blocked us :("
print("[-] BlueCoat blocked us :(")
sys.exit(0)
category = []
for entry in json_data["categorization"]:
category.append(entry["name"])
cat = ', '.join(category)
print "\033[1;32m[-] Your site is categorised as: " + cat + "\033[0;0m"
cat = ', '.join(category)
print("\033[1;32m[-] Your site is categorised as: " + cat + "\033[0;0m")
except Exception as e:
print "[-] An error occurred"
traceback.print_exc()

print("[-] An error occurred")

def serve_content(self):
print "[-] Serving content over HTTP server"
self.server = ThreadedHTTPServer("0.0.0.0", 80)
try:
self.server.start()
except:
pass
print("[-] Serving content over HTTP server")
self.server = ThreadedHTTPServer("0.0.0.0", 8000)
try:
self.server.start()
except:
pass

def shutdown_server(self):
print "[-] Shutting down HTTP server"
print("[-] Shutting down HTTP server")
self.server.stop()

def run(self):
self.clone()
self.serve_content()
time.sleep(10)
self.check_category()
self.check_category()
self.shutdown_server()


Expand Down
Binary file added modules/bluecoat.pyc
Binary file not shown.
14 changes: 7 additions & 7 deletions modules/ibmxforce.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self, domain):
# https://github.com/minisllc/domainhunter/blob/master/domainhunter.py
# Credit: Joe Vest and Andrew Chiles
def checkIBMxForce(self):
print('[-] IBM xForce Check: {}'.format(self.domain))
print(('[-] IBM xForce Check: {}'.format(self.domain)))
s = requests.Session()
useragent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0'
try:
Expand All @@ -36,15 +36,15 @@ def checkIBMxForce(self):

responseJson = json.loads(response.text)

print "\033[1;32m[-] Domain categorised as {}\033[0;0m"\
.format(" | ".join(responseJson["result"].get('cats', {}).keys()))
print(("\033[1;32m[-] Domain categorised as {}\033[0;0m"\
.format(" | ".join(list(responseJson["result"].get('cats', {}).keys())))))

except Exception as e:
print('[-] Error retrieving IBM x-Force reputation!')
return "-"

def submit_category(self):
print('[-] Submitting {} for Financial category'.format(self.domain))
print(('[-] Submitting {} for Financial category'.format(self.domain)))
s = requests.Session()
useragent = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)'
url = 'https://exchange.xforce.ibmcloud.com/url/{}'.format(self.domain)
Expand All @@ -59,10 +59,10 @@ def submit_category(self):
url = 'https://exchange.xforce.ibmcloud.com/api/url/feedback/{}'.format(
self.domain)
response = s.post(url, data=post_data, headers=headers)
if "Thank you for your time and feedback" in response.content:
print "[-] Category successfully submitted, please wait an hour"
if b"Thank you for your time and feedback" in response.content:
print("[-] Category successfully submitted, please wait an hour")
else:
print "[-] Error submitting category"
print("[-] Error submitting category")

if __name__ == "__main__":
url = sys.argv[1]
Expand Down
60 changes: 31 additions & 29 deletions modules/trustedsource.py
Original file line number Diff line number Diff line change
@@ -1,66 +1,68 @@
import requests
import sys
import time
import urllib
import urllib.request, urllib.parse, urllib.error
from bs4 import BeautifulSoup


class TrustedSource:
def __init__(self, url):
self.url = url

def check_category(self, submit):
print "[-] Getting anti-automation tokens"
print("[-] Getting anti-automation tokens")
session = requests.Session()
headers = {
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)',
'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language' : 'en-GB,en;q=0.5'
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-GB,en;q=0.5'
}
session.headers.update(headers)
base_check = 'http://www.trustedsource.org/sources/index.pl'
r = session.get(base_check)
bs = BeautifulSoup(r.text, "html.parser")
form = bs.find("form", { "class" : "contactForm" })
form = bs.find("form", {"class": "contactForm"})
e = form.find("input", {'name': 'e'}).get('value')
c = form.find("input", {'name': 'c'}).get('value')

print "[-] Checking category for " + self.url
print("[-] Checking category for " + self.url)
headers['Referer'] = base_check
session.headers.update(headers)
payload = {'sid':(None, ''), 'e':(None, e), 'c':(None, c), 'p':(None, ''), 'action':(None,'checksingle'),'product':(None,'13-ts-3'), 'url':(None, self.url)}
response = session.post('https://www.trustedsource.org/en/feedback/url',headers=headers,files=payload)
payload = {'sid': (None, ''), 'e': (None, e), 'c': (None, c), 'p': (None, ''), 'action': (None, 'checksingle'),
'product': (None, '13-ts-3'), 'url': (None, self.url)}
response = session.post('https://www.trustedsource.org/en/feedback/url', headers=headers, files=payload)
bs = BeautifulSoup(response.content, "html.parser")
form = bs.find("form", { "class" : "contactForm" })
form = bs.find("form", {"class": "contactForm"})
sid = form.find("input", {'name': 'sid'}).get('value')
results_table = bs.find("table", { "class" : "result-table" })
results_table = bs.find("table", {"class": "result-table"})
td = results_table.find_all('td')
print "\033[1;32m[-] Found category: " + td[len(td)-2].text + "\033[0;0m"
print("\033[1;32m[-] Found category: " + td[len(td) - 2].text + "\033[0;0m")

if submit:
self.submit_category(sid)

def submit_category(self, sid):
print "[-] Submitting URL for finance category"
print("[-] Submitting URL for finance category")
post_data = {
'sid' : sid,
'p' : '',
'action' : 'feedback',
'cat_1' : 'fi',
'cat_2' : 'fi',
'cat_3' : 'fi',
'comment' : '',
'result' : 'Submit+URL+for+Review'
'sid': sid,
'p': '',
'action': 'feedback',
'cat_1': 'fi',
'cat_2': 'fi',
'cat_3': 'fi',
'comment': '',
'result': 'Submit+URL+for+Review'
}
headers = {
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)',
'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language' : 'en-GB,en;q=0.5',
'Referer': 'https://www.trustedsource.org/en/feedback/url',
'DNT' : '1'
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-GB,en;q=0.5',
'Referer': 'https://www.trustedsource.org/en/feedback/url',
'DNT': '1'
}
response = requests.post('https://www.trustedsource.org/en/feedback/url', data = post_data, headers = headers)
if "Thank you for your URL feedback" in response.content:
print "[-] URL submitted, please wait up to 6 hours for categorisation"
response = requests.post('https://www.trustedsource.org/en/feedback/url', data=post_data, headers=headers)
if b"Thank you for your URL feedback" in response.content:
print("[-] URL submitted, please wait up to 6 hours for categorisation")
else:
"[-] An error occured in URL submission"

Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests~=2.22.0
beautifulsoup~=4.9.1

0 comments on commit d222ce6

Please sign in to comment.