Skip to content

Commit

Permalink
Add obfuscation chunk 2 (#1604)
Browse files Browse the repository at this point in the history
Signed-off-by: Marine LM <[email protected]>
  • Loading branch information
MarineLeM authored Jan 2, 2025
1 parent 7a8a756 commit 7b58130
Show file tree
Hide file tree
Showing 30 changed files with 814 additions and 449 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
import io.openbas.injectors.channel.model.ChannelContent;
import io.openbas.rest.exercise.exports.VariableWithValueMixin;
import io.openbas.rest.inject.form.InjectDependencyInput;
import io.openbas.rest.payload.PayloadCreationService;
import io.openbas.rest.payload.form.PayloadCreateInput;
import io.openbas.rest.payload.service.PayloadCreationService;
import io.openbas.service.FileService;
import io.openbas.service.ImportEntry;
import io.openbas.service.ScenarioService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import io.openbas.injector_contract.fields.*;
import io.openbas.injectors.caldera.client.model.Ability;
import io.openbas.injectors.caldera.config.CalderaInjectorConfig;
import io.openbas.injectors.caldera.model.CalderaInjectContent;
import io.openbas.injectors.caldera.model.Obfuscator;
import io.openbas.injectors.caldera.service.CalderaInjectorService;
import jakarta.validation.constraints.NotNull;
Expand Down Expand Up @@ -74,7 +75,11 @@ private ContractSelect obfuscatorField() {
List<Obfuscator> obfuscators = this.injectorCalderaService.obfuscators();
Map<String, String> obfuscatorChoices =
obfuscators.stream().collect(Collectors.toMap(Obfuscator::getName, Obfuscator::getName));
return selectFieldWithDefault("obfuscator", "Obfuscators", obfuscatorChoices, "base64");
return selectFieldWithDefault(
"obfuscator",
"Obfuscators",
obfuscatorChoices,
CalderaInjectContent.getDefaultObfuscator());
}

private ContractExpectations expectations() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ public ExecutionProcess process(
@NotNull final Execution execution, @NotNull final ExecutableInject injection)
throws Exception {
CalderaInjectContent content = contentConvert(injection, CalderaInjectContent.class);
String obfuscator = content.getObfuscator() != null ? content.getObfuscator() : "base64";
String obfuscator =
content.getObfuscator() != null
? content.getObfuscator()
: CalderaInjectContent.getDefaultObfuscator();
Inject inject =
this.injectRepository.findById(injection.getInjection().getInject().getId()).orElseThrow();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.openbas.injectors.caldera.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.openbas.model.inject.form.Expectation;
import java.util.ArrayList;
Expand All @@ -16,4 +17,9 @@ public class CalderaInjectContent {

@JsonProperty("expectations")
private List<Expectation> expectations = new ArrayList<>();

@JsonIgnore
public static String getDefaultObfuscator() {
return "plain-text";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.openbas.injectors.openbas.util;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import lombok.Getter;

public class OpenBASObfuscationMap {
private final Map<String, OpenBASObfuscation> obfuscationMap;

@Getter
public static class OpenBASObfuscation {
private final String information;
private final BiFunction<String, String, String> obfuscate;

public OpenBASObfuscation(String information, BiFunction<String, String, String> obfuscate) {
this.information = information;
this.obfuscate = obfuscate;
}
}

public OpenBASObfuscationMap() {
this.obfuscationMap = new HashMap<>();
this.registerObfuscation("plain-text", "", this::obfuscatePlainText);
this.registerObfuscation(
"base64", "CMD does not support base64 obfuscation", this::obfuscateBase64);
}

public void registerObfuscation(
String key, String information, BiFunction<String, String, String> function) {
if (key == null || function == null) {
throw new IllegalArgumentException("Key and function must not be null.");
}
obfuscationMap.put(key, new OpenBASObfuscation(information, function));
}

public String executeObfuscation(String key, String command, String executor) {
OpenBASObfuscation obfuscation = obfuscationMap.get(key);
if (obfuscation != null) {
return obfuscation.getObfuscate().apply(command, executor);
}
throw new IllegalArgumentException("No obfuscation found for key: " + key);
}

public Map<String, String> getAllObfuscationInfo() {
Map<String, String> keyInfoMap = new HashMap<>();
for (Map.Entry<String, OpenBASObfuscation> entry : obfuscationMap.entrySet()) {
keyInfoMap.put(entry.getKey(), entry.getValue().getInformation());
}
return keyInfoMap;
}

private String obfuscatePlainText(String command, String executor) {
return command;
}

private String obfuscateBase64(String command, String executor) {
String obfuscatedCommand = command;

if (executor.equals("psh") || executor.equals("cmd")) {
byte[] utf16Bytes = command.getBytes(StandardCharsets.UTF_16LE);
String base64 = Base64.getEncoder().encodeToString(utf16Bytes);
obfuscatedCommand = String.format("powershell -Enc %s", base64);

} else if (executor.equals("bash") || executor.equals("sh")) {
obfuscatedCommand =
String.format(
"eval \"$(echo %s | base64 --decode)\"",
Base64.getEncoder().encodeToString(command.getBytes()));
}
return obfuscatedCommand;
}

public String getDefaultObfuscator() {
return "plain-text";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.openbas.migration;

import java.sql.Connection;
import java.sql.Statement;
import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;
import org.springframework.stereotype.Component;

@Component
public class V3_55__Add_obfuscator_inject_contract extends BaseJavaMigration {

@Override
public void migrate(Context context) throws Exception {
Connection connection = context.getConnection();
Statement statement = connection.createStatement();

String addObfuscationQuery =
"UPDATE injectors_contracts "
+ "SET injector_contract_content = JSONB_SET("
+ " injector_contract_content::jsonb,"
+ " '{fields}',"
+ " CASE WHEN NOT EXISTS ("
+ " SELECT 1 FROM jsonb_array_elements(injector_contract_content::jsonb->'fields') AS fields "
+ " WHERE fields->>'key' = 'obfuscator'"
+ " ) THEN "
+ " injector_contract_content::jsonb->'fields' || "
+ " jsonb_build_object("
+ " 'key', 'obfuscator',"
+ " 'cardinality', '1',"
+ " 'defaultValue', jsonb_build_array('plain-text'),"
+ " 'mandatory', false,"
+ " 'mandatoryGroups', null,"
+ " 'label', 'Obfuscator',"
+ " 'readOnly', false,"
+ " 'linkedFields', jsonb_build_array(),"
+ " 'linkedValues', jsonb_build_array(),"
+ " 'type', 'choice',"
+ " 'choices', jsonb_build_array("
+ " jsonb_build_object('value', 'base64', 'label', 'base64', 'information', 'CMD does not support base64 obfuscation'),"
+ " jsonb_build_object('value', 'plain-text', 'label', 'plain-text', 'information', '')"
+ " )"
+ " )"
+ " ELSE "
+ " injector_contract_content::jsonb->'fields'"
+ " END"
+ ") "
+ "WHERE injector_id IN ("
+ " SELECT injector_id FROM injectors WHERE injector_type = 'openbas_implant'"
+ ") "
+ "AND injector_contract_payload IN ("
+ " SELECT payload_id FROM payloads WHERE payload_type = 'Command'"
+ ")";
statement.executeUpdate(addObfuscationQuery);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public class StatusPayloadOutput {
@JsonProperty("payload_arguments")
private List<PayloadArgument> arguments = new ArrayList<>();

@JsonProperty("payload_obfuscator")
private String obfuscator;

@JsonProperty("payload_prerequisites")
private List<PayloadPrerequisite> prerequisites = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ public Inject injectExecutionCallback(

@GetMapping(INJECT_URI + "/{injectId}/executable-payload")
@Tracing(name = "Get payload ready to be executed", layer = "api", operation = "GET")
public Payload getExecutablePayloadInject(@PathVariable @NotBlank final String injectId) {
public Payload getExecutablePayloadInject(@PathVariable @NotBlank final String injectId)
throws Exception {
return executableInjectService.getExecutablePayloadInject(injectId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.openbas.database.model.*;
import io.openbas.database.repository.InjectRepository;
import io.openbas.injectors.openbas.model.OpenBASImplantInjectContent;
import io.openbas.injectors.openbas.util.OpenBASObfuscationMap;
import io.openbas.rest.exception.ElementNotFoundException;
import java.util.ArrayList;
import java.util.Base64;
Expand All @@ -19,6 +21,7 @@
public class ExecutableInjectService {

private final InjectRepository injectRepository;
private final InjectService injectService;
private static final Pattern argumentsRegex = Pattern.compile("#\\{([^#{}]+)}");
private static final Pattern cmdVariablesRegex = Pattern.compile("%(\\w+)%");

Expand Down Expand Up @@ -79,25 +82,27 @@ private String processAndEncodeCommand(
List<PayloadArgument> defaultArguments,
ObjectNode injectContent,
String obfuscator) {
OpenBASObfuscationMap obfuscationMap = new OpenBASObfuscationMap();
String computedCommand = replaceArgumentsByValue(command, defaultArguments, injectContent);

if (executor.equals("cmd")) {
computedCommand = replaceCmdVariables(computedCommand);
computedCommand = computedCommand.trim().replace("\n", " & ");
}

if (obfuscator.equals("base64")) {
return Base64.getEncoder().encodeToString(computedCommand.getBytes());
}
computedCommand = obfuscationMap.executeObfuscation(obfuscator, computedCommand, executor);

return computedCommand;
return Base64.getEncoder().encodeToString(computedCommand.getBytes());
}

public Payload getExecutablePayloadInject(String injectId) {
public Payload getExecutablePayloadInject(String injectId) throws Exception {
Inject inject =
this.injectRepository.findById(injectId).orElseThrow(ElementNotFoundException::new);
InjectorContract contract =
inject.getInjectorContract().orElseThrow(ElementNotFoundException::new);
OpenBASImplantInjectContent content =
injectService.convertInjectContent(inject, OpenBASImplantInjectContent.class);
String obfuscator = content.getObfuscator() != null ? content.getObfuscator() : "plain-text";

// prerequisite
contract
Expand All @@ -112,7 +117,7 @@ public Payload getExecutablePayloadInject(String injectId) {
prerequisite.getExecutor(),
contract.getPayload().getArguments(),
inject.getContent(),
"base64"));
obfuscator));
}
if (hasText(prerequisite.getGetCommand())) {
prerequisite.setGetCommand(
Expand All @@ -121,7 +126,7 @@ public Payload getExecutablePayloadInject(String injectId) {
prerequisite.getExecutor(),
contract.getPayload().getArguments(),
inject.getContent(),
"base64"));
obfuscator));
}
});

Expand All @@ -135,7 +140,7 @@ public Payload getExecutablePayloadInject(String injectId) {
contract.getPayload().getCleanupExecutor(),
contract.getPayload().getArguments(),
inject.getContent(),
"base64"));
obfuscator));
}

// Command
Expand All @@ -147,7 +152,7 @@ public Payload getExecutablePayloadInject(String injectId) {
payloadCommand.getExecutor(),
contract.getPayload().getArguments(),
inject.getContent(),
"base64"));
obfuscator));
return payloadCommand;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static java.time.Instant.now;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.openbas.database.model.ExecutionStatus;
import io.openbas.database.model.Inject;
import io.openbas.database.model.InjectDocument;
Expand All @@ -19,6 +20,7 @@
import jakarta.annotation.Resource;
import jakarta.transaction.Transactional;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.Instant;
import java.util.List;
import java.util.stream.Stream;
Expand Down Expand Up @@ -84,6 +86,12 @@ public void cleanInjectsDocExercise(String exerciseId, String documentId) {
injectDocumentRepository.deleteAll(updatedInjects);
}

public <T> T convertInjectContent(@NotNull final Inject inject, @NotNull final Class<T> converter)
throws Exception {
ObjectNode content = inject.getContent();
return this.mapper.treeToValue(content, converter);
}

public void cleanInjectsDocScenario(String scenarioId, String documentId) {
// Delete document from all scenario injects
List<Inject> scenarioInjects =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@

import io.openbas.database.model.*;
import io.openbas.database.repository.*;
import io.openbas.integrations.PayloadService;
import io.openbas.rest.exception.BadRequestException;
import io.openbas.rest.exception.ElementNotFoundException;
import io.openbas.rest.helper.RestBehavior;
import io.openbas.rest.payload.form.PayloadCreateInput;
import io.openbas.rest.payload.form.PayloadUpdateInput;
import io.openbas.rest.payload.form.PayloadUpsertInput;
import io.openbas.rest.payload.form.PayloadsDeprecateInput;
import io.openbas.rest.payload.service.PayloadCreationService;
import io.openbas.rest.payload.service.PayloadService;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.openbas.rest.payload;
package io.openbas.rest.payload.service;

import static io.openbas.helper.StreamHelper.fromIterable;
import static io.openbas.helper.StreamHelper.iterableToSet;
Expand All @@ -9,7 +9,6 @@
import io.openbas.database.repository.DocumentRepository;
import io.openbas.database.repository.PayloadRepository;
import io.openbas.database.repository.TagRepository;
import io.openbas.integrations.PayloadService;
import io.openbas.rest.payload.form.PayloadCreateInput;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
Expand Down
Loading

0 comments on commit 7b58130

Please sign in to comment.