Skip to content

Commit

Permalink
Added integration test for bucket level rules creation
Browse files Browse the repository at this point in the history
Signed-off-by: Stevan Buzejic <[email protected]>
  • Loading branch information
stevanbuzejic committed Nov 10, 2022
1 parent 1b36aa1 commit 6336299
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 8 deletions.
68 changes: 68 additions & 0 deletions src/test/java/org/opensearch/securityanalytics/TestHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,35 @@ public static String productIndexAvgAggRule(){
" condition: sel | avg(fieldA) by fieldC > 110";
}

public static String randomAggregationRule(String aggFunction, String signAndValue) {
String rule = "title: Remote Encrypting File System Abuse\n" +
"id: 5f92fff9-82e2-48eb-8fc1-8b133556a551\n" +
"description: Detects remote RPC calls to possibly abuse remote encryption service via MS-EFSR\n" +
"references:\n" +
" - https://attack.mitre.org/tactics/TA0008/\n" +
" - https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-36942\n" +
" - https://github.com/jsecurity101/MSRPC-to-ATTACK/blob/main/documents/MS-EFSR.md\n" +
" - https://github.com/zeronetworks/rpcfirewall\n" +
" - https://zeronetworks.com/blog/stopping_lateral_movement_via_the_rpc_firewall/\n" +
"tags:\n" +
" - attack.defense_evasion\n" +
"status: experimental\n" +
"author: Sagie Dulce, Dekel Paz\n" +
"date: 2022/01/01\n" +
"modified: 2022/01/01\n" +
"logsource:\n" +
" product: rpc_firewall\n" +
" category: application\n" +
" definition: 'Requirements: install and apply the RPC Firewall to all processes with \"audit:true action:block uuid:df1941c5-fe89-4e79-bf10-463657acf44d or c681d488-d850-11d0-8c52-00c04fd90f7e'\n" +
"detection:\n" +
" sel:\n" +
" Opcode: Info\n" +
" condition: sel | %s(SeverityValue) by Version %s\n" +
"falsepositives:\n" +
" - Legitimate usage of remote file encryption\n" +
"level: high";
return String.format(Locale.ROOT, rule, aggFunction, signAndValue);
}
public static String windowsIndexMapping() {
return "\"properties\": {\n" +
" \"AccessList\": {\n" +
Expand Down Expand Up @@ -1455,6 +1484,45 @@ public static String windowsIndexMapping() {
" }";
}

public static String randomDoc(int severity, int version) {
String doc = "{\n" +
"\"EventTime\":\"2020-02-04T14:59:39.343541+00:00\",\n" +
"\"HostName\":\"EC2AMAZ-EPO7HKA\",\n" +
"\"Keywords\":\"9223372036854775808\",\n" +
"\"SeverityValue\":%s,\n" +
"\"Severity\":\"INFO\",\n" +
"\"EventID\":22,\n" +
"\"SourceName\":\"Microsoft-Windows-Sysmon\",\n" +
"\"ProviderGuid\":\"{5770385F-C22A-43E0-BF4C-06F5698FFBD9}\",\n" +
"\"Version\":%s,\n" +
"\"TaskValue\":22,\n" +
"\"OpcodeValue\":0,\n" +
"\"RecordNumber\":9532,\n" +
"\"ExecutionProcessID\":1996,\n" +
"\"ExecutionThreadID\":2616,\n" +
"\"Channel\":\"Microsoft-Windows-Sysmon/Operational\",\n" +
"\"Domain\":\"NT AUTHORITY\",\n" +
"\"AccountName\":\"SYSTEM\",\n" +
"\"UserID\":\"S-1-5-18\",\n" +
"\"AccountType\":\"User\",\n" +
"\"Message\":\"Dns query:\\r\\nRuleName: \\r\\nUtcTime: 2020-02-04 14:59:38.349\\r\\nProcessGuid: {b3c285a4-3cda-5dc0-0000-001077270b00}\\r\\nProcessId: 1904\\r\\nQueryName: EC2AMAZ-EPO7HKA\\r\\nQueryStatus: 0\\r\\nQueryResults: 172.31.46.38;\\r\\nImage: C:\\\\Program Files\\\\nxlog\\\\nxlog.exe\",\n" +
"\"Category\":\"Dns query (rule: DnsQuery)\",\n" +
"\"Opcode\":\"Info\",\n" +
"\"UtcTime\":\"2020-02-04 14:59:38.349\",\n" +
"\"ProcessGuid\":\"{b3c285a4-3cda-5dc0-0000-001077270b00}\",\n" +
"\"ProcessId\":\"1904\",\"QueryName\":\"EC2AMAZ-EPO7HKA\",\"QueryStatus\":\"0\",\n" +
"\"QueryResults\":\"172.31.46.38;\",\n" +
"\"Image\":\"C:\\\\Program Files\\\\nxlog\\\\regsvr32.exe\",\n" +
"\"EventReceivedTime\":\"2020-02-04T14:59:40.780905+00:00\",\n" +
"\"SourceModuleName\":\"in\",\n" +
"\"SourceModuleType\":\"im_msvistalog\",\n" +
"\"CommandLine\": \"eachtest\",\n" +
"\"Initiated\": \"true\"\n" +
"}";
return String.format(Locale.ROOT, doc, severity, version);

}

public static String randomDoc() {
return "{\n" +
"\"EventTime\":\"2020-02-04T14:59:39.343541+00:00\",\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
import java.util.Map;
import java.util.stream.Collectors;
import org.opensearch.securityanalytics.model.Rule;

import static org.opensearch.securityanalytics.TestHelpers.productIndexMaxAggRule;
import static org.opensearch.securityanalytics.TestHelpers.productIndexAvgAggRule;
import static org.opensearch.securityanalytics.TestHelpers.productIndexMapping;
import static org.opensearch.securityanalytics.TestHelpers.randomAggregationRule;
import static org.opensearch.securityanalytics.TestHelpers.randomDetector;
import static org.opensearch.securityanalytics.TestHelpers.randomDetectorType;
import static org.opensearch.securityanalytics.TestHelpers.randomDetectorWithInputs;
Expand Down Expand Up @@ -259,9 +259,9 @@ public void testCreatingADetectorWithAggregationRules() throws IOException {

Map<String, Object> responseBody = asMap(createResponse);

String createdRuleId = responseBody.get("_id").toString();
String createdId = responseBody.get("_id").toString();

DetectorInput input = new DetectorInput("windows detector for security analytics", List.of("windows"), List.of(new DetectorRule(createdRuleId)),
DetectorInput input = new DetectorInput("windows detector for security analytics", List.of("windows"), List.of(new DetectorRule(createdId)),
getRandomPrePackagedRules().stream().map(DetectorRule::new).collect(Collectors.toList()));
Detector detector = randomDetectorWithInputs(List.of(input));

Expand All @@ -270,19 +270,19 @@ public void testCreatingADetectorWithAggregationRules() throws IOException {

responseBody = asMap(createResponse);

createdRuleId = responseBody.get("_id").toString();
createdId = responseBody.get("_id").toString();
int createdVersion = Integer.parseInt(responseBody.get("_version").toString());
Assert.assertNotEquals("response is missing Id", Detector.NO_ID, createdRuleId);
Assert.assertNotEquals("response is missing Id", Detector.NO_ID, createdId);
Assert.assertTrue("incorrect version", createdVersion > 0);
Assert.assertEquals("Incorrect Location header", String.format(Locale.getDefault(), "%s/%s", SecurityAnalyticsPlugin.DETECTOR_BASE_URI, createdRuleId), createResponse.getHeader("Location"));
Assert.assertEquals("Incorrect Location header", String.format(Locale.getDefault(), "%s/%s", SecurityAnalyticsPlugin.DETECTOR_BASE_URI, createdId), createResponse.getHeader("Location"));
Assert.assertFalse(((Map<String, Object>) responseBody.get("detector")).containsKey("rule_topic_index"));
Assert.assertFalse(((Map<String, Object>) responseBody.get("detector")).containsKey("findings_index"));
Assert.assertFalse(((Map<String, Object>) responseBody.get("detector")).containsKey("alert_index"));

String request = "{\n" +
" \"query\" : {\n" +
" \"match\":{\n" +
" \"_id\": \"" + createdRuleId + "\"\n" +
" \"_id\": \"" + createdId + "\"\n" +
" }\n" +
" }\n" +
"}";
Expand Down Expand Up @@ -320,7 +320,7 @@ public void testCreatingADetectorWithAggregationRules() throws IOException {
Map<String, Object> executeResults = entityAsMap(executeResponse);
// verify bucket level monitor findings
Map<String, String> params = new HashMap<>();
params.put("detector_id", createdRuleId);
params.put("detector_id", createdId);
Response getFindingsResponse = makeRequest(client(), "GET", SecurityAnalyticsPlugin.FINDINGS_BASE_URI + "/_search", params, null);
Map<String, Object> getFindingsBody = entityAsMap(getFindingsResponse);
assertNotNull(getFindingsBody);
Expand Down Expand Up @@ -387,6 +387,113 @@ public void testUpdateADetector() throws IOException {
Assert.assertEquals(6, response.getHits().getTotalHits().value);
}

/**
* 1. Creates detector with 5 doc prepackaged level rules and one doc level monitor based on the given rules
* 2. Creates two aggregation rules and assigns to a detector, while removing 5 prepackaged rules
* 3. Verifies that two bucket level monitor exists
* 4. Verifies the findings
* @throws IOException
*/
public void testUpdateDetectorAddingAggregationRuleRemovingDocLevelRule() throws IOException {
String index = createTestIndex(randomIndex(), windowsIndexMapping());

// Execute CreateMappingsAction to add alias mapping for index
Request createMappingRequest = new Request("POST", SecurityAnalyticsPlugin.MAPPER_BASE_URI);
// both req params and req body are supported
createMappingRequest.setJsonEntity(
"{ \"index_name\":\"" + index + "\"," +
" \"rule_topic\":\"" + randomDetectorType() + "\", " +
" \"partial\":true" +
"}"
);

Response createMappingResponse = client().performRequest(createMappingRequest);
assertEquals(HttpStatus.SC_OK, createMappingResponse.getStatusLine().getStatusCode());

Detector detector = randomDetector(getRandomPrePackagedRules());

Response createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.DETECTOR_BASE_URI, Collections.emptyMap(), toHttpEntity(detector));
Assert.assertEquals("Create detector failed", RestStatus.CREATED, restStatus(createResponse));

Map<String, Object> responseBody = asMap(createResponse);

String detectorId = responseBody.get("_id").toString();

String request = "{\n" +
" \"query\" : {\n" +
" \"match_all\":{\n" +
" }\n" +
" }\n" +
"}";
SearchResponse response = executeSearchAndGetResponse(DetectorMonitorConfig.getRuleIndex(randomDetectorType()), request, true);
Assert.assertEquals(5, response.getHits().getTotalHits().value);

request = "{\n" +
" \"query\" : {\n" +
" \"match\":{\n" +
" \"_id\": \"" + detectorId + "\"\n" +
" }\n" +
" }\n" +
"}";

// Verify that bucket level monitor is created
List<SearchHit> hits = executeSearch(Detector.DETECTORS_INDEX, request);
SearchHit hit = hits.get(0);
Map<String, Object> detectorAsMap = (Map<String, Object>) hit.getSourceAsMap().get("detector");
List<String> monitorIds = (List<String>) (detectorAsMap).get("monitor_id");
Assert.assertEquals(1, monitorIds.size());
String monitorId = monitorIds.get(0);
String monitorType = ((Map<String, String>) entityAsMap(client().performRequest(new Request("GET", "/_plugins/_alerting/monitors/" + monitorId))).get("monitor")).get("monitor_type");
Assert.assertEquals(MonitorType.DOC_LEVEL_MONITOR.getValue(), monitorType);

// Create aggregation rules
String cntRuleId = createRule(randomAggregationRule( "sum", " > 2"));
String avgTermRuleId = createRule(randomAggregationRule( "avg", " > 1"));
// Update detector and empty doc level rules so detector contains only one aggregation rule
DetectorInput input = new DetectorInput("windows detector for security analytics", List.of("windows"), List.of(new DetectorRule(cntRuleId), new DetectorRule(avgTermRuleId)),
Collections.emptyList());
Detector updatedDetector = randomDetectorWithInputs(List.of(input));

Response updateResponse = makeRequest(client(), "PUT", SecurityAnalyticsPlugin.DETECTOR_BASE_URI + "/" + detectorId, Collections.emptyMap(), toHttpEntity(updatedDetector));
Assert.assertEquals("Update detector failed", RestStatus.OK, restStatus(updateResponse));
Map updateResponseBody = asMap(updateResponse);
detectorId = updateResponseBody.get("_id").toString();

request = "{\n" +
" \"query\" : {\n" +
" \"match\":{\n" +
" \"_id\": \"" + detectorId + "\"\n" +
" }\n" +
" }\n" +
"}";

hits = executeSearch(Detector.DETECTORS_INDEX, request);
hit = hits.get(0);
detectorAsMap = (Map<String, Object>) hit.getSourceAsMap().get("detector");
monitorIds = (List<String>) (detectorAsMap).get("monitor_id");
Assert.assertEquals(2, monitorIds.size());
indexDoc(index, "1", randomDoc(2, 4));
indexDoc(index, "2", randomDoc(3, 4));


for(String id: monitorIds){
monitorType = ((Map<String, String>) entityAsMap(client().performRequest(new Request("GET", "/_plugins/_alerting/monitors/" + id))).get("monitor")).get("monitor_type");
Assert.assertEquals(MonitorType.BUCKET_LEVEL_MONITOR.getValue(), monitorType);
executeAlertingMonitor(id, Collections.emptyMap());
}
// verify bucket level monitor findings
Map<String, String> params = new HashMap<>();
params.put("detector_id", detectorId);
Response getFindingsResponse = makeRequest(client(), "GET", SecurityAnalyticsPlugin.FINDINGS_BASE_URI + "/_search", params, null);
Map<String, Object> getFindingsBody = entityAsMap(getFindingsResponse);
assertNotNull(getFindingsBody);
Assert.assertEquals(2, getFindingsBody.get("total_findings"));
List<String> docIds = ((ArrayList)((Map<String, Object>)((List)getFindingsBody.get("findings")).get(0)).get("related_doc_ids"));
assertTrue(Arrays.asList("1", "2").containsAll(docIds));
String findingDetectorId = ((Map<String, Object>)((List)getFindingsBody.get("findings")).get(0)).get("detectorId").toString();
assertEquals(detectorId, findingDetectorId);
}

public void testUpdateDetectorAddingNewAggregationRule() throws IOException {
String index = createTestIndex(randomIndex(), productIndexMapping());

Expand Down

0 comments on commit 6336299

Please sign in to comment.