Skip to content

Commit

Permalink
#52 Split MISP service in several files
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Aug 28, 2017
1 parent bfb741b commit bb6e4d0
Show file tree
Hide file tree
Showing 8 changed files with 694 additions and 610 deletions.
39 changes: 39 additions & 0 deletions thehive-misp/app/connectors/misp/MispConfig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package connectors.misp

import javax.inject.Inject

import scala.concurrent.duration.{ DurationInt, FiniteDuration }
import scala.util.Try

import play.api.Configuration

import services.CustomWSAPI

class MispConfig(val interval: FiniteDuration, val connections: Seq[MispConnection]) {

def this(configuration: Configuration, defaultCaseTemplate: Option[String], globalWS: CustomWSAPI) = this(
configuration.getOptional[FiniteDuration]("misp.interval").getOrElse(1.hour),

for {
cfg configuration.getOptional[Configuration]("misp").toSeq
mispWS = globalWS.withConfig(cfg)

defaultArtifactTags = cfg.getOptional[Seq[String]]("tags").getOrElse(Nil)
name cfg.subKeys

mispConnectionConfig Try(cfg.get[Configuration](name)).toOption.toSeq
url mispConnectionConfig.getOptional[String]("url")
key mispConnectionConfig.getOptional[String]("key")
instanceWS = mispWS.withConfig(mispConnectionConfig)
artifactTags = mispConnectionConfig.getOptional[Seq[String]]("tags").getOrElse(defaultArtifactTags)
caseTemplate = mispConnectionConfig.getOptional[String]("caseTemplate").orElse(defaultCaseTemplate)
} yield MispConnection(name, url, key, instanceWS, caseTemplate, artifactTags))

@Inject def this(configuration: Configuration, httpSrv: CustomWSAPI) =
this(
configuration,
configuration.getOptional[String]("misp.caseTemplate"),
httpSrv)

def getConnection(name: String): Option[MispConnection] = connections.find(_.name == name)
}
30 changes: 30 additions & 0 deletions thehive-misp/app/connectors/misp/MispConnection.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package connectors.misp

import play.api.Logger

import services.CustomWSAPI

case class MispConnection(
name: String,
baseUrl: String,
key: String,
ws: CustomWSAPI,
caseTemplate: Option[String],
artifactTags: Seq[String]) {

private[MispConnection] lazy val logger = Logger(getClass)

logger.info(
s"""Add MISP connection $name
|\turl: $baseUrl
|\tproxy: ${ws.proxy}
|\tcase template: ${caseTemplate.getOrElse("<not set>")}
|\tartifact tags: ${artifactTags.mkString}""".stripMargin)

private[misp] def apply(url: String) =
ws.url(s"$baseUrl/$url")
.withHttpHeaders(
"Authorization" key,
"Accept" "application/json")

}
205 changes: 205 additions & 0 deletions thehive-misp/app/connectors/misp/MispConverter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package connectors.misp

import play.api.libs.json.{ JsObject, JsString, Json }

