CLI

The feed.py example command-line interface is available from the Git repository.

Example Usage

Subscribing to mwcapture:

$ python feed.py --host 137.226.161.210 -p 10001 -c mwcapture -i [email protected] -s <secret> subscribe
[feedcli] Connecting to feed broker...
[feedcli] publish to mwcapture by [email protected]: {"url": "http://122.122.219.221:7065/itfwnqga", "daddr": "10.10.10.10", "saddr": "122.122.219.221", "dport": "445", "sport": "2668", "sha512": "d8921f70c7fa0468d94c2066eace77a977e27750ddba72cd21e219cbc4aaa016da5c5ce338e6daaad6260680806af4430d5cfb12babf3142f880078d1e25ba12", "md5": "7acba0d01e49618e25744d9a08e6900c"}
[feedcli] publish to mwcapture by [email protected]: {"url": "http://88.36.251.116:4776/vhjxbjq", "daddr": "10.10.10.10", "saddr": "88.36.251.116", "dport": "445", "sport": "1764", "sha512": "76465aebcb5eb5335f400f0922c938b39e48140be8814e801f8605033eabf98f0dd34da4f148c5cba3277b48d47334fafff04e9f53d16011ca0a4df9a61e3c52", "md5": "344770974dce3c039b48d27bd4e9a114"}
[feedcli] publish to mwcapture by [email protected]: {"url": "http://125.227.66.180:3884/dcuit", "daddr": "10.10.10.10", "saddr": "125.227.66.180", "dport": "445", "sport": "2727", "sha512": "5762b0366a019be847365d38c0b5494f1b62db16ec2dd5a931e7d311dba30ee3e3bb0097b3b3bd3bbbaf78827c4653b230c6e42d1178558a1780e102451391d3", "md5": "95ad430abca3da496600f764c120683c"}

Subscribing to three channels at once:

$ python feed.py --host 137.226.161.210 -p 10001 -c mwbinary-sensorunique -c dcerpcrequests -c shellceprofiles -i [email protected] -s <secret> subscribe
[feedcli] Connecting to feed broker...
[feedcli] publish to dcerpcrequests by [email protected]: {"uuid": "4b324fc8-1670-01d3-1278-5a47bf6ee188", "daddr": "10.10.10.10", "opnum": 31, "saddr": "122.124.132.163", "dport": "445", "sport": "6539"}
[feedcli] publish to shellcodeprofiles by [email protected]: {"profile": "[\n    {\n        \"call\": \"LoadLibraryA\",\n        \"args\" : [ \n                \"urlmon\"\n        ],\n        \"return\" : \"0x7df20000\"\n    },\n    {\n        \"call\": \"URLDownloadToFile\",\n        \"args\" : [ \n                \"\",\n                \"http:\\/\\/122.124.132.163:6447\\/jfafb\",\n                \"x.\",\n            \"0\",\n            \"0\"\n        ],\n        \"return\":  \"0\"\n    },\n    {\n        \"call\": \"LoadLibraryA\",\n        \"args\" : [ \n                \"x.\"\n        ],\n        \"return\" : \"0x00000000\"\n    },\n    {\n        \"call\": \"ExitThread\",\n        \"args\" : [ \n            \"0\"\n        ],\n        \"return\":  \"0\"\n    }\n]"}
[feedcli] publish to dcerpcrequests by [email protected]: {"uuid": "4b324fc8-1670-01d3-1278-5a47bf6ee188", "daddr": "10.10.10.10", "opnum": 32, "saddr": "94.85.150.35", "dport": "445", "sport": "1522"}
[feedcli] publish to dcerpcrequests by [email protected]: {"uuid": "4b324fc8-1670-01d3-1278-5a47bf6ee188", "daddr": "10.10.10.10", "opnum": 31, "saddr": "94.85.150.35", "dport": "445", "sport": "1522"}

Publishing to the "test" channel, while being subscribed in another instance:

$ python feed.py --host 137.226.161.210 -p 10001 -c test -i [email protected] -s <secret> publish foo
[feedcli] Connecting to feed broker...
$

$ python feed.py --host 137.226.161.210 -p 10001 -c test -i [email protected] -s <secret> subscribe
[feedcli] Connecting to feed broker...
[feedcli] publish to test by [email protected]: foo

Wire protocol parsing / speaking

The code in feed.py explains the wire protocol a bit and assists in writing your own hpfeeds connectors.

The important bits are:
Message types:

OP_ERROR        = 0
OP_INFO         = 1
OP_AUTH         = 2
OP_PUBLISH      = 3
OP_SUBSCRIBE    = 4

Message building

def msghdr(op, data):
        return struct.pack('!iB', 5+len(data), op) + data
def msgpublish(ident, chan, data):
        if isinstance(data, str):
                data = data.encode('latin1')
        return msghdr(OP_PUBLISH, struct.pack('!B', len(ident)) + ident + struct.pack('!B', len(chan)) + chan + data)
def msgsubscribe(ident, chan):
        return msghdr(OP_SUBSCRIBE, struct.pack('!B', len(ident)) + ident + chan)
def msgauth(rand, ident, secret):
        hash = hashlib.sha1(rand+secret).digest()
        return msghdr(OP_AUTH, struct.pack('!B', len(ident)) + ident + hash)

The function msghdr simply packs together the message contents with a 4 byte length field and one byte opcode.
The other three functions are used to put together the respective messages. The code just creates the length prefixes for the different message parts.
An interesting aspect is the authentication message. It consists of the identifier as (length, value) pair and the binary sha1 digest of rand+secret.