Skip to content

Commit

Permalink
Merge pull request #340 from Security-Onion-Solutions/jertel/suri
Browse files Browse the repository at this point in the history
pcap improvements
  • Loading branch information
jertel authored Feb 12, 2024
2 parents ae3c6a1 + 2f0154f commit ddc460f
Show file tree
Hide file tree
Showing 15 changed files with 225 additions and 85 deletions.
17 changes: 12 additions & 5 deletions agent/modules/stenoquery/stenoquery.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ func (steno *StenoQuery) CreateQuery(job *model.Job) string {

query := fmt.Sprintf("before %s and after %s", endTime, beginTime)

if len(job.Filter.Protocol) > 0 {
query = fmt.Sprintf("%s and %s", query, job.Filter.Protocol)
}

if len(job.Filter.SrcIp) > 0 {
query = fmt.Sprintf("%s and host %s", query, job.Filter.SrcIp)
}
Expand All @@ -162,12 +166,15 @@ func (steno *StenoQuery) CreateQuery(job *model.Job) string {
query = fmt.Sprintf("%s and host %s", query, job.Filter.DstIp)
}

if job.Filter.SrcPort > 0 {
query = fmt.Sprintf("%s and port %d", query, job.Filter.SrcPort)
}
// Some legacy jobs won't have the protocol provided
if job.Filter.Protocol != model.PROTOCOL_ICMP {
if job.Filter.SrcPort > 0 {
query = fmt.Sprintf("%s and port %d", query, job.Filter.SrcPort)
}

if job.Filter.DstPort > 0 {
query = fmt.Sprintf("%s and port %d", query, job.Filter.DstPort)
if job.Filter.DstPort > 0 {
query = fmt.Sprintf("%s and port %d", query, job.Filter.DstPort)
}
}

return query
Expand Down
39 changes: 39 additions & 0 deletions agent/modules/stenoquery/stenoquery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ func TestCreateQuery(tester *testing.T) {
query := sq.CreateQuery(job)
assert.Equal(tester, expectedQuery, query)

job.Filter.Protocol = model.PROTOCOL_TCP
query = sq.CreateQuery(job)
expectedQuery = expectedQuery + " and tcp"
assert.Equal(tester, expectedQuery, query)

job.Filter.SrcIp = "1.2.3.4"
query = sq.CreateQuery(job)
expectedQuery = expectedQuery + " and host " + job.Filter.SrcIp
Expand All @@ -66,3 +71,37 @@ func TestCreateQuery(tester *testing.T) {
expectedQuery = expectedQuery + " and port " + strconv.Itoa(job.Filter.DstPort)
assert.Equal(tester, expectedQuery, query)
}

func TestCreateQueryIcmp(tester *testing.T) {
sq := NewStenoQuery(nil)

job := model.NewJob()
job.Filter.BeginTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:05:05Z")
job.Filter.EndTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:06:05Z")
expectedQuery := "before 2006-01-02T15:06:05Z and after 2006-01-02T15:05:05Z"
query := sq.CreateQuery(job)
assert.Equal(tester, expectedQuery, query)

job.Filter.Protocol = model.PROTOCOL_ICMP
query = sq.CreateQuery(job)
expectedQuery = expectedQuery + " and icmp"
assert.Equal(tester, expectedQuery, query)

job.Filter.SrcIp = "1.2.3.4"
query = sq.CreateQuery(job)
expectedQuery = expectedQuery + " and host " + job.Filter.SrcIp
assert.Equal(tester, expectedQuery, query)

job.Filter.DstIp = "1.2.1.2"
query = sq.CreateQuery(job)
expectedQuery = expectedQuery + " and host " + job.Filter.DstIp
assert.Equal(tester, expectedQuery, query)

job.Filter.SrcPort = 123
query = sq.CreateQuery(job)
assert.Equal(tester, expectedQuery, query) // port ignored for icmp

job.Filter.DstPort = 123
query = sq.CreateQuery(job)
assert.Equal(tester, expectedQuery, query) // port ignored for icmp
}
2 changes: 1 addition & 1 deletion html/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ a#title, a#title:visited, a#title:active, a#title:hover {

.filter.label {
display: inline-block;
width: 44%;
width: 40%;
text-align: right;
white-space: nowrap;
vertical-align: top;
Expand Down
43 changes: 24 additions & 19 deletions html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,7 @@ <h2 v-if="!isKind('pcap')" v-text="kind"></h2>
<v-col xs12>
<v-text-field v-model="form.sensorId" :placeholder="i18n.sensorId" persistent-hint :hint="i18n.sensorIdHelp"></v-text-field>
<v-text-field v-model="form.importId" :placeholder="i18n.importId" persistent-hint :hint="i18n.importIdHelp"></v-text-field>
<v-text-field v-model="form.protocol" :placeholder="i18n.protocol" persistent-hint :hint="i18n.protocolHelp"></v-text-field>
<v-text-field v-model="form.srcIp" :placeholder="i18n.srcIp" persistent-hint :hint="i18n.srcIpHelp"></v-text-field>
<v-text-field v-model="form.srcPort" :placeholder="i18n.srcPort" persistent-hint :hint="i18n.srcPortHelp"></v-text-field>
<v-text-field v-model="form.dstIp" :placeholder="i18n.dstIp" persistent-hint :hint="i18n.dstIpHelp"></v-text-field>
Expand Down Expand Up @@ -1373,29 +1374,29 @@ <h2>{{ i18n.viewJob }}</h2>
<v-expansion-panels>
<v-expansion-panel>
<v-expansion-panel-header>
<v-chip>
<v-chip label class="mx-2">
<v-avatar>
<v-icon>fa-hashtag</v-icon>
</v-avatar>
<span id="jobDetailsId">{{ job.id }}</span>
</v-chip>
<v-chip>
<v-chip label class="mx-2">
<v-avatar>
<v-icon>fa-ethernet</v-icon>
</v-avatar>
<span id="jobDetailsSensorId">{{ job.nodeId }}</span>
</v-chip>
<v-chip v-if="job.filter">
<v-chip v-if="job.filter" label class="mx-2">
<v-avatar>
<v-icon>fa-file-export</v-icon>
</v-avatar>
<span id="jobDetailsSrc">{{ job.filter.srcIp }}:{{ job.filter.srcPort }}<span class="resolved-host" v-if="$root.pickHostname(job.filter.srcIp)"> - {{ $root.pickHostname(job.filter.srcIp) }}</span></span>
<span id="jobDetailsSrc">{{ job.filter.srcIp }}<span v-if="job.filter.srcPort != 0">:{{ job.filter.srcPort }}</span><span class="resolved-host" v-if="$root.pickHostname(job.filter.srcIp)"> - {{ $root.pickHostname(job.filter.srcIp) }}</span></span>
</v-chip>
<v-chip v-if="job.filter">
<v-chip v-if="job.filter" label class="mx-2">
<v-avatar>
<v-icon>fa-file-import</v-icon>
</v-avatar>
<span id="jobDetailsDst">{{ job.filter.dstIp }}:{{ job.filter.dstPort }}<span class="resolved-host" v-if="$root.pickHostname(job.filter.dstIp)"> - {{ $root.pickHostname(job.filter.dstIp) }}</span></span>
<span id="jobDetailsDst">{{ job.filter.dstIp }}<span v-if="job.filter.dstPort != 0">:{{ job.filter.dstPort }}</span><span class="resolved-host" v-if="$root.pickHostname(job.filter.dstIp)"> - {{ $root.pickHostname(job.filter.dstIp) }}</span></span>
</v-chip>
</v-expansion-panel-header>
<v-expansion-panel-content>
Expand All @@ -1404,55 +1405,59 @@ <h2>{{ i18n.viewJob }}</h2>
<v-col>
<div>
<span class="filter label">{{ i18n.job }}:</span>
<span v-if="job.filter" class="filter value">{{ job.id }}</span>
<span class="filter value">{{ job.id }}</span>
</div>
<div>
<span class="filter label">{{ i18n.owner }}:</span>
<span v-if="job.filter" class="filter value">{{ job.owner }}</span>
<span class="filter value">{{ job.owner }}</span>
</div>
<div>
<span class="filter label">{{ i18n.sensorId }}:</span>
<span v-if="job.filter" class="filter value">{{ job.nodeId }}</span>
<span class="filter value">{{ job.nodeId }}</span>
</div>
<div>
<span class="filter label">{{ i18n.importId }}:</span>
<span v-if="job.filter" class="filter value">{{ job.filter.importId }}</span>
<span v-if="job.filter" class="filter value">{{ job.filter.importId }}&nbsp;</span>
</div>
<div>
<span class="filter label">{{ i18n.protocol }}:</span>
<span v-if="job.filter" class="filter value">{{ job.filter.protocol }}&nbsp;</span>
</div>
<div>
<span class="filter label">{{ i18n.srcIp }}:</span>
<span v-if="job.filter" class="filter value">{{ job.filter.srcIp }}<span class="resolved-host" v-if="$root.pickHostname(job.filter.srcIp)"> - {{ $root.pickHostname(job.filter.srcIp) }}</span></span>
<span v-if="job.filter" class="filter value">{{ job.filter.srcIp }}&nbsp;<span class="resolved-host" v-if="$root.pickHostname(job.filter.srcIp)"> - {{ $root.pickHostname(job.filter.srcIp) }}</span></span>
</div>
<div>
<span class="filter label">{{ i18n.srcPort }}:</span>
<span v-if="job.filter" class="filter value">{{ job.filter.srcPort }}</span>
<span v-if="job.filter" class="filter value">{{ job.filter.srcPort }}&nbsp;</span>
</div>
<div>
<span class="filter label">{{ i18n.dstIp }}:</span>
<span v-if="job.filter" class="filter value">{{ job.filter.dstIp }}<span class="resolved-host" v-if="$root.pickHostname(job.filter.dstIp)"> - {{ $root.pickHostname(job.filter.dstIp) }}</span></span>
<span v-if="job.filter" class="filter value">{{ job.filter.dstIp }}&nbsp;<span class="resolved-host" v-if="$root.pickHostname(job.filter.dstIp)"> - {{ $root.pickHostname(job.filter.dstIp) }}</span></span>
</div>
<div>
<span class="filter label">{{ i18n.dstPort }}:</span>
<span v-if="job.filter" class="filter value">{{ job.filter.dstPort }}</span>
<span v-if="job.filter" class="filter value">{{ job.filter.dstPort }}&nbsp;</span>
</div>
<div>
<span class="filter label">{{ i18n.dateQueued }}:</span>
<span v-if="job.filter" class="filter value">{{ job.createTime | formatDateTime}}</span>
<span class="filter value">{{ job.createTime | formatDateTime}}&nbsp;</span>
</div>
<div>
<span class="filter label">{{ i18n.dateCompleted }}:</span>
<span v-if="job.filter" class="filter value">{{ job.completeTime | formatDateTime}}</span>
<span class="filter value">{{ job.completeTime | formatDateTime}}&nbsp;</span>
</div>
<div>
<span class="filter label">{{ i18n.dateFailed }}:</span>
<span v-if="job.filter" class="filter value">{{ job.failTime | formatDateTime}}</span>
<span class="filter value">{{ job.failTime | formatDateTime}}&nbsp;</span>
</div>
<div>
<span class="filter label">{{ i18n.beginTime }}:</span>
<span v-if="job.filter" class="filter value">{{ job.filter.beginTime | formatTimestamp }}</span>
<span v-if="job.filter" class="filter value">{{ job.filter.beginTime | formatTimestamp }}&nbsp;</span>
</div>
<div>
<span class="filter label">{{ i18n.endTime }}:</span>
<span v-if="job.filter" class="filter value">{{ job.filter.endTime | formatTimestamp }}</span>
<span v-if="job.filter" class="filter value">{{ job.filter.endTime | formatTimestamp }}&nbsp;</span>
</div>
</v-col>
</v-row>
Expand Down
4 changes: 3 additions & 1 deletion html/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,9 @@ $(document).ready(function() {
if (event.code == "Escape") {
this.unmaximize(true);
}
event.cancel();
if (typeof event.cancelable !== "boolean" || event.cancelable) {
event.preventDefault();
}
},
batchLookup(ips, comp) {
if (!this.enableReverseLookup) {
Expand Down
4 changes: 4 additions & 0 deletions html/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ const i18n = {
deleteUserConfirm: 'You are about to permanently delete this user:',
denied: 'Denied',
description: 'Description',
destination: 'Destination',
details: 'Details',
disconnected: 'Disconnected from manager',
diskUsageElastic: 'Elastic Storage Used',
Expand Down Expand Up @@ -553,6 +554,8 @@ const i18n = {
pending: 'Pending',
product: 'Security Onion',
profile: 'Profile',
protocol: 'Protocol',
protocolHelp: 'Optional protocol, such as "icmp" or "tcp".',
queriesHelp: 'Choose from several pre-defined queries',
queryHelp: 'Specify a query in Onion Query Language (OQL)',
quickActions: 'Actions',
Expand Down Expand Up @@ -684,6 +687,7 @@ const i18n = {
sortedBy: 'Sort:',
sortInclude: "Sort By",
sortIncludeHelp: "Add as a sort-by field",
source: 'Source',
sponsorsIntro: 'Brought to you by:',
srcIp: 'Source IP',
srcIpHelp: 'Optional source IP address to include in this job filter',
Expand Down
2 changes: 1 addition & 1 deletion html/js/routes/job.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ routes.push({ path: '/job/:jobId', name: 'job', component: {
view += (code < 32 || code > 126) && code != 13 && code != 10 ? "." : input[idx];
}
return view;
}
}
}
}});

10 changes: 8 additions & 2 deletions html/js/routes/jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ routes.push({ path: '/jobs', name: 'jobs', component: {
valid: false,
sensorId: null,
importId: null,
protocol: null,
srcIp: null,
srcPort: null,
dstIp: null,
Expand Down Expand Up @@ -90,6 +91,7 @@ routes.push({ path: '/jobs', name: 'jobs', component: {
}
this.form.sensorId = localStorage['settings.jobs.addJobForm.sensorId'];
this.form.importId = localStorage['settings.jobs.addJobForm.importId'];
this.form.protocol = localStorage['settings.jobs.addJobForm.protocol'];
this.form.srcIp = localStorage['settings.jobs.addJobForm.srcIp'];
this.form.srcPort = localStorage['settings.jobs.addJobForm.srcPort'];
this.form.dstIp = localStorage['settings.jobs.addJobForm.dstIp'];
Expand All @@ -111,13 +113,14 @@ routes.push({ path: '/jobs', name: 'jobs', component: {
}
},
submitAddJob(event) {
this.addJob(this.form.sensorId, this.form.importId, this.form.srcIp, this.form.srcPort, this.form.dstIp, this.form.dstPort, this.form.beginTime, this.form.endTime);
this.addJob(this.form.sensorId, this.form.importId, this.form.protocol, this.form.srcIp, this.form.srcPort, this.form.dstIp, this.form.dstPort, this.form.beginTime, this.form.endTime);
this.dialog = false;
this.saveAddJobForm();
},
saveAddJobForm() {
if (this.form.sensorId) localStorage['settings.jobs.addJobForm.sensorId'] = this.form.sensorId;
if (this.form.importId) localStorage['settings.jobs.addJobForm.importId'] = this.form.importId;
if (this.form.protocol) localStorage['settings.jobs.addJobForm.protocol'] = this.form.protocol;
if (this.form.srcIp) localStorage['settings.jobs.addJobForm.srcIp'] = this.form.srcIp;
if (this.form.srcPort) localStorage['settings.jobs.addJobForm.srcPort'] = this.form.srcPort;
if (this.form.dstIp) localStorage['settings.jobs.addJobForm.dstIp'] = this.form.dstIp;
Expand All @@ -128,6 +131,7 @@ routes.push({ path: '/jobs', name: 'jobs', component: {
clearAddJobForm() {
this.form.sensorId = null;
this.form.importId = null;
this.form.protocol = null;
this.form.srcIp = null;
this.form.srcPort = null;
this.form.dstIp = null;
Expand All @@ -136,14 +140,15 @@ routes.push({ path: '/jobs', name: 'jobs', component: {
this.form.endTime = null;
localStorage.removeItem('settings.jobs.addJobForm.sensorId');
localStorage.removeItem('settings.jobs.addJobForm.importId');
localStorage.removeItem('settings.jobs.addJobForm.protocol');
localStorage.removeItem('settings.jobs.addJobForm.srcIp');
localStorage.removeItem('settings.jobs.addJobForm.srcPort');
localStorage.removeItem('settings.jobs.addJobForm.dstIp');
localStorage.removeItem('settings.jobs.addJobForm.dstPort');
localStorage.removeItem('settings.jobs.addJobForm.beginTime');
localStorage.removeItem('settings.jobs.addJobForm.endTime');
},
async addJob(sensorId, importId, srcIp, srcPort, dstIp, dstPort, beginTime, endTime) {
async addJob(sensorId, importId, protocol, srcIp, srcPort, dstIp, dstPort, beginTime, endTime) {
try {
if (!sensorId) {
this.$root.showError(this.i18n.sensorIdRequired);
Expand All @@ -154,6 +159,7 @@ routes.push({ path: '/jobs', name: 'jobs', component: {
nodeId: sensorId,
filter: {
importId: importId,
protocol: protocol.toLowerCase(),
srcIp: srcIp,
srcPort: parseInt(srcPort),
dstIp: dstIp,
Expand Down
5 changes: 5 additions & 0 deletions model/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (
"time"
)

const PROTOCOL_ICMP = "icmp"
const PROTOCOL_TCP = "tcp"
const PROTOCOL_UDP = "udp"

type Filter struct {
ImportId string `json:"importId"`
BeginTime time.Time `json:"beginTime"`
Expand All @@ -18,6 +22,7 @@ type Filter struct {
SrcPort int `json:"srcPort"`
DstIp string `json:"dstIp"`
DstPort int `json:"dstPort"`
Protocol string `json:"protocol"`
Parameters map[string]interface{} `json:"parameters"`
}

Expand Down
24 changes: 21 additions & 3 deletions packet/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ func ToStream(packets []gopacket.Packet) (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(full.Bytes())), nil
}

func getPacketProtocol(packet gopacket.Packet) string {
if packet.Layer(layers.LayerTypeTCP) != nil {
return model.PROTOCOL_TCP
}
if packet.Layer(layers.LayerTypeUDP) != nil {
return model.PROTOCOL_UDP
}
if packet.Layer(layers.LayerTypeICMPv4) != nil ||
packet.Layer(layers.LayerTypeICMPv6) != nil {
return model.PROTOCOL_ICMP
}
return ""
}

func filterPacket(filter *model.Filter, packet gopacket.Packet) bool {
var srcIp, dstIp string
var srcPort, dstPort int
Expand Down Expand Up @@ -100,10 +114,14 @@ func filterPacket(filter *model.Filter, packet gopacket.Packet) bool {

include := (filter.BeginTime.IsZero() || timestamp.After(filter.BeginTime)) &&
(filter.EndTime.IsZero() || timestamp.Before(filter.EndTime)) &&
(filter.Protocol == "" || filter.Protocol == getPacketProtocol(packet)) &&
(filter.SrcIp == "" || srcIp == filter.SrcIp) &&
(filter.SrcPort == 0 || srcPort == filter.SrcPort) &&
(filter.DstIp == "" || dstIp == filter.DstIp) &&
(filter.DstPort == 0 || dstPort == filter.DstPort)
(filter.DstIp == "" || dstIp == filter.DstIp)

if include && (filter.Protocol == "udp" || filter.Protocol == "tcp") {
include = (filter.SrcPort == 0 || srcPort == filter.SrcPort) &&
(filter.DstPort == 0 || dstPort == filter.DstPort)
}

return include
}
Expand Down
Loading

0 comments on commit ddc460f

Please sign in to comment.