Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions main/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
OPENC2_PROFILES = [
'example_process_dns_server_bind.py',
'example_network_firewall_juniper.py',
'example_locate_address.py',
'example_address_whois.py',
'example_response.py',
]

Expand Down
Binary file modified main/settings.pyc
Binary file not shown.
39 changes: 38 additions & 1 deletion orchid/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

from orchid.models import OpenC2Action

# Added for authentication
from functools import wraps
from django.contrib.auth import authenticate, login
from django.http import HttpResponseForbidden

# Logging
import logging
logger = logging.getLogger("console")
Expand Down Expand Up @@ -49,4 +54,36 @@ def register(function):

return current_def

return register
return register


def http_basic_auth(func):
"""
Use as a decorator for views that need to perform HTTP basic
authorisation.
"""
@wraps(func)
def _decorator(request, *args, **kwargs):

if request.META.has_key('HTTP_AUTHORIZATION'):
try:
authmeth, auth = request.META['HTTP_AUTHORIZATION'].split(' ', 1)
if authmeth.lower() == 'basic':
auth = auth.strip().decode('base64')
username, password = auth.split(':', 1)
user = authenticate(username=username, password=password)

if user:

login(request, user)

else:

return HttpResponseForbidden()

except ValueError:
# Bad HTTP_AUTHORIZATION header
return HttpResponseForbidden()

return func(request, *args, **kwargs)
return _decorator
Binary file modified orchid/decorators.pyc
Binary file not shown.
1 change: 0 additions & 1 deletion orchid/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ def sig_match(self, message, function_signature):
# Specifier In message | Match | Match - Generic Profile - But use profile logic to check specifiers (saves writing a profile for every firewall etc)
# Specifier Not In message | No Match | Match


# Check actions
if function_signature["action"] != message["action"]:

Expand Down
Binary file modified orchid/models.pyc
Binary file not shown.
3 changes: 3 additions & 0 deletions orchid/profiles/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ def dispatch(self,message):
logger.debug("Dispatcher called")
capable_handlers = []

# Force the action string to lowercase
message['action'] = message['action'].lower()

# Check action / target type
if message["action"] == 'query' and message["target"]["type"] == 'openc2:openc2':
return HttpResponse(self.capabilities(),status=200)
Expand Down
Binary file modified orchid/profiles/__init__.pyc
Binary file not shown.
101 changes: 101 additions & 0 deletions orchid/profiles/example_address_whois.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
####################################################
#________ .__ .___________ #
#\_____ \_______ ____ | |__ | \______ \ #
# / | \_ __ \_/ ___\| | \| || | \ #
#/ | \ | \/\ \___| Y \ || ` \ #
#\_______ /__| \___ >___| /___/_______ / #
# \/ \/ \/ \/ #
# #
# Orchestrator for Intelligence Defence - OpenC2 #
####################################################
# Name:
#
# example_address_whois.py
#
# Descriptions:
#
# This profile carries out a Whois lookup of a Cybox addresses in OpenC2 messages using ARIN or RIPE
#
#
# Sample Files
#
# - ./samples/whois_address_connection.json

from orchid.decorators import openc2_action
from django.conf import settings
from orchid.profiles import Dispatcher
from orchid.response import make_response_message,respond_message

# General
import requests
import json

# Cybox/STIX/TAXII Stuff
from cybox.core import Observable
from cybox.objects.address_object import Address


# Logging
import logging
logger = logging.getLogger("console")

@openc2_action(actuator_list=[{"type":"process-directory-service"}], target_list=[{"type":"cybox:AddressObjectType"}])
def report(target, actuator, modifier):

cybox_address_obs = Observable.from_json(json.dumps(target["specifiers"]))

address = str(cybox_address_obs.object_.properties.address_value)

if is_public(address):

whois = whois_lookup(address)

# Handle response
if whois and "respond-to" in modifier:

if "command-ref" in modifier:
ref = modifier["command-ref"]
else:
ref = None

respond_message(make_response_message(ref, "simple", {"whois":whois}),modifier["respond-to"])

return True

def is_public(address):

# I could use NetAddr here but I am trying to keep the import footprint light
addr = address.split(".")
#Private A Class
if addr[0] == '10':
return False
#Private B Class
elif addr[0] == '172' and int(addr[1]) >= 16 and int(addr[1]) < 32:
return False
#Private C Class
elif addr[0] == '192' and addr[1] == '168':
return False
else:
return True

def whois_lookup(address):

# ARIN the RIPE if needed
try:
url = 'http://whois.arin.net/rest/ip/'+address
headers = {'Accept': 'application/json'}

response = requests.get(url, headers=headers).text

if "RIPE" in response:

url = 'https://stat.ripe.net/data/whois/data.json?resource='+address
return json.loads(requests.get(url, headers=headers).text)

else:

return json.loads(response)
except:

return False

