Skip to content

Commit 7f2d730

Browse files
committed
start cip parser
1 parent 962b305 commit 7f2d730

File tree

13 files changed

+345
-1
lines changed

13 files changed

+345
-1
lines changed

install.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
if [ $# -eq 0 ]
1414
then
1515
echo "You need to specify the Bro installation directory."
16-
echo "Usage: $0 </path/to/bro>"
16+
echo "Usage: $0 </path/to/bro/>"
1717
exit 1
1818
fi
1919

@@ -35,6 +35,8 @@ testing/btest/scripts/policy/protocols/enip/
3535
testing/btest/Baseline/scripts.base.protocols.enip.*/
3636
testing/btest/Baseline/scripts.policy.protocols.enip.*/
3737
testing/btest/Traces/enip/
38+
scripts/base/protocols/cip/
39+
src/analyzer/protocol/cip/
3840
"
3941

4042
for varname in $dirs
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Generated by binpac_quickstart
2+
@load ./main
3+
@load-sigs ./dpd.sig

scripts/base/protocols/cip/dpd.sig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Generated by binpac_quickstart
2+
3+
signature dpd_cip {
4+
5+
ip-proto == tcp
6+
7+
8+
# ## TODO: Define the payload. When Bro sees this regex, on
9+
# ## any port, it will enable your analyzer on that
10+
# ## connection.
11+
# ## payload /^CIP/
12+
13+
enable "cip"
14+
}

scripts/base/protocols/cip/main.bro

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
##! Implements base functionality for cip analysis.
2+
##! Generates the Cip.log file.
3+
4+
# Generated by binpac_quickstart
5+
6+
module Cip;
7+
8+
export {
9+
redef enum Log::ID += { LOG };
10+
11+
type Info: record {
12+
## Timestamp for when the event happened.
13+
ts: time &log;
14+
## Unique ID for the connection.
15+
uid: string &log;
16+
## The connection's 4-tuple of endpoint addresses/ports.
17+
id: conn_id &log;
18+
19+
# ## TODO: Add other fields here that you'd like to log.
20+
};
21+
22+
## Event that can be handled to access the cip record as it is sent on
23+
## to the loggin framework.
24+
global log_cip: event(rec: Info);
25+
}
26+
27+
# TODO: The recommended method to do dynamic protocol detection
28+
# (DPD) is with the signatures in dpd.sig. If you can't come up
29+
# with any signatures, then you can do port-based detection by
30+
# uncommenting the following and specifying the port(s):
31+
32+
# const ports = { 1234/tcp, 5678/tcp };
33+
34+
35+
# redef likely_server_ports += { ports };
36+
37+
event bro_init() &priority=5
38+
{
39+
Log::create_stream(Cip::LOG, [$columns=Info, $ev=log_cip, $path="cip"]);
40+
41+
# TODO: If you're using port-based DPD, uncomment this.
42+
# Analyzer::register_for_ports(Analyzer::ANALYZER_CIP, ports);
43+
}
44+
45+
event cip_event(c: connection)
46+
{
47+
local info: Info;
48+
info$ts = network_time();
49+
info$uid = c$uid;
50+
info$id = c$id;
51+
52+
Log::write(Cip::LOG, info);
53+
}

src/analyzer/protocol/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ add_subdirectory(arp)
33
add_subdirectory(ayiya)
44
add_subdirectory(backdoor)
55
add_subdirectory(bittorrent)
6+
# add_subdirectory(cip)
67
add_subdirectory(conn-size)
78
add_subdirectory(dce-rpc)
89
add_subdirectory(dhcp)

src/analyzer/protocol/cip/CIP.cc

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include "CIP.h"
2+
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
3+
#include "Reporter.h"
4+
#include "events.bif.h"
5+
6+
using namespace analyzer::cip;
7+
8+
CIP_Analyzer::CIP_Analyzer(Connection* c)
9+
10+
: tcp::TCP_ApplicationAnalyzer("CIP", c)
11+
{
12+
interp = new binpac::CIP::CIP_Conn(this);
13+
had_gap = false;
14+
}
15+
16+
CIP_Analyzer::~CIP_Analyzer()
17+
{
18+
delete interp;
19+
}
20+
21+
void CIP_Analyzer::Done()
22+
{
23+
tcp::TCP_ApplicationAnalyzer::Done();
24+
25+
interp->FlowEOF(true);
26+
interp->FlowEOF(false);
27+
}
28+
29+
void CIP_Analyzer::EndpointEOF(bool is_orig)
30+
{
31+
tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
32+
interp->FlowEOF(is_orig);
33+
}
34+
35+
void CIP_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
36+
{
37+
tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
38+
39+
assert(TCP());
40+
if ( TCP()->IsPartial() )
41+
return;
42+
43+
if ( had_gap )
44+
// If only one side had a content gap, we could still try to
45+
// deliver data to the other side if the script layer can handle this.
46+
return;
47+
48+
try
49+
{
50+
interp->NewData(orig, data, data + len);
51+
}
52+
catch ( const binpac::Exception& e )
53+
{
54+
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
55+
}
56+
}
57+
58+
void CIP_Analyzer::Undelivered(uint64 seq, int len, bool orig)
59+
{
60+
tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
61+
had_gap = true;
62+
interp->NewGap(orig, len);
63+
}

src/analyzer/protocol/cip/CIP.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#ifndef ANALYZER_PROTOCOL_CIP_CIP_H
2+
#define ANALYZER_PROTOCOL_CIP_CIP_H
3+
4+
#include "events.bif.h"
5+
#include "analyzer/protocol/tcp/TCP.h"
6+
#include "cip_pac.h"
7+
8+
namespace analyzer { namespace cip {
9+
10+
class CIP_Analyzer
11+
12+
: public tcp::TCP_ApplicationAnalyzer {
13+
14+
public:
15+
CIP_Analyzer(Connection* conn);
16+
virtual ~CIP_Analyzer();
17+
18+
// Overriden from Analyzer.
19+
virtual void Done();
20+
virtual void DeliverStream(int len, const u_char* data, bool orig);
21+
virtual void Undelivered(uint64 seq, int len, bool orig);
22+
23+
// Overriden from tcp::TCP_ApplicationAnalyzer.
24+
virtual void EndpointEOF(bool is_orig);
25+
26+
static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn)
27+
{ return new CIP_Analyzer(conn); }
28+
29+
protected:
30+
binpac::CIP::CIP_Conn* interp;
31+
bool had_gap;
32+
};
33+
34+
} } // namespace analyzer::*
35+
36+
#endif
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
include(BroPlugin)
2+
3+
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
4+
5+
bro_plugin_begin(Bro CIP)
6+
bro_plugin_cc(CIP.cc Plugin.cc)
7+
bro_plugin_bif(events.bif)
8+
bro_plugin_pac(cip.pac cip-analyzer.pac cip-protocol.pac)
9+
bro_plugin_end()