trait MispConverter {
def convertAttribute(mispAttribute: MispAttribute): Seq[JsObject] = {
val dataType = toArtifact(mispAttribute.tpe)
val tags = Seq(s"MISP:type=${mispAttribute.tpe}", s"MISP:category=${mispAttribute.category}")
val fields = Json.obj(
"data" mispAttribute.value,
"dataType" dataType,
"message" mispAttribute.comment,
"startDate" mispAttribute.date,
"tags" tags)

val types = mispAttribute.tpe.split('|').toSeq
if (types.length > 1) {
val values = mispAttribute.value.split('|').toSeq
val typesValues = types.zipAll(values, "noType", "noValue")
val additionnalMessage = typesValues
.map {
case (t, v) s"$t: $v"
}
.mkString("\n")
typesValues.map {
case (tpe, value)
fields +
("dataType" JsString(toArtifact(tpe))) +
("data" JsString(value)) +
("message" JsString(mispAttribute.comment + "\n" + additionnalMessage))
}
}
else {
Seq(fields)
}
}

def fromArtifact(dataType: String, data: Option[String]): (String, String) = {
dataType match {
case "filename" "Payload delivery" "filename"
case "fqdn" "Network activity" "hostname"
case "url" "External analysis" "url"
case "user-agent" "Network activity" "user-agent"
case "domain" "Network activity" "domain"
case "ip" "Network activity" "ip-src"
case "mail_subject" "Payload delivery" "email-subject"
case "hash" data.fold(0)(_.length) match {
case 32 "Payload delivery" "md5"
case 40 "Payload delivery" "sha1"
case 64 "Payload delivery" "sha256"
case 56 "Payload delivery" "sha224"
case 71 "Payload delivery" "sha384"
case 128 "Payload delivery" "sha512"
case _ "Payload delivery" "other"
}
case "mail" "Payload delivery" "email-src"
case "registry" "Persistence mechanism" "regkey"
case "uri_path" "Network activity" "uri"
case "regexp" "Other" "other"
case "other" "Other" "other"
case "file" "Payload delivery" "malware-sample"
case _ "Other" "other"
}
}

def toArtifact(tpe: String): String = attribute2artifactLookup.getOrElse(tpe, "other")

private lazy val attribute2artifactLookup = Map(
"md5" "hash",
"sha1" "hash",
"sha256" "hash",
"filename" "filename",
"pdb" "other",
"filename|md5" "other",
"filename|sha1" "other",
"filename|sha256" "other",
"ip-src" "ip",
"ip-dst" "ip",
"hostname" "fqdn",
"domain" "domain",
"domain|ip" "other",
"email-src" "mail",
"email-dst" "mail",
"email-subject" "mail_subject",
"email-attachment" "other",
"float" "other",
"url" "url",
"http-method" "other",
"user-agent" "user-agent",
"regkey" "registry",
"regkey|value" "registry",
"AS" "other",
"snort" "other",
"pattern-in-file" "other",
"pattern-in-traffic" "other",
"pattern-in-memory" "other",
"yara" "other",
"sigma" "other",
"vulnerability" "other",
"attachment" "file",
"malware-sample" "file",
"link" "other",
"comment" "other",
"text" "other",
"hex" "other",
"other" "other",
"named" "other",
"mutex" "other",
"target-user" "other",
"target-email" "mail",
"target-machine" "fqdn",
"target-org" "other",
"target-location" "other",
"target-external" "other",
"btc" "other",
"iban" "other",
"bic" "other",
"bank-account-nr" "other",
"aba-rtn" "other",
"bin" "other",
"cc-number" "other",
"prtn" "other",
"threat-actor" "other",
"campaign-name" "other",
"campaign-id" "other",
"malware-type" "other",
"uri" "uri_path",
"authentihash" "other",
"ssdeep" "hash",
"imphash" "hash",
"pehash" "hash",
"impfuzzy" "hash",
"sha224" "hash",
"sha384" "hash",
"sha512" "hash",
"sha512/224" "hash",
"sha512/256" "hash",
"tlsh" "other",
"filename|authentihash" "other",
"filename|ssdeep" "other",
"filename|imphash" "other",
"filename|impfuzzy" "other",
"filename|pehash" "other",
"filename|sha224" "other",
"filename|sha384" "other",
"filename|sha512" "other",
"filename|sha512/224" "other",
"filename|sha512/256" "other",
"filename|tlsh" "other",
"windows-scheduled-task" "other",
"windows-service-name" "other",
"windows-service-displayname" "other",
"whois-registrant-email" "mail",
"whois-registrant-phone" "other",
"whois-registrant-name" "other",
"whois-registrar" "other",
"whois-creation-date" "other",
"x509-fingerprint-sha1" "other",
"dns-soa-email" "other",
"size-in-bytes" "other",
"counter" "other",
"datetime" "other",
"cpe" "other",
"port" "other",
"ip-dst|port" "other",
"ip-src|port" "other",
"hostname|port" "other",
"email-dst-display-name" "other",
"email-src-display-name" "other",
"email-header" "other",
"email-reply-to" "other",
"email-x-mailer" "other",
"email-mime-boundary" "other",
"email-thread-index" "other",
"email-message-id" "other",
"github-username" "other",
"github-repository" "other",
"github-organisation" "other",
"jabber-id" "other",
"twitter-id" "other",
"first-name" "other",
"middle-name" "other",
"last-name" "other",
"date-of-birth" "other",
"place-of-birth" "other",
"gender" "other",
"passport-number" "other",
"passport-country" "other",
"passport-expiration" "other",
"redress-number" "other",
"nationality" "other",
"visa-number" "other",
"issue-date-of-the-visa" "other",
"primary-residence" "other",
"country-of-residence" "other",
"special-service-request" "other",
"frequent-flyer-number" "other",
"travel-details" "other",
"payment-details" "other",
"place-port-of-original-embarkation" "other",
"place-port-of-clearance" "other",
"place-port-of-onward-foreign-destination" "other",
"passenger-name-record-locator-number" "other",
"mobile-application-id" "other")
}
8 changes: 5 additions & 3 deletions thehive-misp/app/connectors/misp/MispCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import org.elastic4play.{ NotFoundError, Timed }

@Singleton
class MispCtrl @Inject() (
mispSynchro: MispSynchro,
mispSrv: MispSrv,
mispExport: MispExport,
mispConfig: MispConfig,
caseSrv: CaseSrv,
authenticated: Authenticated,
Expand All @@ -47,13 +49,13 @@ class MispCtrl @Inject() (

@Timed
def syncAlerts: Action[AnyContent] = authenticated(Role.admin).async { implicit request
mispSrv.synchronize()
mispSynchro.synchronize()
.map { m Ok(Json.toJson(m)) }
}

@Timed
def syncAllAlerts: Action[AnyContent] = authenticated(Role.admin).async { implicit request
mispSrv.fullSynchronize()
mispSynchro.fullSynchronize()
.map { m Ok(Json.toJson(m)) }
}

Expand All @@ -67,7 +69,7 @@ class MispCtrl @Inject() (
def exportCase(mispName: String, caseId: String): Action[AnyContent] = authenticated(Role.write).async { implicit request
caseSrv
.get(caseId)
.flatMap { caze mispSrv.export(mispName, caze) }
.flatMap { caze mispExport.export(mispName, caze) }
.map {
case (_, exportedAttributes)
renderer.toMultiOutput(CREATED, exportedAttributes)
Expand Down
Loading

0 comments on commit bb6e4d0

Please sign in to comment.