Binary file added orchid/profiles/example_address_whois.pyc
Binary file not shown.
93 changes: 93 additions & 0 deletions orchid/profiles/example_locate_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
####################################################
#________ .__ .___________ #
#\_____ \_______ ____ | |__ | \______ \ #
# / | \_ __ \_/ ___\| | \| || | \ #
#/ | \ | \/\ \___| Y \ || ` \ #
#\_______ /__| \___ >___| /___/_______ / #
# \/ \/ \/ \/ #
# #
# Orchestrator for Intelligence Defence - OpenC2 #
####################################################
# Name:
#
# example_address_lookup.py
#
# Descriptions:
#
# This profile carries out a GeoIP lookup of a Cybox addresses in OpenC2 messages using a third party service
#
#
# Sample Files
#
# - ./samples/geoip_address_connection.json

from orchid.decorators import openc2_action
from django.conf import settings
from orchid.profiles import Dispatcher
from orchid.response import make_response_message,respond_message

# General
import requests
import json

# Cybox/STIX/TAXII Stuff
from cybox.core import Observable
from cybox.objects.address_object import Address


# Logging
import logging
logger = logging.getLogger("console")

@openc2_action(actuator_list=[{"type":"process-location-service"}], target_list=[{"type":"cybox:AddressObjectType"}])
def locate(target, actuator, modifier):

cybox_address_obs = Observable.from_json(json.dumps(target["specifiers"]))

address = str(cybox_address_obs.object_.properties.address_value)

if is_public(address):

country = geo_lookup(address)
print country
# Handle response
if country and "respond-to" in modifier:

if "command-ref" in modifier:
ref = modifier["command-ref"]
else:
ref = None

respond_message(make_response_message(ref, "simple", {"country":country}),modifier["respond-to"])

return True

def is_public(address):

# I could use NetAddr here but I am trying to keep the import footprint light
addr = address.split(".")
#Private A Class
if addr[0] == '10':
return False
#Private B Class
elif addr[0] == '172' and int(addr[1]) >= 16 and int(addr[1]) < 32:
return False
#Private C Class
elif addr[0] == '192' and addr[1] == '168':
return False
else:
return True

def geo_lookup(address):

# Using Free GeoIP Net
url="http://freegeoip.net/json/%s" % address

try:

return json.loads(requests.get(url).text)

except:

print "ERROR: freegeoip.net"
return False
Binary file added orchid/profiles/example_locate_address.pyc
Binary file not shown.
Binary file modified orchid/profiles/example_response.pyc
Binary file not shown.
6 changes: 5 additions & 1 deletion orchid/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ def make_response_message(command_ref, com_type, value):
msg["modifiers"]["type"] = com_type
msg["modifiers"]["value"] = value


return json.dumps(msg)

def respond_ack(modifiers):
Expand All @@ -73,3 +72,8 @@ def respond_ack(modifiers):

logger.error("A response was requested, but didnt have the required fields to facilate response.")


def respond_message(message,respond_to_url):

logger.info("Send message %s to %s" % (message,respond_to_url))
req = requests.post(respond_to_url, data=message, verify=False)
Binary file modified orchid/response.pyc
Binary file not shown.
2 changes: 2 additions & 0 deletions orchid/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from django.http import HttpResponseRedirect, HttpResponse

from validators import openc2_validatior
from decorators import http_basic_auth

from profiles import Dispatcher
import json
Expand All @@ -27,6 +28,7 @@
# Create a single dispatcher on load
dispatcher = Dispatcher()

@http_basic_auth
@csrf_exempt
def service_router(request):

Expand Down
Binary file modified orchid/views.pyc
Binary file not shown.
24 changes: 24 additions & 0 deletions samples/locate_address.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"action": "LOCATE",
"actuator": {
"specifiers": {},
"type": "process-location-service"
},
"modifiers": {
"respond-to":"http://localhost/openc2",
"command-ref":1
},
"target": {
"specifiers": {
"id": "example:Observable-5a014655-0491-4e4f-b7ba-3343b4fcb7dc",
"object": {
"id": "example:Address-aa691157-e74c-4030-9e3d-7e9642db33bd",
"properties": {
"address_value": "123.123.123.123",
"xsi:type": "AddressObjectType"
}
}
},
"type": "cybox:AddressObjectType"
}
}
24 changes: 24 additions & 0 deletions samples/whois_address_connection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"action": "REPORT",
"actuator": {
"specifiers": {},
"type": "process-directory-service"
},
"modifiers": {
"respond-to":"http://localhost/openc2",
"command-ref":1
},
"target": {
"specifiers": {
"id": "example:Observable-5a014655-0491-4e4f-b7ba-3343b4fcb7dc",
"object": {
"id": "example:Address-aa691157-e74c-4030-9e3d-7e9642db33bd",
"properties": {
"address_value": "8.8.8.8",
"xsi:type": "AddressObjectType"
}
}
},
"type": "cybox:AddressObjectType"
}
}