Details
| ID | GRR |
|---|---|
| Provider | Open Source |
| Category | Endpoint |
| From Version | 5.0.0 |
| Supported Modules | Agentix XSIAM |
README
Overview
Use the GRR integration to manage and communicate with the clients connected to your GRR server.
This integration was integrated and tested with GRR Rapid Response v3.2.3.2.
Configure GRR on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for GRR.
- Click Add instance to create and configure a new integration instance.
- Name: a textual name for the integration instance.
- Server URL: e.g. https://192.168.0.1
- Port: e.g. 8000
- Username: The username of the GRR server.
- Trust any certificate (not secure)
- Use system proxy settings
- Click Test to validate the URLs, token, and connection.
Commands
You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
- Start a flow on a client: grr-set-flows
- Get a list of flows: grr-get-flows
- Get a list of available hunts: grr-get-hunts
- Get hunt summary: grr-get-hunt
- Set a hunt: grr-set-hunts
- Get a list of clients: grr-get-clients
1. Start a flow on a client
Starts a flow on a specified client according to optoinal parameters.
Base Command
grr-set-flows
Input
| Argument Name | Description | Required |
|---|---|---|
| client_id | ID of the client | Required |
| flow | JSON string of the flow to set | Required |
| headers | The headers to display in output | Optional |
Context Output
| Path | Description |
|---|---|
| Flow | A GRR flow |
| Flow.ID | GRR flow ID |
| Flow.Args | GRR flow Args |
| Flow.Creator | GRR flow creator |
| Flow.LastActiveAt | When the flow was last active |
| Flow.NestedFlow | Nested GRR flows |
| Flow.StartedAt | When the flow was started |
| Flow.State | GRR flow state |
Command Example
!grr-set-flows client_id="C.d824a5afc0ee6a46" flow="{\"name\":\"AnalyzeClientMemory\"}"
Context Example
"Flow": [
{
"Expires": null,
"IsRobot": null,
"Description": null,
"Creator": "admin",
"NestedFlow": null,
"LastActiveAt": 1535900542748033,
"Args": {
"request": {}
},
"State": "RUNNING",
"StartedAt": 1535900542745106,
"ID": "F:B51D6EAB",
"Created": null,
"Name": "AnalyzeClientMemory"
}
]
}
Human Readable Output
2. Get a list of flows
Lists flows launched on a specified client.
Base Command
grr-get-flows
Input
| Argument Name | Description | Required |
|---|---|---|
| client_id | ID of the client | Required |
| offset | Starting offset | Optional |
| count | Maximum number of flows to fetch | Optional |
| headers | The headers to display in output | Optional |
Context Output
| Path | Description |
|---|---|
| Flow | A GRR flow |
| Flow.ID | GRR flow ID |
| Flow.Args | GRR flow Args |
| Flow.Creator | GRR flow creator |
| Flow.LastActiveAt | When the flow was last active |
| Flow.NestedFlow | Nested GRR flows |
| Flow.StartedAt | When the flow was started |
| Flow.State | GRR flow state |
Command Example
!grr-get-flows client_id="C.d824a5afc0ee6a46" count="2"
Context Example
{
"Flow": [
{
"Expires": null,
"IsRobot": null,
"Description": null,
"Creator": "admin",
"NestedFlow": [],
"LastActiveAt": 1535900632278975,
"Args": {
"request": {}
},
"State": "TERMINATED",
"StartedAt": 1535900542745106,
"ID": "F:B51D6EAB",
"Created": null,
"Name": "AnalyzeClientMemory"
},
{
"Expires": null,
"IsRobot": null,
"Description": null,
"Creator": "admin",
"NestedFlow": [],
"LastActiveAt": 1535854160731193,
"Args": {
"request": {}
},
"State": "TERMINATED",
"StartedAt": 1535853917723700,
"ID": "F:88F4F65D",
"Created": null,
"Name": "AnalyzeClientMemory"
}
]
}
Human Readable Output
3. Get a list of available hunts
Renders list of available hunts
Base Command
grr-get-hunts
Input
| Argument Name | Description | Required |
|---|---|---|
| offset | Starting offset | Optional |
| count | Maximumn number of items to fetch | Optional |
| created_by | Only return hunts created by a specified user. If the approved_by and/or description_contains arguments are specified, then logical AND is applied to all criteria. This filter can only be used in conjunction with the active_within filter (to prevent queries of death). | Optional |
| description_contains | Only return hunts where the description contains given sub-string (matching is case-insensitive). If the created_by and/or approved_by arguments are specified, then logical AND is applied to all criteria. This filter can only be used in conjunction with the active_within filter (to prevent queries of death). | Optional |
| active_within | Only return hunts that were active within given time duration | Optional |
| headers | The headers to display in output | Optional |
Context Output
| Path | Description |
|---|---|
| Hunt | A GRR hunt |
| Hunt.ID | GRR hunt ID |
| Hunt.Creator | GRR hunt creator |
| Hunt.Description | GRR hunt description |
| Hunt.Created | Time the GRR hunt was created |
| Hunt.Expires | Time the GRR hunt expires |
| Hunt.IsRobot | Whether the hunt originated from a robot |
| Hunt.Name | GRR hunt name |
| Hunt.State | GRR hunt state |
Command Example
!grr-get-hunts count=2
Context Example
{
"Hunt": [
{
"Expires": 1537063517000000,
"Description": "",
"Creator": "admin",
"IsRobot": false,
"State": "PAUSED",
"ID": "H:7B7F45F",
"Created": 1535853917657925,
"Name": "GenericHunt"
},
{
"Expires": 1537059453000000,
"Description": "",
"Creator": "admin",
"IsRobot": false,
"State": "PAUSED",
"ID": "H:AED24E68",
"Created": 1535849853550599,
"Name": "GenericHunt"
}
]
}
Human Readable Output
4. Get hunt summary
Returns the summary of a specified hunt.
Base Command
grr-get-hunt
Input
| Argument Name | Description | Required |
|---|---|---|
| hunt_id | GRR hunt id | Required |
| headers | The headers to display in output | Optional |
Context Output
| Path | Description |
|---|---|
| Hunt | A GRR hunt |
| Hunt.ID | GRR hunt ID |
| Hunt.Creator | GRR hunt creator |
| Hunt.Description | GRR hunt description |
| Hunt.Created | Time the GRR hunt was created |
| Hunt.Expires | Time the GRR hunt expires |
| Hunt.IsRobot | Whether the hunt originated from a robot |
| Hunt.Name | GRR hunt name |
| Hunt.State | GRR hunt state |
Command Example
!grr-get-hunt hunt_id=H:7B7F45F
Context Example
{
"Hunt": [
{
"Expires": 1537063517000000,
"Description": "",
"Creator": "admin",
"IsRobot": false,
"State": "PAUSED",
"ID": "H:7B7F45F",
"Created": 1535853917657925,
"Name": "GenericHunt"
}
]
}
Human Readable Output
5. Set a hunt
Handles hunt creation request.
Base Command
grr-set-hunts
Input
| Argument Name | Description | Required |
|---|---|---|
| hunt_runner_args | Hunt runner arguments | Optional |
| hunt_args | Hunt arguments | Optional |
| headers | The headers to display | Optional |
Context Output
There is no context output for this command.
Command Example
!grr-set-hunts
Context Example
{
"Hunt": [
{
"Expires": 1537117003000000,
"Description": "",
"Creator": "admin",
"IsRobot": false,
"State": "PAUSED",
"ID": "H:74EB48EE",
"Created": 1535907403638694,
"Name": "GenericHunt"
}
]
}
Human Readable Output
6. Get a list of clients
Returns results of a client search.
Base Command
grr-get-clients
Input
| Argument Name | Description | Required |
|---|---|---|
| query | Search query string. See the GRR documentation for more information. | Optional |
| offset | Found clients starting offset | Optional |
| count | Number of found clients to fetch | Optional |
| headers | The headers to display in output | Optional |
Context Output
| Path | Description |
|---|---|
| Client | GRR client |
| Client.ID | GRR client ID |
| Client.LastBootedAt | GRR client LastBootedAt time |
| Client.FirstSeenAt | GRR client FirstSeenAt time |
| Client.LatClock | GRR client LastClock |
| Client.LastCrashAt | GRR client LastCrashAt time |
| Client.AgentInfo | GRR client AgentInfo |
| Client.HardwareInfo | GRR client HardwareInfo |
| Client.Interfaces | GRR client interfaces |
| Client.Labels | GRR client labels |
| Client.OS | GRR client OS details |
| Client.User | GRR Client user |
| Client.Volumes | GRR client volumes |
Command Example
!grr-get-clients
Context Example
{
"Client": [
{
"HardwareInfo": {
"system_product_name": "HVM domU",
"bios_rom_size": "64 kB",
"bios_vendor": "Xen",
"system_sku_number": "Not Specified",
"system_family": "Not Specified",
"system_uuid": "EC2EDE26-BB13-B80C-1915-DC53118B923F",
"system_manufacturer": "Xen",
"bios_release_date": "08/24/2006",
"bios_version": "4.2.amazon",
"serial_number": "ec2ede26-bb13-b80c-1915-dc53118b923f",
"bios_revision": "4.2"
},
"LastClock": 1535907460060247,
"Interfaces": [
{
"ifname": "lo",
"addresses": [
{
"packed_bytes": "fwAAAQ==",
"address_type": "INET"
},
{
"packed_bytes": "AAAAAAAAAAAAAAAAAAAAAQ==",
"address_type": "INET6"
}
],
"mac_address": "AAAAAAAA"
},
{
"ifname": "eth0",
"addresses": [
{
"packed_bytes": "rB8sWw==",
"address_type": "INET"
},
{
"packed_bytes": "/oAAAAAAAAAE1kv//h5yfg==",
"address_type": "INET6"
}
],
"mac_address": "BtZLHnJ+"
}
],
"OS": {
"kernel": "4.4.0-1065-aws",
"install_date": 1534280169000000,
"system": "Linux",
"fqdn": "ip-172-31-44-91.eu-central-1.compute.internal",
"machine": "x86_64",
"version": "16.4",
"release": "Ubuntu"
},
"AgentInfo": {
"client_name": "grr",
"client_description": "grr linux amd64",
"client_version": 3232,
"build_time": "2018-06-28 09:37:57"
},
"Labels": [],
"LastBootedAt": 1535292604000000,
"FirstSeenAt": 1535293827970976,
"User": [
{
"username": "ubuntu",
"shell": "/bin/bash",
"homedir": "/home/ubuntu",
"last_logon": 1535356309000000,
"gid": 1000,
"full_name": "Ubuntu",
"uid": 1000
}
],
"Volumes": [
{
"total_allocation_units": 50808745,
"bytes_per_sector": 4096,
"sectors_per_allocation_unit": 1,
"unixvolume": {
"mount_point": "/"
},
"actual_available_allocation_units": 50027766
}
],
"LastCrashAt": null,
"LastSeenAt": 1535907460075229,
"ID": "C.d824a5afc0ee6a46"
}
]
}
Human Readable Output
Configuration parameters
server— Server URL (e.g. https://192.168.0.1) (required)port— Portcredentials— Username (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (13)
-
grr-get-clientsRenders results of a client search.
-
grr-get-filesDeprecatedDownloads files from specified machine without requiring approval
-
grr-get-flowsLists flows launched on a given client
-
grr-get-huntRenders hunt's summary
-
grr-get-huntsRenders list of available hunts
-
grr-set-flowsStarts a flow on a given client with given parameters
-
grr-set-huntsHandles hunt creation request
-
grr_get_filesDownloads files from specified machine without requiring approval
-
grr_get_flowsDeprecatedLists flows launched on a given client
-
grr_get_huntRenders hunt's summary
-
grr_get_huntsRenders list of available hunts
-
grr_set_flowsDeprecatedStarts a flow on a given client with given parameters
-
grr_set_huntsHandles hunt creation request
var username = params.credentials.identifier; var password = params.credentials.password; var server = params.server + ':' + params.port; var insecure = params.insecure; var proxy = params.proxy; var getCSRFToken = function(url) { var res = http( url, { Method: 'GET', Username: username, Password: password }, insecure, proxy ); if (res.StatusCode < 200 || res.StatusCode >= 300) { throw 'Failed to get CSRF token, request status code: ' + res.StatusCode + ' and Body: ' + res.Body + '.'; } for (var ix in res.Cookies) { if (res.Cookies[ix].Name.toLowerCase() === 'csrftoken') { return res.Cookies[ix].Value; } } return 'Failed to get CSRF token, Please verify credentials and check GRR logs.'; } var sendRequest = function(method, url, queryName, body) { var csrfToken = getCSRFToken(url); var res = http( url, { Method: method, Username: username, Password: password, Body: body, Headers: {'X-CSRFToken': [csrfToken]}, Cookies: [{Name: 'csrftoken', Value: csrfToken, Path: '/', MaxAge: 300}] }, insecure, proxy ); if (res.StatusCode < 200 || res.StatusCode >= 300) { throw 'Failed to ' + (queryName || 'get main page') + ', request status code: ' + res.StatusCode + ' and Body: ' + res.Body + '.'; } return res.Body; } var parseJSON = function(raw){ var output = null; var innerParse = function(i){ var att = null; if(typeof i === 'object' && 'value' in i){ if (typeof i.value === 'object'){ i = JSON.parse(JSON.stringify(i.value)); }else { i = i.value; } } if(typeof i === 'object'){ att = {}; for (var k in i){ if (k == "urn"){ if(typeof i[k] === 'object' && 'value' in i[k]){ if (typeof i[k].value === 'object'){ i[k] = JSON.parse(JSON.stringify(i[k].value)); }else { i[k] = i[k].value; } } if(typeof i[k] === 'string'){ var urn = i[k].split('/'); if (urn.length > 0){ att.id = urn[urn.length - 1]; } } } if (typeof i[k] === 'object'){ att[k] = parseJSON(i[k]); } else { att[k] = i[k]; } } } else{ att = i; } return att; } if(raw instanceof Array){ output = []; raw.forEach(function(row){ output.push(innerParse(row)) }) } else { output = innerParse(raw); } return output; } var getHunts = function(method, url, queryName, body, jsonPath, title) { var raw = JSON.parse(sendRequest(method, url, queryName, body).slice(5)); var output = parseJSON(jsonPath ? dq(raw,jsonPath) : raw); if(!(output instanceof Array )){ output = [output]; } var context = { Hunt : [] }; output.forEach(function(i){ var huntEntity = { ID : i.id, Created : i.created, Creator : i.creator, Description : i.description, Expires : i.expires, IsRobot : i.is_robot, Name: i.name, State: i.state }; context.Hunt.push(huntEntity); }); return { Type: entryTypes.note, Contents: raw, ContentsFormat: formats.json, HumanReadable: tblToMd(title,output,argToList(args.headers)), EntryContext: context }; }; var getHunt = function(method, url, queryName, body, jsonPath, title) { var raw = JSON.parse(sendRequest(method, url, queryName, body).slice(5)); var output = parseJSON(jsonPath ? dq(raw,jsonPath) : raw); if(!(output instanceof Array )){ output = [output]; } var context = { Hunt : [] }; output.forEach(function(i){ var huntEntity = { ID : i.id, Created : i.created, Creator : i.creator, Description : i.description, Expires : i.expires, IsRobot : i.is_robot, Name: i.name, State: i.state }; context.Hunt.push(huntEntity); }); return { Type: entryTypes.note, Contents: raw, ContentsFormat: formats.json, HumanReadable: tblToMd(title,output,argToList(args.headers)), EntryContext: context }; }; var getFlows = function(method, url, queryName, body, jsonPath, title) { var raw = JSON.parse(sendRequest(method, url, queryName, body).slice(5)); var output = parseJSON(jsonPath ? dq(raw,jsonPath) : raw); if(!(output instanceof Array )){ output = [output]; } var context = { Flow : [] }; output.forEach(function(i){ var flowEntity = { ID : i.id, Created : i.created, Creator : i.creator, Description : i.description, Expires : i.expires, IsRobot : i.is_robot, Name: i.name, State: i.state, Args : i.args, LastActiveAt : i.last_active_at, NestedFlow : (i.nested_flows && typeof i === 'object' ? i.nested_flows.map(function(a) {return a.id}) : i.nested_flows), //i.nested_flows.map(function(a) {return a.id}), StartedAt : i.started_at }; context.Flow.push(flowEntity); }); return { Type: entryTypes.note, Contents: raw, ContentsFormat: formats.json, HumanReadable: tblToMd(title,output,argToList(args.headers)), EntryContext: context }; }; var getClients = function(method, url, queryName, body, jsonPath, title) { var raw = JSON.parse(sendRequest(method, url, queryName, body).slice(5)); var output = parseJSON(jsonPath ? dq(raw,jsonPath) : raw); if(!(output instanceof Array )){ output = [output]; } var context = { Client : [] }; output.forEach(function(i){ var clientEntity = { FirstSeenAt : i.first_seen_at, ID : i.id, LastBootedAt : i.last_booted_at, LastClock : i.last_clock, LastCrashAt : i.last_crash_at, LastSeenAt : i.last_seen_at, AgentInfo : i.agent_info, HardwareInfo : i.hardware_info, Interfaces : i.interfaces, Labels : i.labels, OS : i.os_info, User : i.users, Volumes : i.volumes }; context.Client.push(clientEntity); }); return { Type: entryTypes.note, Contents: raw, ContentsFormat: formats.json, HumanReadable: tblToMd(title,output,argToList(args.headers)), EntryContext: context }; }; var sendRequestAndParse = function(method, url, queryName, body) { return JSON.parse(sendRequest(method, url, queryName, body).slice(5)); }; switch (command) { case 'test-module': return sendRequest('GET', server) ? 'ok' : 'No body in response'; case 'grr-set-flows': case 'grr_set_flows'://deprecated return getFlows('POST', server + '/api/clients/' + args.client_id + '/flows', 'post flow', '{"flow": ' + args.flow + '}',null,"Set Flow"); case 'grr-get-files': case 'grr_get_files'://deprecated var pathsArr; try { pathsArr = JSON.parse(args.paths); } catch (e) { logDebug('input is not a valid JSON trying to split'); pathsArr = args.paths ? args.paths.split(",") : []; } args.paths = pathsArr; return sendRequestAndParse('POST', server + '/api/robot-actions/get-files', 'post get-files', JSON.stringify(args)); case 'grr-get-hunts': case 'grr_get_hunts'://deprecated return getHunts('GET', server + '/api/hunts' + encodeToURLQuery(args), 'get hunts',null,"items",'Hunts'); case 'grr-get-hunt': case 'grr_get_hunt'://deprecated return getHunt('GET', server + '/api/hunts/' + args.hunt_id, 'get hunt',null,"","Hunt {0}".format(args.hunt_id)); case 'grr-set-hunts': case 'grr_set_hunts'://deprecated return getHunts('POST', server + '/api/hunts', 'set hunts', JSON.stringify(args),null,null,"Set Hunt"); case 'grr-get-clients': return getClients('GET',server + '/api/clients' + encodeToURLQuery(args), 'get client',null,"items","Clients"); case 'grr-get-flows': case 'grr_get_flows'://deprecated var urlArgs = {}; if (args.count) { urlArgs.count = args.count; } if (args.offset) { urlArgs.offset = args.offset; } return getFlows('GET', server + '/api/clients/' + args.client_id + '/flows' + encodeToURLQuery(urlArgs), 'get flow',null,"items","Flows"); }





