diff --git a/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaContract.java b/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaContract.java index 975eff866e..f1418aa9ad 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaContract.java +++ b/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaContract.java @@ -103,7 +103,7 @@ private List abilityContracts(@NotNull final ContractConfig contractCo ContractAssetGroup assetGroupField = assetGroupField("assetgroups", "Asset groups", Multiple); ContractExpectations expectationsField = expectations(); - List abilities = this.injectorCalderaService.abilities(); + List abilities = this.injectorCalderaService.abilities().stream().filter(ability -> !ability.getTactic().equals("openbas")).toList(); // Build contracts return abilities.stream().map((ability -> { ContractDef builder = contractBuilder(); diff --git a/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaExecutor.java b/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaExecutor.java index fbaca353b7..cfa768e926 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaExecutor.java +++ b/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaExecutor.java @@ -9,6 +9,7 @@ import io.openbas.database.repository.InjectRepository; import io.openbas.execution.ExecutableInject; import io.openbas.execution.Injector; +import io.openbas.injectors.caldera.client.model.Ability; import io.openbas.injectors.caldera.client.model.Agent; import io.openbas.injectors.caldera.client.model.ExploitResult; import io.openbas.injectors.caldera.config.CalderaInjectorConfig; @@ -80,10 +81,17 @@ public ExecutionProcess process(@NotNull final Execution execution, @NotNull fin if (assets.isEmpty()) { execution.addTrace(traceError("Found 0 asset to execute the ability on (likely this inject does not have any target or the targeted asset is inactive and has been purged)")); } - String contract = inject.getInjectorContract().getId(); + String contract; if( inject.getInjectorContract().getPayload() != null ) { // This is a payload, need to create the ability on the fly - + List abilities = calderaService.abilities().stream().filter(ability -> ability.getName().equals(inject.getInjectorContract().getPayload().getId())).toList(); + if( !abilities.isEmpty() ) { + calderaService.deleteAbility(abilities.getFirst()); + } + Ability abilityToExecute = calderaService.createAbility(inject.getInjectorContract().getPayload()); + contract = abilityToExecute.getAbility_id(); + } else { + contract = inject.getInjectorContract().getId(); } assets.forEach((asset, aBoolean) -> { try { diff --git a/openbas-api/src/main/java/io/openbas/injectors/caldera/client/CalderaInjectorClient.java b/openbas-api/src/main/java/io/openbas/injectors/caldera/client/CalderaInjectorClient.java index 7c61e00818..73af6551ac 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/caldera/client/CalderaInjectorClient.java +++ b/openbas-api/src/main/java/io/openbas/injectors/caldera/client/CalderaInjectorClient.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.openbas.database.model.Endpoint; +import io.openbas.database.model.Injector; import io.openbas.injectors.caldera.client.model.Ability; import io.openbas.injectors.caldera.client.model.Agent; import io.openbas.injectors.caldera.client.model.Result; @@ -26,6 +27,7 @@ import org.springframework.util.StringUtils; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -53,6 +55,27 @@ public List abilities() { } } + public Ability createAbility(Map body) { + try { + String jsonResponse = this.post( + this.config.getRestApiV2Url() + ABILITIES_URI, + body + ); + return this.objectMapper.readValue(jsonResponse, new TypeReference<>() { + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void deleteAbility(Ability ability) { + try { + this.delete(this.config.getRestApiV2Url() + ABILITIES_URI + "/" + ability.getAbility_id()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + // -- AGENTS -- private final static String AGENT_URI = "/agents"; diff --git a/openbas-api/src/main/java/io/openbas/injectors/caldera/service/CalderaInjectorService.java b/openbas-api/src/main/java/io/openbas/injectors/caldera/service/CalderaInjectorService.java index dec83ea6a8..f70d8d3abe 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/caldera/service/CalderaInjectorService.java +++ b/openbas-api/src/main/java/io/openbas/injectors/caldera/service/CalderaInjectorService.java @@ -1,5 +1,7 @@ package io.openbas.injectors.caldera.service; +import com.fasterxml.jackson.core.type.TypeReference; +import io.openbas.database.model.*; import io.openbas.injectors.caldera.client.CalderaInjectorClient; import io.openbas.injectors.caldera.client.model.*; import io.openbas.injectors.caldera.model.Obfuscator; @@ -7,8 +9,10 @@ import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import lombok.extern.java.Log; +import org.hibernate.Hibernate; import org.springframework.stereotype.Service; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.*; @@ -42,6 +46,47 @@ public List obfuscators() { return this.client.obfuscators(); } + public void deleteAbility(Ability ability) { + this.client.deleteAbility(ability); + } + + public Ability createAbility(Payload payload) { + List> executors = new ArrayList<>(); + switch (payload.getType()) { + case "Command": + Command payloadCommand = (Command) Hibernate.unproxy(payload); + Arrays.stream(payloadCommand.getPlatforms()).forEach(platform -> { + Map executor = new HashMap<>(); + executor.put("platform", platform.equalsIgnoreCase("macos") ? "darwin" : platform.toLowerCase()); + executor.put("name", payloadCommand.getExecutor().equals("bash") ? "sh" : payloadCommand.getExecutor()); + executor.put("command", payloadCommand.getContent()); + executors.add(executor); + }); + break; + case "DnsResolution": + DnsResolution payloadDnsResolution = (DnsResolution) Hibernate.unproxy(payload); + Arrays.stream(payloadDnsResolution.getPlatforms()).forEach(platform -> { + Map executor = new HashMap<>(); + executor.put("platform", platform.equals(Endpoint.PLATFORM_TYPE.MacOS.name()) ? "darwin" : platform.toLowerCase()); + executor.put("name", platform.equals(Endpoint.PLATFORM_TYPE.Windows.name()) ? "cmd" : "sh"); + executor.put("command", "nslookup " + payloadDnsResolution.getHostname()); + executors.add(executor); + }); + break; + default: + throw new UnsupportedOperationException("Payload type " + payload.getType() + " is not supported"); + } + Map body = new HashMap<>(); + body.put("name", payload.getId()); + body.put("tactic", "openbas"); + body.put("technique_id", "openbas"); + body.put("technique_name", "openbas"); + body.put("executors", executors); + return this.client.createAbility(body); + } + + // -- AGENTS -- + public List agents() { try { return this.client.agents().stream().toList(); diff --git a/openbas-api/src/main/java/io/openbas/migration/V3_17__Payloads.java b/openbas-api/src/main/java/io/openbas/migration/V3_17__Payloads.java new file mode 100644 index 0000000000..e89cd8ec5c --- /dev/null +++ b/openbas-api/src/main/java/io/openbas/migration/V3_17__Payloads.java @@ -0,0 +1,23 @@ +package io.openbas.migration; + +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; +import org.springframework.stereotype.Component; + +import java.sql.Connection; +import java.sql.Statement; + +@Component +public class V3_17__Payloads extends BaseJavaMigration { + + @Override + public void migrate(Context context) throws Exception { + Connection connection = context.getConnection(); + Statement select = connection.createStatement(); + select.execute("ALTER TABLE payloads RENAME COLUMN network_traffic_ip TO network_traffic_ip_src;"); + select.execute("ALTER TABLE payloads ADD column network_traffic_ip_dst text;"); + select.execute("ALTER TABLE payloads ADD column network_traffic_port_src int;"); + select.execute("ALTER TABLE payloads ADD column network_traffic_port_dst int;"); + select.execute("ALTER TABLE payloads ADD column network_traffic_protocol varchar(255);"); + } +} diff --git a/openbas-api/src/main/java/io/openbas/rest/payload/PayloadApi.java b/openbas-api/src/main/java/io/openbas/rest/payload/PayloadApi.java index bbe1d58119..b2dd11fda4 100644 --- a/openbas-api/src/main/java/io/openbas/rest/payload/PayloadApi.java +++ b/openbas-api/src/main/java/io/openbas/rest/payload/PayloadApi.java @@ -1,9 +1,8 @@ package io.openbas.rest.payload; -import io.openbas.database.model.Command; -import io.openbas.database.model.Executable; -import io.openbas.database.model.Payload; +import io.openbas.database.model.*; import io.openbas.database.repository.AttackPatternRepository; +import io.openbas.database.repository.DocumentRepository; import io.openbas.database.repository.PayloadRepository; import io.openbas.database.repository.TagRepository; import io.openbas.rest.exception.ElementNotFoundException; @@ -15,6 +14,7 @@ import jakarta.transaction.Transactional; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; +import org.hibernate.Hibernate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -39,6 +39,7 @@ public class PayloadApi extends RestBehavior { private TagRepository tagRepository; private PayloadService payloadService; private AttackPatternRepository attackPatternRepository; + private DocumentRepository documentRepository; @Autowired public void setPayloadRepository(PayloadRepository payloadRepository) { @@ -60,6 +61,11 @@ public void setAttackPatternRepository(AttackPatternRepository attackPatternRepo this.attackPatternRepository = attackPatternRepository; } + @Autowired + public void setDocumentRepository(DocumentRepository documentRepository) { + this.documentRepository = documentRepository; + } + @GetMapping("/api/payloads") public Iterable payloads() { return payloadRepository.findAll(); @@ -98,9 +104,35 @@ public Payload createPayload(@Valid @RequestBody PayloadCreateInput input) { executablePayload.setUpdateAttributes(input); executablePayload.setAttackPatterns(fromIterable(attackPatternRepository.findAllById(input.getAttackPatternsIds()))); executablePayload.setTags(iterableToSet(tagRepository.findAllById(input.getTagIds()))); + executablePayload.setExecutableFile(documentRepository.findById(input.getExecutableFile()).orElseThrow()); executablePayload = payloadRepository.save(executablePayload); this.payloadService.updateInjectorContractsForPayload(executablePayload); return executablePayload; + case "FileDrop": + FileDrop fileDropPayload = new FileDrop(); + fileDropPayload.setUpdateAttributes(input); + fileDropPayload.setAttackPatterns(fromIterable(attackPatternRepository.findAllById(input.getAttackPatternsIds()))); + fileDropPayload.setTags(iterableToSet(tagRepository.findAllById(input.getTagIds()))); + fileDropPayload.setFileDropFile(documentRepository.findById(input.getExecutableFile()).orElseThrow()); + fileDropPayload = payloadRepository.save(fileDropPayload); + this.payloadService.updateInjectorContractsForPayload(fileDropPayload); + return fileDropPayload; + case "DnsResolution": + DnsResolution dnsResolutionPayload = new DnsResolution(); + dnsResolutionPayload.setUpdateAttributes(input); + dnsResolutionPayload.setAttackPatterns(fromIterable(attackPatternRepository.findAllById(input.getAttackPatternsIds()))); + dnsResolutionPayload.setTags(iterableToSet(tagRepository.findAllById(input.getTagIds()))); + dnsResolutionPayload = payloadRepository.save(dnsResolutionPayload); + this.payloadService.updateInjectorContractsForPayload(dnsResolutionPayload); + return dnsResolutionPayload; + case "NetworkTraffic": + NetworkTraffic networkTrafficPayload = new NetworkTraffic(); + networkTrafficPayload.setUpdateAttributes(input); + networkTrafficPayload.setAttackPatterns(fromIterable(attackPatternRepository.findAllById(input.getAttackPatternsIds()))); + networkTrafficPayload.setTags(iterableToSet(tagRepository.findAllById(input.getTagIds()))); + networkTrafficPayload = payloadRepository.save(networkTrafficPayload); + this.payloadService.updateInjectorContractsForPayload(networkTrafficPayload); + return networkTrafficPayload; default: throw new UnsupportedOperationException("Payload type " + input.getType() + " is not supported"); } @@ -113,13 +145,43 @@ public Payload updatePayload( @NotBlank @PathVariable final String payloadId, @Valid @RequestBody PayloadUpdateInput input) { Payload payload = this.payloadRepository.findById(payloadId).orElseThrow(ElementNotFoundException::new); - payload.setUpdateAttributes(input); payload.setAttackPatterns(fromIterable(attackPatternRepository.findAllById(input.getAttackPatternsIds()))); payload.setTags(iterableToSet(tagRepository.findAllById(input.getTagIds()))); payload.setUpdatedAt(Instant.now()); - payload = payloadRepository.save(payload); - this.payloadService.updateInjectorContractsForPayload(payload); - return payload; + switch( payload.getType() ) { + case "Command": + Command payloadCommand = (Command) Hibernate.unproxy(payload); + payloadCommand.setUpdateAttributes(input); + payloadCommand = payloadRepository.save(payloadCommand); + this.payloadService.updateInjectorContractsForPayload(payloadCommand); + return payloadCommand; + case "Executable": + Executable payloadExecutable = (Executable) Hibernate.unproxy(payload); + payloadExecutable.setUpdateAttributes(input); + payloadExecutable = payloadRepository.save(payloadExecutable); + this.payloadService.updateInjectorContractsForPayload(payloadExecutable); + return payloadExecutable; + case "FileDrop": + FileDrop payloadFileDrop = (FileDrop) Hibernate.unproxy(payload); + payloadFileDrop.setUpdateAttributes(input); + payloadFileDrop = payloadRepository.save(payloadFileDrop); + this.payloadService.updateInjectorContractsForPayload(payloadFileDrop); + return payloadFileDrop; + case "DnsResolution": + DnsResolution payloadDnsResolution = (DnsResolution) Hibernate.unproxy(payload); + payloadDnsResolution.setUpdateAttributes(input); + payloadDnsResolution = payloadRepository.save(payloadDnsResolution); + this.payloadService.updateInjectorContractsForPayload(payloadDnsResolution); + return payloadDnsResolution; + case "NetworkTraffic": + NetworkTraffic payloadNetworkTraffic = (NetworkTraffic) Hibernate.unproxy(payload); + payloadNetworkTraffic.setUpdateAttributes(input); + payloadNetworkTraffic = payloadRepository.save(payloadNetworkTraffic); + this.payloadService.updateInjectorContractsForPayload(payloadNetworkTraffic); + return payloadNetworkTraffic; + default: + throw new UnsupportedOperationException("Payload type " + payload.getType() + " is not supported"); + } } @Secured(ROLE_ADMIN) diff --git a/openbas-api/src/main/java/io/openbas/rest/payload/form/PayloadCreateInput.java b/openbas-api/src/main/java/io/openbas/rest/payload/form/PayloadCreateInput.java index 47404fb5bd..b694f12a19 100644 --- a/openbas-api/src/main/java/io/openbas/rest/payload/form/PayloadCreateInput.java +++ b/openbas-api/src/main/java/io/openbas/rest/payload/form/PayloadCreateInput.java @@ -35,6 +35,15 @@ public class PayloadCreateInput { @JsonProperty("command_content") private String content; + @JsonProperty("executable_file") + private String executableFile; + + @JsonProperty("file_drop_file") + private String fileDropFile; + + @JsonProperty("dns_resolution_hostname") + private String hostname; + @JsonProperty("payload_arguments") private List arguments; diff --git a/openbas-framework/src/main/java/io/openbas/executors/caldera/client/CalderaExecutorClient.java b/openbas-framework/src/main/java/io/openbas/executors/caldera/client/CalderaExecutorClient.java index 7009ee7b78..2e32cc41df 100644 --- a/openbas-framework/src/main/java/io/openbas/executors/caldera/client/CalderaExecutorClient.java +++ b/openbas-framework/src/main/java/io/openbas/executors/caldera/client/CalderaExecutorClient.java @@ -104,9 +104,9 @@ public Ability createSubprocessorAbility(Injector injector) { } Map body = new HashMap<>(); body.put("name", "caldera-subprocessor-" + injector.getName()); - body.put("tactic", "initial-access"); - body.put("technique_id", "T1133"); - body.put("technique_name", "External Remote Services"); + body.put("tactic", "openbas"); + body.put("technique_id", "openbas"); + body.put("technique_name", "openbas"); body.put("executors", executors); String jsonResponse = this.post( this.config.getRestApiV2Url() + ABILITIES_URI, @@ -146,9 +146,9 @@ public Ability createClearAbility(Injector injector) { } Map body = new HashMap<>(); body.put("name", "caldera-clear-" + injector.getName()); - body.put("tactic", "initial-access"); - body.put("technique_id", "T1133"); - body.put("technique_name", "External Remote Services"); + body.put("tactic", "openbas"); + body.put("technique_id", "openbas"); + body.put("technique_name", "openbas"); body.put("executors", executors); String jsonResponse = this.post( this.config.getRestApiV2Url() + ABILITIES_URI, diff --git a/openbas-front/src/admin/components/payloads/CreatePayload.js b/openbas-front/src/admin/components/payloads/CreatePayload.js index dfc3214063..6e570fd099 100644 --- a/openbas-front/src/admin/components/payloads/CreatePayload.js +++ b/openbas-front/src/admin/components/payloads/CreatePayload.js @@ -4,8 +4,8 @@ import { connect } from 'react-redux'; import * as R from 'ramda'; import { Fab, List, ListItemButton, ListItemIcon, ListItemText, Step, StepLabel, Stepper } from '@mui/material'; import { withStyles } from '@mui/styles'; -import { Add } from '@mui/icons-material'; -import { Console } from 'mdi-material-ui'; +import { Add, DnsOutlined } from '@mui/icons-material'; +import { ApplicationCogOutline, Console, FileImportOutline, LanConnect } from 'mdi-material-ui'; import { addPayload } from '../../../actions/Payload'; import PayloadForm from './PayloadForm'; import inject18n from '../../../components/i18n'; @@ -43,6 +43,7 @@ class CreatePayload extends Component { R.assoc('payload_platforms', R.pluck('id', data.payload_platforms)), R.assoc('payload_tags', R.pluck('id', data.payload_tags)), R.assoc('payload_attack_patterns', R.pluck('id', data.payload_attack_patterns)), + R.assoc('executable_file', data.executable_file?.id), )(data); return this.props .addPayload(inputValues) @@ -68,6 +69,42 @@ class CreatePayload extends Component { + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/openbas-front/src/admin/components/payloads/PayloadForm.js b/openbas-front/src/admin/components/payloads/PayloadForm.js index 346da019d4..bc0186500e 100644 --- a/openbas-front/src/admin/components/payloads/PayloadForm.js +++ b/openbas-front/src/admin/components/payloads/PayloadForm.js @@ -12,6 +12,7 @@ import TagField from '../../../components/TagField'; import PlatformField from '../../../components/PlatformField'; import OldSelectField from '../../../components/fields/OldSelectField'; import AttackPatternField from '../../../components/AttackPatternField'; +import DocumentField from '../../../components/DocumentField'; const useStyles = makeStyles(() => ({ tuple: { @@ -32,6 +33,9 @@ const PayloadForm = (props) => { case 'Command': requiredFields.push(...['command_executor', 'command_content']); break; + case 'Executable': + requiredFields.push(...['executable_file']); + break; default: // do nothing } @@ -114,6 +118,25 @@ const PayloadForm = (props) => { /> )} + {type === 'Executable' && ( + <> + + + )} + {type === 'DnsResolution' && ( + <> + + + )} {({ fields, meta }) => ( <> @@ -238,7 +261,7 @@ const PayloadForm = (props) => { {t('PowerShell')} - + {t('Command Prompt')} diff --git a/openbas-front/src/admin/components/payloads/PayloadPopover.js b/openbas-front/src/admin/components/payloads/PayloadPopover.js index cbd2275c98..759f2a6811 100644 --- a/openbas-front/src/admin/components/payloads/PayloadPopover.js +++ b/openbas-front/src/admin/components/payloads/PayloadPopover.js @@ -6,11 +6,11 @@ import { MoreVert } from '@mui/icons-material'; import { deletePayload, updatePayload } from '../../../actions/Payload'; import PayloadForm from './PayloadForm'; import { useFormatter } from '../../../components/i18n'; -import { attackPatternOptions, platformOptions, tagOptions } from '../../../utils/Option'; +import { attackPatternOptions, documentOptions, platformOptions, tagOptions } from '../../../utils/Option'; import Transition from '../../../components/common/Transition'; import Drawer from '../../../components/common/Drawer'; -const PayloadPopover = ({ payload, tagsMap, attackPatternsMap, killChainPhasesMap, onUpdate, onDelete }) => { +const PayloadPopover = ({ payload, documentsMap, tagsMap, attackPatternsMap, killChainPhasesMap, onUpdate, onDelete }) => { const [openDelete, setOpenDelete] = useState(false); const [openEdit, setOpenEdit] = useState(false); const [anchorEl, setAnchorEl] = useState(null); @@ -59,6 +59,7 @@ const PayloadPopover = ({ payload, tagsMap, attackPatternsMap, killChainPhasesMa const payloadAttackPatterns = attackPatternOptions(payload.payload_attack_patterns, attackPatternsMap, killChainPhasesMap); const payloadTags = tagOptions(payload.payload_tags, tagsMap); const payloadPlatforms = platformOptions(payload.payload_platforms); + const payloadFiles = documentOptions(payload.executable_file ? [payload.executable_file] : [], documentsMap); const initialValues = R.pipe( R.pick([ 'payload_name', @@ -67,12 +68,14 @@ const PayloadPopover = ({ payload, tagsMap, attackPatternsMap, killChainPhasesMa 'payload_cleanup_command', 'command_executor', 'command_content', + 'dns_resolution_hostname', 'payload_arguments', 'payload_prerequisites', ]), R.assoc('payload_platforms', payloadPlatforms), R.assoc('payload_attack_patterns', payloadAttackPatterns), R.assoc('payload_tags', payloadTags), + R.assoc('executable_file', R.head(payloadFiles)), )(payload); return ( <> diff --git a/openbas-front/src/admin/components/payloads/Payloads.js b/openbas-front/src/admin/components/payloads/Payloads.js index 799571e7a3..38636f18e1 100644 --- a/openbas-front/src/admin/components/payloads/Payloads.js +++ b/openbas-front/src/admin/components/payloads/Payloads.js @@ -2,7 +2,6 @@ import React, { useState } from 'react'; import { useDispatch } from 'react-redux'; import { Chip, List, ListItem, ListItemIcon, ListItemSecondaryAction, ListItemText } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { SubscriptionsOutlined } from '@mui/icons-material'; import { searchPayloads } from '../../../actions/Payload'; import CreatePayload from './CreatePayload'; import useDataLoader from '../../../utils/hooks/useDataLoader'; @@ -18,6 +17,8 @@ import { fetchTags } from '../../../actions/Tag'; import ItemTags from '../../../components/ItemTags'; import { fetchAttackPatterns } from '../../../actions/AttackPattern'; import PlatformIcon from '../../../components/PlatformIcon'; +import { fetchDocuments } from '../../../actions/Document'; +import PayloadIcon from '../../../components/PayloadIcon'; const useStyles = makeStyles(() => ({ itemHead: { @@ -82,12 +83,14 @@ const Payloads = () => { const classes = useStyles(); const dispatch = useDispatch(); const { t, nsdt } = useFormatter(); - const { tagsMap, attackPatternsMap, killChainPhasesMap } = useHelper((helper) => ({ + const { documentsMap, tagsMap, attackPatternsMap, killChainPhasesMap } = useHelper((helper) => ({ + documentsMap: helper.getDocumentsMap(), attackPatternsMap: helper.getAttackPatternsMap(), killChainPhasesMap: helper.getKillChainPhasesMap(), tagsMap: helper.getTagsMap(), })); useDataLoader(() => { + dispatch(fetchDocuments()); dispatch(fetchTags()); dispatch(fetchAttackPatterns()); dispatch(fetchKillChainPhases()); @@ -168,7 +171,7 @@ const Payloads = () => { divider={true} > - + { ({ + icon: { + paddingTop: 4, + display: 'inline-block', + }, + text: { + display: 'inline-block', + flexGrow: 1, + marginLeft: 10, + }, + autoCompleteIndicator: { + display: 'none', + }, +}); + +class DocumentField extends Component { + constructor(props) { + super(props); + this.state = { documentInput: '' }; + } + + componentDidMount() { + this.props.fetchDocuments(); + } + + render() { + const { t, name, documents, classes } = this.props; + const documentsOptions = R.map( + (n) => ({ + id: n.document_id, + label: n.document_name, + }), + documents, + ); + return ( +
+ ( + +
+ +
+
{option.label}
+
+ )} + classes={{ clearIndicator: classes.autoCompleteIndicator }} + /> +
+ ); + } +} + +const select = (state) => { + const helper = storeHelper(state); + return { + documents: helper.getDocuments(), + }; +}; + +export default R.compose( + connect(select, { fetchDocuments, addDocument }), + inject18n, + withStyles(styles), +)(DocumentField); diff --git a/openbas-front/src/components/PayloadIcon.tsx b/openbas-front/src/components/PayloadIcon.tsx new file mode 100644 index 0000000000..f9c3c08c19 --- /dev/null +++ b/openbas-front/src/components/PayloadIcon.tsx @@ -0,0 +1,38 @@ +import React, { FunctionComponent } from 'react'; +import { Tooltip } from '@mui/material'; +import { ApplicationCogOutline, Console, FileImportOutline, LanConnect } from 'mdi-material-ui'; +import { DnsOutlined, SubscriptionsOutlined } from '@mui/icons-material'; + +interface PayloadIconProps { + payloadType: string; + tooltip?: boolean; +} + +const renderIcon = (payloadType: string) => { + switch (payloadType) { + case 'Command': + return ; + case 'Executable': + return ; + case 'FileDrop': + return ; + case 'DnsResolution': + return ; + case 'NetworkTraffic': + return ; + default: + return ; + } +}; +const PayloadIcon: FunctionComponent = ({ payloadType, tooltip = false }) => { + if (tooltip) { + return ( + + {renderIcon(payloadType)} + + ); + } + return renderIcon(payloadType); +}; + +export default PayloadIcon; diff --git a/openbas-front/src/utils/Option.ts b/openbas-front/src/utils/Option.ts index 3572b70d1c..2e693f6bf6 100644 --- a/openbas-front/src/utils/Option.ts +++ b/openbas-front/src/utils/Option.ts @@ -1,6 +1,6 @@ import * as R from 'ramda'; import countriesJson from '../static/geo/countries.json'; -import type { AttackPattern, Exercise, KillChainPhase, Organization, Scenario, Tag } from './api-types'; +import type { AttackPattern, Exercise, KillChainPhase, Organization, Scenario, Tag, Document } from './api-types'; interface Countries { features: [{ @@ -20,6 +20,19 @@ export interface Option { color?: string; } +export const documentOptions = ( + document_ids: string[] | undefined, + documentsMap: Record, +) => (document_ids ?? []) + .map((documentId) => documentsMap[documentId]) + .filter((documentItem) => documentItem !== undefined) + .map( + (documentItem) => ({ + id: documentItem.document_id, + label: documentItem.document_name, + }) as Option, + ); + export const tagOptions = ( tag_ids: string[] | undefined, tagsMap: Record, diff --git a/openbas-model/src/main/java/io/openbas/database/model/DnsResolution.java b/openbas-model/src/main/java/io/openbas/database/model/DnsResolution.java index 5790a3cdb6..1ea11f903e 100644 --- a/openbas-model/src/main/java/io/openbas/database/model/DnsResolution.java +++ b/openbas-model/src/main/java/io/openbas/database/model/DnsResolution.java @@ -20,6 +20,9 @@ public class DnsResolution extends Payload { public static final String DNS_RESOLUTION_TYPE = "DnsResolution"; + @JsonProperty("payload_type") + private String type = DNS_RESOLUTION_TYPE; + @Queryable(filterable = true, sortable = true) @Column(name = "dns_resolution_hostname") @JsonProperty("dns_resolution_hostname") diff --git a/openbas-model/src/main/java/io/openbas/database/model/Executable.java b/openbas-model/src/main/java/io/openbas/database/model/Executable.java index af643d96a4..af52a1f63f 100644 --- a/openbas-model/src/main/java/io/openbas/database/model/Executable.java +++ b/openbas-model/src/main/java/io/openbas/database/model/Executable.java @@ -24,7 +24,7 @@ public class Executable extends Payload { @JoinColumn(name = "executable_file") @JsonSerialize(using = MonoIdDeserializer.class) @JsonProperty("executable_file") - private Document file; + private Document executableFile; public Executable() { diff --git a/openbas-model/src/main/java/io/openbas/database/model/FileDrop.java b/openbas-model/src/main/java/io/openbas/database/model/FileDrop.java index 4e26383978..5a4c3016bc 100644 --- a/openbas-model/src/main/java/io/openbas/database/model/FileDrop.java +++ b/openbas-model/src/main/java/io/openbas/database/model/FileDrop.java @@ -17,11 +17,14 @@ public class FileDrop extends Payload { public static final String FILE_DROP_TYPE = "FileDrop"; + @JsonProperty("payload_type") + private String type = FILE_DROP_TYPE; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "file_drop_file") @JsonSerialize(using = MonoIdDeserializer.class) @JsonProperty("file_drop_file") - private Document file; + private Document fileDropFile; public FileDrop() { diff --git a/openbas-model/src/main/java/io/openbas/database/model/NetworkTraffic.java b/openbas-model/src/main/java/io/openbas/database/model/NetworkTraffic.java index 33579d9635..4af2f0a64e 100644 --- a/openbas-model/src/main/java/io/openbas/database/model/NetworkTraffic.java +++ b/openbas-model/src/main/java/io/openbas/database/model/NetworkTraffic.java @@ -20,11 +20,38 @@ public class NetworkTraffic extends Payload { public static final String NETWORK_TRAFFIC_TYPE = "NetworkTraffic"; + @JsonProperty("payload_type") + private String type = NETWORK_TRAFFIC_TYPE; + + @Queryable(filterable = true, sortable = true) + @Column(name = "network_traffic_ip_src") + @JsonProperty("network_traffic_ip_src") + @NotNull + private String ipSrc; + + @Queryable(filterable = true, sortable = true) + @Column(name = "network_traffic_ip_dst") + @JsonProperty("network_traffic_ip_dst") + @NotNull + private String ipDst; + + @Queryable(filterable = true, sortable = true) + @Column(name = "network_traffic_port_src") + @JsonProperty("network_traffic_port_src") + @NotNull + private Integer portSrc; + + @Queryable(filterable = true, sortable = true) + @Column(name = "network_traffic_port_dst") + @JsonProperty("network_traffic_port_dst") + @NotNull + private Integer portDst; + @Queryable(filterable = true, sortable = true) - @Column(name = "network_traffic_ip") - @JsonProperty("network_traffic_ip") + @Column(name = "network_traffic_protocol") + @JsonProperty("network_traffic_protocol") @NotNull - private String ip; + private String protocol; public NetworkTraffic() {