Canari v3 - Sneak Peak!

At DEF CON XX, I had the privilege of presenting a new Maltego-based penetration testing toolkit called Sploitego. Sploitego had many features including extensive integration with some of the de facto penetration testing tools like NMap, MetaSploit, and Nessus - but most importantly a very flexible and rapid transform development framework (which took the better half of a year to develop). Here's a video for those of you who missed the presentation:

At the time the framework was tightly coupled into Sploitego. The assumption was that people would use the framework to extend Sploitego and augment it with their own transforms. However, people who felt the same pain as me when developing Maltego transforms, soon wanted the framework to stand on its own - and so Canari was born.

Today there are dozens of publicly released Canari transform packages and probably hundreds more operating behind the closed doors of private corporations. Canari is being actively used by many organizations in the Fortune 500 space to facilitate the development and operation of threat intelligence products.

The reason for its popularity at the time was its ease of use, portability, and rich feature set. Transform developers could finally focus on developing their transforms and leave the Maltego integration to Canari. Here's a "Hello World!" example of what a Canari-based Maltego transform looks like in version Canari v1.1:

from canari.maltego.entities import Person, Phrase
from canari.maltego.utils import debug, progress
from canari.framework import configure 

__all__ = [ 'dotransform' ]

@configure(
    label='Person to "Hello World!"',
    description='Returns a Phrase entity with the phrase "Hello Word!"',
    uuids=[ 'mytransformpack.PhraseToHelloWorld' ],
    inputs=[ ( 'Hello World Set', Person ) ]
)
def dotransform(request, response, config):
    response += Phrase('Hello %s' + request.value)
    return response

A simple transform like this would have needed 3 to 4 times more code had we been scripting this transform without Canari.

However, like everything else, we felt that we could significantly improve on the Canari v1.1 transform specification. One of the common complaints we receive is the complexity of the @annotation() statements when defining entities and transforms - and this is what we'll be showing you today in terms of improvements in Canari v3!

A New (and Simpler) Beginning

One of the biggest improvements in Canari v3 is the improvement of code readability. We've decided to leverage the power of the object-oriented programming model in Python to improve code readability and reuse. Take a gander at the same "Hello World!" transform written using Canari v3:

from canari.maltego.entities import Person, Phrase
from canari.maltego.transform import Transform

__author__ = 'Scuba Douba'

class HelloWorld(Transform):
    """This transform returns a set of phrases with various salutations to a Person's name."""

    # The transform input entity type.
    input_type = Person

    def do_transform(self, request, response, config):
        person = request.entity
        response += Phrase('Hello %s!' % person.value)
        return response

It may look like you have to write a little more code but the improvements here are subtle and pay off in spades. Unlike our ugly @configure() statement in our first code example, we opted for a more elegant solution - deriving our transform meta-data from class properties.

Transform meta-data is used to define the various aspects of the transform's look and feel in Maltego's user interface as well as how it interacts with different entity types. If you recall, the @configure() statement required you to define four properties: label, description, uuids, and inputs. In Canari v3 you only have to define one property explicitly - input_type. The rest are automatically generated!

Automatic Input Entity Type Deserialization

One of the limitations with Maltego's local transform adapter is that it doesn't pass the input entity's type information to the local transform. In Canari v1.1, developers were provided a request object that was essentially a Python dict() and it was up to them to derive the type. This was problematic for transforms that were being reused with different input entity types.

With Canari v3, by explicitly setting input_type, transform developers can directly work with an entity without having to "deserialize" or "map" it first. So if we wanted to use the person's last name in our "Hello World!" transform, above, then all we have to do is add this line:

    response += Phrase('Hello Mr(s) %s!' % person.lastname)

Code Reusability

What if we wanted to reuse our transform code for "Hello World!" with a different input entity type? Good question - subclassing to the rescue! Let's say we want to use our "Hello World!" transform against a Location entity as well. All we have to do is just subclass it and change the input_type:

from canari.maltego.entities import Location

#...

class HelloLocation(HelloWorld):
    input_type = Location 

Automatic Meta-data Generation

Cool, but what about our transform descriptions, author credits, transform identifiers, display names, etc. - where do they go? Glad you asked. All these properties can now be automatically derived from various elements of your transform code. For example:

  1. Transform descriptions are automatically derived from the transform class' __doc__ string (i.e. """This transform returns a set of phrases with various salutations to a Person's name.""") or can be explicitly set via the description class property.
  2. Transform author information is derived from the __author__ global variable (i.e. __author__ = 'Scuba Douba') or can be explicitly set via the author class property.
  3. Transform display labels (what you see when you right click on an entity) are automatically derived from its class name (i.e. HelloWorld appears as Hello World in Maltego's UI) or can be explicitly set via the display_name class property.
  4. Transform identifiers (unique IDs used by Maltego internally) are now automatically generated using the names of the transform package and class (i.e. for transform HelloWorld in transform package foo, the unique identifier becomes foo.HelloWorld) or can be explicitly set via the name class property.
  5. Finally, the transform's input set (or context menu group) is derived from the transform package name (i.e. transforms in transform package foo fall under the Foo transform set) or can be explicitly set via the transform_set class property.

Development Milestones

We've successfully migrated the local transform management and execution code to Canari v3. However, we're still working on refining Canari's internal API and migrating the remote transform runner and entity definition specification to v3. We are also working on developing comprehensive unit-tests and documentation to provide our development community with high quality code and project support.

We expect to release Canari v3 next year (maybe at a security conference near you?). However, early pre-releases will be provided to our generous donors for helping us fund this effort.

Kudos to our Donors

We've managed to raise over $500 dollars in funding thanks to our generous donors and we continue to seek your help in funding our efforts. Canari is accepting donations at Pledgie and GoFundMe. Donors will get exclusive access to our early pre-releases and get to play with Canari v3! For those of you who have supported Canari, we thank you dearly from the bottom of our hearts. Without you, this effort would not be possible!

Click here to lend your support to: Help the Canari Framework get to Version 3.0 and make a donation at pledgie.com !