Packet processing doubt in spicy parser and zeek broker

Hi,
As there is no native mms parser inside zeek, I am trying to send packets which have dport 102 over tcp over the broker to another python-based mms parser. I know this is not concrete and full-fledged solution as port-based detection may not be correct always or may generate false detection. Anyway, I have used “zkg create --feature=spicy-protocol-analyzer” to create the below parser. (I am using zeek 6.0.0, also tested in zeek 6.2.0-dev.147)
The idea is to send payload above tcp to the python module over the broker, so I have used below mms.evt, mms.spicy and zeek_mms.spicy to generate mms.hlto.

#mms.evt

import mms;
import Zeek_mms;

# TODO: Adjust below how/when the analyzer will be activated. The example
# defines a well-known port to trigger it. For more information, see:
#
#   https://docs.zeek.org/projects/spicy/en/latest/zeek.html#interface-definitions-evt-files
#
protocol analyzer mms over TCP:
    parse originator with mms::mmsUnit,
    parse responder with mms::mmsUnit,
    port 102/tcp; # adapt port number in main.zeek accordingly

# TODO: Connect Spicy-side events with Zeek-side events. The example just
# defines a simple example event that forwards the raw data (which in practice
# you don't want to do!).
on mms::mmsUnit -> event mms::message($conn, $is_orig, self.payload);

#mms.spicy

# TODO: Define your analyzer here.

module mms;

public type mmsUnit = unit {
    payload: bytes &eod;
};

#zeek_mms.spicy

# Set up protocol confirmation/rejection for analyzers, as well as any further
# Zeek-specific analysis.

module Zeek_mms;

import mms;
import zeek;

# TODO: Protocol analyzers should confirm once they are reasonably sure that
# they are indeed parsing the right protocol. Pick a unit that's a little bit
# into the parsing process here.
#
on mms::mmsUnit::%done {
     zeek::confirm_protocol();
}

# Any error bubbling up to the top unit will trigger a protocol rejection.
on mms::mmsUnit::%error {
    zeek::reject_protocol("error while parsing mms");
}

#main.zeek

@load base/protocols/conn/removal-hooks

module mms;

global mms_topic = "/topic/mms";
global mms_parsed: event(c: connection);

export {
	## Log stream identifier.
	redef enum Log::ID += { LOG };

	## Record type containing the column fields of the mms log.
	type Info: record {
		## Timestamp for when the activity happened.
		ts: time &log;
		## Unique ID for the connection.
		uid: string &log;
		## The connection's 4-tuple of endpoint addresses/ports.
		id: conn_id &log;

		# TODO: Adapt subsequent fields as needed.

		# Message direction (request or response)
		network_direction: string &log &optional;   
		## Request-side payload.
		request: string &optional &log;
		## Response-side payload.
		reply: string &optional &log;
	};

	## A default logging policy hook for the stream.
	global log_policy: Log::PolicyHook;

	## Default hook into mms logging.
	global log_mms: event(rec: Info);

	## mms finalization hook.
	global finalize_mms: Conn::RemovalHook;
}


const request_str_g:  string = "request";            # Used with Network 'requests'
const response_str_g: string = "response";           # Used with Network 'responses'


redef record connection += {
	mms: Info &optional;
};

const ports = {
	# TODO: Replace with actual port(s).
	102/tcp # adapt port number in mms.evt accordingly
};

redef likely_server_ports += { ports };

event zeek_init() &priority=5
	{
	suspend_processing();

        Broker::peer(addr_to_uri(127.0.0.1), 50004/tcp);

	Log::create_stream(mms::LOG, [$columns=Info, $ev=log_mms, $path="mms", $policy=log_policy]);
	}

event Broker::peer_added(ep: Broker::EndpointInfo, msg: string)
{
        print "PEER ADDED", ep;
        continue_processing();

}

# Initialize logging state.
hook set_session(c: connection)
	{
	if ( c?$mms )
		return;

	c$mms = Info($ts=network_time(), $uid=c$uid, $id=c$id);
	Conn::register_removal_hook(c, finalize_mms);
	}

function emit_log(c: connection)
	{
	if ( ! c?$mms )
		return;

	Log::write(mms::LOG, c$mms);
	delete c$mms;
	}

# Example event defined in mms.evt.
event mms::message(c: connection, is_orig: bool, payload: string)
	{
	print fmt("Testing mms: [%s] %s %s", (is_orig ? "request" : "reply"), c$id, payload);
	hook set_session(c);

	local info = c$mms;
	if ( is_orig )
	{
		info$request = payload;
		info$network_direction = request_str_g;
	}
	else
	{
		info$reply = payload;
		info$network_direction = response_str_g;
	}

        Broker::publish(mms_topic, mms_parsed, c);
	}

hook finalize_mms(c: connection)
	{
	# TODO: For UDP protocols, you may want to do this after every request
	# and/or reply.
	emit_log(c);
	
	#Broker::publish(mms_topic, mms_parsed, c);	
	}

The below packet dump I am getting while running the script … (highlighting the packet dump)
[root@root]# /usr/local/zeek/bin/zeek -Cr mms-takeControl.pcap main.zeek