src/analyzer/protocol/cip/Plugin.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include "plugin/Plugin.h"
2+
#include "CIP.h"
3+
4+
namespace plugin {
5+
namespace Bro_CIP {
6+
7+
class Plugin : public plugin::Plugin {
8+
public:
9+
plugin::Configuration Configure()
10+
{
11+
AddComponent(new ::analyzer::Component("CIP",
12+
::analyzer::cip::CIP_Analyzer::InstantiateAnalyzer));
13+
14+
plugin::Configuration config;
15+
config.name = "Bro::CIP";
16+
config.description = "Common Industrial Protocol analyzer";
17+
return config;
18+
}
19+
} plugin;
20+
21+
}
22+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
refine flow CIP_Flow += {
2+
function proc_cip_message(msg: CIP_PDU): bool
3+
%{
4+
BifEvent::generate_cip_event(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn());
5+
return true;
6+
%}
7+
};
8+
9+
refine typeattr CIP_PDU += &let {
10+
proc: bool = $context.flow.proc_cip_message(this);
11+
};
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#
2+
# Useful reference for specs: http://odva.org/
3+
# CIP NETWORKS LIBRARY Volume I: http://www.tud.ttu.ee/im/Kristjan.Sillmann/ISP0051%20Rakenduslik%20Andmeside/CIP%20docs/CIP%20Vol1_3.3.pdf
4+
#
5+
# Binpac for Common Industrial Protocol analyser.
6+
#
7+
8+
##############################
9+
# CONSTANTS #
10+
##############################
11+
12+
enum tag_types {
13+
BOOL = 0x00C1; # 1 byte
14+
SINT = 0x00C2; # 2 bytes
15+
INT = 0x00C3; # 4 bytes
16+
DINT = 0x00C4; # 4 bytes
17+
REAL = 0x00CA; # 4 bytes
18+
DWORD = 0x00D3; # 4 bytes
19+
LINT = 0x00C5; # 8 bytes
20+
};
21+
22+
# E8B means 8-bit element
23+
# C16B means 16-bit class
24+
enum segment_types {
25+
E8B = 0x28;
26+
E16B = 0x29;
27+
E32B = 0x2A;
28+
29+
C8B = 0x20;
30+
C16B = 0x21;
31+
32+
I8B = 0x24;
33+
I16B = 0x25;
34+
35+
A8B = 0x30;
36+
A16B = 0x31;
37+
38+
ANSI = 0x91;
39+
};
40+
41+
enum services {
42+
READ_TAG = 0x4C;
43+
READ_TAG_REPLY = 0xCC;
44+
READ_TAG_FRAGMENTED = 0x52;
45+
WRITE_TAG = 0x4D;
46+
WRITE_TAG_FRAGMENTED = 0x53;
47+
READ_MODIFY_WRITE_TAG = 0x4E;
48+
};
49+
50+
##############################
51+
# RECORD TYPES #
52+
##############################
53+
54+
type Message_Request = record {
55+
srv: uint8;
56+
psize: uint8;
57+
path: uint8[psize * 2];
58+
data: uint16;
59+
} &byteorder=bigendian;
60+
61+
type Message_Reply = record {
62+
srv: uint8;
63+
rsrvd: uint8;
64+
st: uint8;
65+
ext: uint8;
66+
reply: Reply_Data;
67+
} &byteorder=bigendian;
68+
69+
type Reply_Data = record {
70+
type: uint16;
71+
data: case(type) of {
72+
BOOL -> uint8[1];
73+
SINT -> uint8[2];
74+
INT -> uint8[4];
75+
DINT -> uint8[4];
76+
REAL -> uint8[4];
77+
DWORD -> uint8[4];
78+
LINT -> uint8[8];
79+
};
80+
} &byteorder=bigendian;
81+
82+
type CIP_PDU(is_orig: bool) = record {
83+
data: bytestring &restofdata;
84+
} &byteorder=bigendian;

src/analyzer/protocol/cip/cip.pac

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Analyzer for Common Industrial Protocol
2+
# - cip-protocol.pac: describes the cip protocol messages
3+
# - cip-analyzer.pac: describes the cip analyzer code
4+
5+
%include binpac.pac
6+
%include bro.pac
7+
8+
%extern{
9+
#include "events.bif.h"
10+
%}
11+
12+
analyzer CIP withcontext {
13+
connection: CIP_Conn;
14+
flow: CIP_Flow;
15+
};
16+
17+
# Our connection consists of two flows, one in each direction.
18+
connection CIP_Conn(bro_analyzer: BroAnalyzer) {
19+
upflow = CIP_Flow(true);
20+
downflow = CIP_Flow(false);
21+
};
22+
23+
%include cip-protocol.pac
24+
25+
# Now we define the flow:
26+
flow CIP_Flow(is_orig: bool) {
27+
28+
# ## TODO: Determine if you want flowunit or datagram parsing:
29+
30+
# Using flowunit will cause the anlayzer to buffer incremental input.
31+
# This is needed for &oneline and &length. If you don't need this, you'll
32+
# get better performance with datagram.
33+
34+
# flowunit = CIP_PDU(is_orig) withcontext(connection, this);
35+
datagram = CIP_PDU(is_orig) withcontext(connection, this);
36+
37+
};
38+
39+
%include cip-analyzer.pac

src/analyzer/protocol/cip/events.bif

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Generated for cip connections
2+
##
3+
## See `Google <http://lmgtfy.com/?q=cip>`__ for more information about cip
4+
##
5+
## c: The connection
6+
##
7+
event cip_event%(c: connection%);

0 commit comments

Comments
 (0)