processing suspended
PEER ADDED, [id=6775f597-ede4-5fb6-9d23-32acf3517b5a, network=[address=127.0.0.1, bound_port=50004/tcp]]
processing continued
Testing mms: [request] [orig_h=127.0.0.1, orig_p=36597/tcp, resp_h=127.0.0.1, resp_p=102/tcp] \x03\x00\x00\x13\x0e\xe0\x00\x00\x00\x06\x00\xc1\x02\x00\x02\xc2\x02\x02\x00\x03\x00\x00/\x02\xf0\x80\xa8&\x80\x02\x00\x1f\x81\x01\x03\x82\x01\x03\x83\x02\x08\x08\xa4\x16\x80\x01\x01\x81\x03\x05\xe8\x00\x82\x0c\x03\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\x00\x00 \x02\xf0\x80\xa0\x17\x02\x01\x01\xb3\x12\xa0\x0a\x80\x08reserved\x83\x01\x01\x86\x01\x01\x03\x00\x00\x1a\x02\xf0\x80\xa0\x11\x02\x01\x01\xb4\x0c\xa0\x0a\x80\x08reserved\x03\x00\x00\x09\x02\xf0\x80\x8b\x00


^C1227729893.716384 received termination signal
Testing mms: [reply] [orig_h=127.0.0.1, orig_p=36597/tcp, resp_h=127.0.0.1, resp_p=102/tcp] \x03\x00\x00\x05\x00\xd0\x80\x03\x00\x00\x07\x00\x0f\x80\xa9\x00\x03\x00\x00\x0c\x00\x0f\x80\xa1\x05\x02\x01\x00\xb3\x00\x03\x00\x00\x05\x00\x0f\x80\x03\x00\x00\x05\x00\x0f\x80

Where as if you see the below image or pcap file you will find less bytes in the payload.

I have 3 questions here,

  1. Why am I seeing extra bytes in the broker dump? Is there any issue in the parser or script?

  2. When I do ctrl+c, then only I can see the reply packet in the stdout. As you can see in the above dump, reply is being printed after terminating the zeek process. Why? Sometime there are many mms packets but it’s getting stuck after the first.

  3. Zeek is not printing COTP packets here although it’s matching the criteria of dport 102 over tcp. Why?

PCAP LINK

Thanks
Biswa

@Benjamin_Bannier can you please help to debug this?

Hi,

only looking at this very quickly - I assume you expect Zeek & Spicy to handle TCP sessions differently than they are handled in practice.

When analyzing TCP connections, these are treated as a stream. Hence when you have a unit that contains an &eod at the top-level, this means “until the end of the connection”, not “until the end of the current packet”.

This also matched with the behavior of pressing ctrl+c - aborting Zeek forces the connection to end, which will cause the event to be raised.

I hope this helps a bit,
Johanna

1 Like

Hi @johanna , Thanks for the insight. Can you please suggest how to send per packet mms PDUs which is available just above tcp protocol? Actually, the idea is to parse the mms protocol on a different python daemon as mms parser is not available currently in zeek. So, I need to send mms PDUs to the daemon from zeek over the broker interface.
Any other option or suggestion is also appreciated.

Thanks
Biswa

Can you please suggest how to send per packet mms PDUs which is available just above tcp protocol?

You might have success by declaring your payload field with a &chunked attribute. With that its field hook would be invoked whenever a chunk of data is available which for a TCP analyzer very likely means until a packet boundary.

Hi @Benjamin_Bannier , Thanks for the pointers. I have tried with the below code.

public type mmsUnit = unit {
payload: bytes &eod &chunked;
};

However, I have got unexpected/random payload bytes and not matching with the pcap file attached above.

I have tested with both zeek 6.0 & it’s broker and also with zeek 6.2 & it’s broker but getting same observation.

[root@root analyzer]# /mnt/zeek/broker/bin/zeek -Cr /home/biswa/mms-takeControl.pcap …/scripts/main.zeek
processing suspended
PEER ADDED, [id=bfec893e-7e74-5b1c-bd78-1442a11b947e, network=[address=127.0.0.1, bound_port=50004/tcp]]
processing continued
Testing mms: [request] [orig_h=127.0.0.1, orig_p=36597/tcp, resp_h=127.0.0.1, resp_p=102/tcp] \x03\x00\x00\x09\x02\xf0\x80\x8b\x00
^C1227729893.716384 received termination signal
Testing mms: [reply] [orig_h=127.0.0.1, orig_p=36597/tcp, resp_h=127.0.0.1, resp_p=102/tcp] \x03\x00\x00\x05\x00\x0f\x80


These above bytes seem garbage and expected output : “\x03\x00\x00.…\ff\ff\ff” (refer above pcap or image) from the above initiate-Request PDU requests.

Please suggest the use of &chunked to get per packet mms PDUs ( if you open the pcap you will see 3 MMS packets, 1 initiate-RequestPDU and 2 confirmed-RequestPDU )

Thanks
Biswa

Hi @Benjamin_Bannier , apart from spicy parser, is there any other easier option (packet filter) to filter tcp/udp packet running on particular port and forward payload/pdus above tcp layer to broker interface?
Currently, the requirement is to forward payload above tcp layer (destination port 102) to another external daemon over broker interface or any other standard interface if not possible over the broker interface.

Thanks
Biswa