From 8434d5104855f5ab2d7ed96d7e35d4533b6dfd47 Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Mon, 15 May 2017 10:52:07 +0200 Subject: [PATCH 1/6] #25 Add missing logo and favicons --- ui/Gruntfile.js | 13 ++++++++++--- ui/app/apple-touch-icon.png | Bin 1025 -> 0 bytes ui/app/favicon.png | Bin 1025 -> 0 bytes ui/app/images/brain-black.png | Bin 1025 -> 0 bytes ui/app/images/brain-white.png | Bin 1022 -> 0 bytes ui/bower.json | 2 +- ui/package.json | 2 +- version.sbt | 2 +- 8 files changed, 13 insertions(+), 6 deletions(-) delete mode 100644 ui/app/apple-touch-icon.png delete mode 100644 ui/app/favicon.png delete mode 100644 ui/app/images/brain-black.png delete mode 100644 ui/app/images/brain-white.png diff --git a/ui/Gruntfile.js b/ui/Gruntfile.js index 62cddcbef..eeb160d96 100644 --- a/ui/Gruntfile.js +++ b/ui/Gruntfile.js @@ -248,7 +248,7 @@ module.exports = function(grunt) { src: [ '<%= yeoman.dist %>/scripts/{,*/}*.js', '<%= yeoman.dist %>/styles/{,*/}*.css', - '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', + // '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', '<%= yeoman.dist %>/styles/fonts/*' ] } @@ -404,7 +404,7 @@ module.exports = function(grunt) { 'images/{,*/}*.{webp}', 'styles/fonts/{,*/}*.*' ] - }, { + },{ expand: true, cwd: '.tmp/images', dest: '<%= yeoman.dist %>/images', @@ -427,6 +427,12 @@ module.exports = function(grunt) { cwd: '<%= yeoman.app %>/styles', dest: '.tmp/styles/', src: '{,*/}*.css' + }, + images: { + expand: true, + cwd: '<%= yeoman.app %>/images', + dest: '<%= yeoman.dist %>/images', + src: '{,*/}*.{png,jpg,jpeg,gif,svg}' } }, @@ -439,7 +445,8 @@ module.exports = function(grunt) { 'copy:styles' ], dist: [ - 'copy:styles'/*, + 'copy:styles', + 'copy:images'/*, 'imagemin', 'svgmin'*/ ] diff --git a/ui/app/apple-touch-icon.png b/ui/app/apple-touch-icon.png deleted file mode 100644 index cfd4700dc08643cd67a53355dfb5c045a4a65c9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1025 zcmV+c1pfPpP)8%!+ zxrk4$$N+ZXek31hfjLwdIMf1jB;xn6tD}Kf&$)tl5d$sAw->nWEiePYQQ_vWv#9~> z!INTbzs1QmOEgdrH#Hf!lfhN(l_%JM0EzjKJgN zh_A*#Vd8P*9LzcEQfqI;b0+8fn>A{|JyU8{272S!w^rgst2BD4tv&o0&%G(7xA7+4 z$1C^&U*w#BS&2$YX$WI@2mj(5{Di$rG-GYLEj@mX{U4lO?jFGD$Z5qlhwhwnZ3c4A ziz%gxcq80>k1qrK0p7)AO6gI~d8)&kkE`9H@EyKFQoFc@WBg*yd9j>_Ug3J19G@P{2WG5|fwA~KFV1-bPp^9A zJXRjVYU;1$oS!vLYtH$(TCJycUAJmgY&vopd7j}~sac(ZJMnz24f$ty9ar06u3{vx zty-mVr*Z|`)TeW34R6AsB4=A;F`A9Q?>cky zt2(&f z>pfp!Uf_5~1Ce~HNStUAPdHm(&MFE|ftx5WrL|Z(5b@kpefO^&iypZ8h2j vgZ!vko%jn^;&)#YTwlbl1a=ljJMsSqAu5Bev$DQ*00000NkvXXu0mjf#D3j% diff --git a/ui/app/favicon.png b/ui/app/favicon.png deleted file mode 100644 index cfd4700dc08643cd67a53355dfb5c045a4a65c9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1025 zcmV+c1pfPpP)8%!+ zxrk4$$N+ZXek31hfjLwdIMf1jB;xn6tD}Kf&$)tl5d$sAw->nWEiePYQQ_vWv#9~> z!INTbzs1QmOEgdrH#Hf!lfhN(l_%JM0EzjKJgN zh_A*#Vd8P*9LzcEQfqI;b0+8fn>A{|JyU8{272S!w^rgst2BD4tv&o0&%G(7xA7+4 z$1C^&U*w#BS&2$YX$WI@2mj(5{Di$rG-GYLEj@mX{U4lO?jFGD$Z5qlhwhwnZ3c4A ziz%gxcq80>k1qrK0p7)AO6gI~d8)&kkE`9H@EyKFQoFc@WBg*yd9j>_Ug3J19G@P{2WG5|fwA~KFV1-bPp^9A zJXRjVYU;1$oS!vLYtH$(TCJycUAJmgY&vopd7j}~sac(ZJMnz24f$ty9ar06u3{vx zty-mVr*Z|`)TeW34R6AsB4=A;F`A9Q?>cky zt2(&f z>pfp!Uf_5~1Ce~HNStUAPdHm(&MFE|ftx5WrL|Z(5b@kpefO^&iypZ8h2j vgZ!vko%jn^;&)#YTwlbl1a=ljJMsSqAu5Bev$DQ*00000NkvXXu0mjf#D3j% diff --git a/ui/app/images/brain-black.png b/ui/app/images/brain-black.png deleted file mode 100644 index cfd4700dc08643cd67a53355dfb5c045a4a65c9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1025 zcmV+c1pfPpP)8%!+ zxrk4$$N+ZXek31hfjLwdIMf1jB;xn6tD}Kf&$)tl5d$sAw->nWEiePYQQ_vWv#9~> z!INTbzs1QmOEgdrH#Hf!lfhN(l_%JM0EzjKJgN zh_A*#Vd8P*9LzcEQfqI;b0+8fn>A{|JyU8{272S!w^rgst2BD4tv&o0&%G(7xA7+4 z$1C^&U*w#BS&2$YX$WI@2mj(5{Di$rG-GYLEj@mX{U4lO?jFGD$Z5qlhwhwnZ3c4A ziz%gxcq80>k1qrK0p7)AO6gI~d8)&kkE`9H@EyKFQoFc@WBg*yd9j>_Ug3J19G@P{2WG5|fwA~KFV1-bPp^9A zJXRjVYU;1$oS!vLYtH$(TCJycUAJmgY&vopd7j}~sac(ZJMnz24f$ty9ar06u3{vx zty-mVr*Z|`)TeW34R6AsB4=A;F`A9Q?>cky zt2(&f z>pfp!Uf_5~1Ce~HNStUAPdHm(&MFE|ftx5WrL|Z(5b@kpefO^&iypZ8h2j vgZ!vko%jn^;&)#YTwlbl1a=ljJMsSqAu5Bev$DQ*00000NkvXXu0mjf#D3j% diff --git a/ui/app/images/brain-white.png b/ui/app/images/brain-white.png deleted file mode 100644 index 42cb34f7b418d8bbbc2fa86c471b208026ad7a35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1022 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5m^kh={g8AI%&+V01C2~c>21sKj4t%ljE2%{kQ?J@*ObwSf{xQBt z|De^6^a(8;5^F+aqCC8pELgN4Y`Rj1oB3SVi+5c+y8VP=N{Wj$GJ;v&rf?iddK-|f zXXd;>;K=@i8C#Tg*2%m(`}N;RQQP;Q=EBG1`*!`pL}mGab}k`@D^& zwf~&-(I8ngJ$=$T74Ll~=B-++t*Q3Y`sB8n`T3uN_nnIWsrrALVeW~4ebCl zwpGC*t2ARI@@3+`@bpz*76=cTkTj`iU0U)a`_I`=p8uKgaL<{i%ht=EE0|>eDcWk2 zIkV@wWigQlxy~2HE$`qI-}8@my1b7ZL$+8~rcD2=>a*$$);5QJ9M6hb+EV%3j(fv-8?P|Wrvj6g-<(;* z_44F$>`4jl&JA}fQmXY- zGYT$M**a}okRPQ9~spMLq;u%Fj;)~_lr-1+zH z7Ri{6w=??#%Z^E=+LYbO3qSDeTZ6^*f~|St8}~k}vc3KyLwB;{l_m4QL%owxT;H00)|WTsW()}YcDngrA!39=zLKdq!Zu_%?Hyu4g5GcUV1Ik6yB XFTW^#_B$IXpdtoOS3j3^P6 Date: Mon, 15 May 2017 10:17:06 +0200 Subject: [PATCH 2/6] #20 add missing files for packages --- conf/application.sample | 82 ++++++++++++++++++++++++++++++++++++++ package/etc_default_cortex | 17 ++++++++ 2 files changed, 99 insertions(+) create mode 100644 conf/application.sample create mode 100644 package/etc_default_cortex diff --git a/conf/application.sample b/conf/application.sample new file mode 100644 index 000000000..bd499d47b --- /dev/null +++ b/conf/application.sample @@ -0,0 +1,82 @@ +# Secret key +# ~~~~~ +# The secret key is used to secure cryptographics functions. +# If you deploy your application to several instances be sure to use the same key! +#play.crypto.secret="***changeme***" + +analyzer { + path = "path/to/Cortex-Analyzers/analyzers" + config { + global { + proxy { + #http="http://PROXY_ADDRESS:PORT", + #https="http://PROXY_ADDRESS:PORT" + } + } + CIRCLPassiveDNS { + #user= "..." + #password= "..." + } + CIRCLPassiveSSL { + #user= "..." + #password= "..." + } + DNSDB { + #server="https://api.dnsdb.info" + #key="..." + } + DomainTools { + #username="..." + #key="..." + } + GoogleSafebrowsing { + #key = "..." + } + Hippocampe { + #url="..." + } + JoeSandbox { + #url = "..." + #apikey = "..." + } + Nessus { + #url ="..." + #login="..." + #password="..." + #policy="..." + #ca_bundle="..." + #allowed_network="..." + } + OTXQuery { + #key="..." + } + PassiveTotal { + #key="..." + #username="..." + } + PhishingInitiative { + #key="..." + } + PhishTank { + #key="..." + } + Virusshare { + #path = "..." + } + VirusTotal { + #key="..." + } + Yara { + #rules=["..."] + } + } + + fork-join-executor { + # Min number of threads available for analyze + parallelism-min = 2 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 2.0 + # Max number of threads available for analyze + parallelism-max = 4 + } +} \ No newline at end of file diff --git a/package/etc_default_cortex b/package/etc_default_cortex new file mode 100644 index 000000000..b965c6d21 --- /dev/null +++ b/package/etc_default_cortex @@ -0,0 +1,17 @@ +# ##################################### +# ##### Environment Configuration ##### +# ##################################### + +# *WARNING* This file is not read by if you are using systemd + +# This file gets sourced before the actual startscript +# gets executed. You can use this file to provide +# environment variables + +# Define if Cortex service is enabled (no by default) +# ----------------- +ENABLED=no + +# Setting DAEMON_ARGS +# pidfile is disabled (/dev/null) has it is handle by system loader (upstart/sysVinit) +#DAEMON_ARGS=-Dconfig.file=/etc/cortex/cortex.conf -Dlogger.file=/etc/cortex/logback.xml -Dpidfile.path=/dev/null From 9f8f1eb70a51fffc0962cb8c4aed16cdebc72d3d Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 16 May 2017 13:37:51 +0200 Subject: [PATCH 3/6] Fix MISP modules integration Enable MISP modules in docker package Send config to MISP modules Catch exception in MISP modules loader Update documentation --- app/models/JsonFormat.scala | 3 +- app/models/MispModule.scala | 4 +- app/services/MispSrv.scala | 125 ++++++++++++++++++------------ build.sbt | 23 ++++-- conf/application.sample | 85 ++++++++++++++++++-- conf/reference.conf | 8 +- contrib/misp-modules-loader.py | 52 ++++++++----- docs/README.md | 1 + docs/installation/docker-guide.md | 1 + docs/misp.md | 56 +++++++++++++ package/docker/entrypoint | 8 ++ 11 files changed, 278 insertions(+), 88 deletions(-) create mode 100644 docs/misp.md diff --git a/app/models/JsonFormat.scala b/app/models/JsonFormat.scala index 561544d3b..4c08752ff 100644 --- a/app/models/JsonFormat.scala +++ b/app/models/JsonFormat.scala @@ -18,7 +18,8 @@ object JsonFormat { implicit val fileArtifactWrites: OWrites[FileArtifact] = OWrites[FileArtifact](fileArtifact ⇒ Json.obj( "attributes" → fileArtifact.attributes)) - implicit val dataArtifactWrites: OWrites[DataArtifact] = Json.writes[DataArtifact] + implicit val dataArtifactWrites: OWrites[DataArtifact] = OWrites[DataArtifact](artifact ⇒ + artifact.attributes + ("data" → JsString(artifact.data))) implicit val dataActifactReads: Reads[DataArtifact] = Json.reads[DataArtifact] val artifactWrites: OWrites[Artifact] = OWrites[Artifact] { diff --git a/app/models/MispModule.scala b/app/models/MispModule.scala index c5c08f065..3d01ed72c 100644 --- a/app/models/MispModule.scala +++ b/app/models/MispModule.scala @@ -1,5 +1,7 @@ package models +import play.api.libs.json.JsObject + case class MispModule( name: String, version: String, @@ -7,7 +9,7 @@ case class MispModule( author: String, dataTypeList: Seq[String], inputAttributes: Seq[String], - config: Seq[String], + config: JsObject, loaderCommand: String) extends Analyzer { val license = "AGPL-3.0" val url = "https://github.com/MISP/misp-modules" diff --git a/app/services/MispSrv.scala b/app/services/MispSrv.scala index c75ee7c23..ff232bb35 100644 --- a/app/services/MispSrv.scala +++ b/app/services/MispSrv.scala @@ -1,12 +1,13 @@ package services import java.io.{ ByteArrayInputStream, FileInputStream, InputStream, SequenceInputStream } -import javax.inject.Inject +import javax.inject.{ Inject, Singleton } import akka.actor.ActorSystem import models.JsonFormat._ import models._ import org.apache.commons.codec.binary.{ Base64, Base64InputStream } +import util.JsonConfig import play.api.libs.json.{ Json, _ } import play.api.{ Configuration, Logger } @@ -15,8 +16,11 @@ import scala.concurrent.{ ExecutionContext, Future } import scala.sys.process._ import scala.util.{ Failure, Success, Try } +@Singleton class MispSrv( - loaderCommandOption: Option[String], + mispModulesEnabled: Boolean, + loaderCommand: String, + mispModuleConfig: JsObject, externalAnalyzerSrv: ExternalAnalyzerSrv, jobSrv: JobSrv, akkaSystem: ActorSystem) { @@ -26,7 +30,9 @@ class MispSrv( externalAnalyzerSrv: ExternalAnalyzerSrv, jobSrv: JobSrv, akkaSystem: ActorSystem) = this( - configuration.getString("misp.modules.loader"), + configuration.getBoolean("misp.modules.enabled").getOrElse(false), + configuration.getString("misp.modules.loader").get, + JsonConfig.configWrites.writes(configuration.getConfig("misp.modules.config").getOrElse(Configuration.empty)), externalAnalyzerSrv, jobSrv, akkaSystem) @@ -34,25 +40,27 @@ class MispSrv( private[MispSrv] lazy val logger = Logger(getClass) private[MispSrv] lazy val analyzeExecutionContext: ExecutionContext = akkaSystem.dispatchers.lookup("analyzer") - lazy val list: Seq[MispModule] = - loaderCommandOption.fold(Seq.empty[MispModule]) { loaderCommand ⇒ - Json.parse(s"$loaderCommand --list".!!) - .as[Seq[String]] - .map { moduleName ⇒ - moduleName → (for { - moduleInfo ← Try(Json.parse(s"$loaderCommand --info $moduleName".!!)) - module ← Try(moduleInfo.as[MispModule](reads(loaderCommand))) - } yield module) - } - .flatMap { - case (moduleName, Failure(error)) ⇒ - logger.warn(s"Load MISP module $moduleName fails", error) - Nil - case (_, Success(module)) ⇒ - logger.info(s"Register MISP module ${module.name} ${module.version}") - Seq(module) - } - } + logger.info(s"MISP modules is ${if (mispModulesEnabled) "enabled" else "disabled"}, loader is $loaderCommand") + + lazy val list: Seq[MispModule] = if (mispModulesEnabled) { + Json.parse(s"$loaderCommand --list".!!) + .as[Seq[String]] + .map { moduleName ⇒ + moduleName → (for { + moduleInfo ← Try(Json.parse(s"$loaderCommand --info $moduleName".!!)) + module ← Try(moduleInfo.as[MispModule](reads(loaderCommand, mispModuleConfig))) + } yield module) + } + .flatMap { + case (moduleName, Failure(error)) ⇒ + logger.warn(s"Load MISP module $moduleName fails", error) + Nil + case (_, Success(module)) ⇒ + logger.info(s"Register MISP module ${module.name} ${module.version}") + Seq(module) + } + } + else Nil def get(moduleName: String): Option[MispModule] = list.find(_.name == moduleName) @@ -89,30 +97,31 @@ class MispSrv( } def query(module: String, mispType: String, data: String)(implicit ec: ExecutionContext): Future[JsObject] = { - loaderCommandOption - .flatMap { loaderCommand ⇒ - val artifact = toArtifact(mispType, data) - get(module) - .map { mispModule ⇒ - val mispReport = Future { - val input = Json.obj(mispType → data) - val output = (s"$loaderCommand --run $module" #< input.toString).!! - Json.parse(output).as[JsObject] - } - jobSrv.create(mispModule, artifact, mispReport.map(toReport)) - mispReport - + val artifact = toArtifact(mispType, data) + val mispModule = if (mispModulesEnabled) { + get(module) + .map { mispModule ⇒ + val mispReport = Future { + val input = Json.obj("config" → mispModule.config, mispType → data) + val output = (s"$loaderCommand --run $module" #< input.toString).!! + Json.parse(output).as[JsObject] } - .orElse { - externalAnalyzerSrv - .get(module) - .map { analyzer ⇒ - externalAnalyzerSrv.analyze(analyzer, artifact) - .map { report ⇒ toMispOutput(report) } - } + jobSrv.create(mispModule, artifact, mispReport.map(toReport)) + mispReport + + } + } + else None + mispModule + .orElse { + externalAnalyzerSrv + .get(module) + .map { analyzer ⇒ + externalAnalyzerSrv.analyze(analyzer, artifact) + .map { report ⇒ toMispOutput(report) } } } - .getOrElse(Future.failed(new Exception(s"Module $module not found"))) + .getOrElse(Future.failed(new Exception(s"Module $module not found"))) // TODO add appropriate exception } def analyze(module: MispModule, artifact: Artifact): Future[Report] = { @@ -121,10 +130,13 @@ class MispSrv( val input = artifact match { case DataArtifact(data, _) ⇒ - stringStream(Json.obj(dataType2mispType(artifact.dataType).head → data).toString) + val mispType = dataType2mispType(artifact.dataType) + .filter(module.inputAttributes.contains) + .head + stringStream((Json.obj("config" → module.config) + (mispType → JsString(data))).toString) case FileArtifact(data, _) ⇒ new SequenceInputStream(Iterator( - stringStream("""{"attachment":""""), + stringStream(Json.obj("config" → module.config).toString.replaceFirst("}$", ""","attachment":"""")), new Base64InputStream(new FileInputStream(data), true), stringStream("\"}")).asJavaEnumeration) } @@ -207,15 +219,26 @@ class MispSrv( else mispTypes } - private def reads(loaderCommand: String): Reads[MispModule] = + private def reads(loaderCommand: String, mispModuleConfig: JsObject): Reads[MispModule] = for { name ← (__ \ "name").read[String] - version ← (__ \ "meta" \ "version").read[String] - description ← (__ \ "meta" \ "description").read[String] - author ← (__ \ "meta" \ "author").read[String] - config ← (__ \ "meta" \ "config").read[Seq[String]] + version ← (__ \ "moduleinfo" \ "version").read[String] + description ← (__ \ "moduleinfo" \ "description").read[String] + author ← (__ \ "moduleinfo" \ "author").read[String] + config = (mispModuleConfig \ name).asOpt[JsObject].getOrElse(JsObject(Nil)) + requiredConfig ← (__ \ "config").read[Set[String]] + missingConfig = requiredConfig -- config.keys + _ ← if (missingConfig.nonEmpty) { + val message = s"MISP module $name is disabled because the following configuration " + + s"item${if (missingConfig.size > 1) "s are" else " is"} missing: ${missingConfig.mkString(", ")}" + logger.warn(message) + Reads[Unit](_ ⇒ JsError(message)) + } + else { + Reads[Unit](_ ⇒ JsSuccess(())) + } input ← (__ \ "mispattributes" \ "input").read[Seq[String]] - dataTypes = input.map(mispType2dataType) + dataTypes = input.map(mispType2dataType).distinct } yield MispModule(name, version, description, author, dataTypes, input, config, loaderCommand) private val typeLookup = Map( diff --git a/build.sbt b/build.sbt index 75bf94c82..2c82422c4 100644 --- a/build.sbt +++ b/build.sbt @@ -43,14 +43,21 @@ mappings in Universal ~= { file("package/cortex.service") -> "package/cortex.service", file("package/cortex.conf") -> "package/cortex.conf", file("package/cortex") -> "package/cortex", - file("package/logback.xml") -> "conf/logback.xml" + file("package/logback.xml") -> "conf/logback.xml", + file("contrib/misp-modules-loader.py") -> "contrib/misp-modules-loader.py" ) } // Package // -maintainer := "Thomas Franco val mappings = pm.mappings.filterNot { @@ -62,7 +69,7 @@ linuxPackageMappings ~= { _.map { pm => file("package/cortex.conf") -> "/etc/init/cortex.conf", file("package/cortex") -> "/etc/init.d/cortex", file("conf/application.sample") -> "/etc/cortex/application.conf", - file("conf/logback.xml") -> "/etc/cortex/logback.xml" + file("package/logback.xml") -> "/etc/cortex/logback.xml" ).withConfig() } @@ -125,7 +132,11 @@ dockerCommands ~= { dc => "apt-get install -y --no-install-recommends python-pip python2.7-dev ssdeep libfuzzy-dev libfuzzy2 libimage-exiftool-perl libmagic1 build-essential git && " + "cd /opt && " + "git clone https://github.com/CERT-BDF/Cortex-Analyzers.git && " + - "pip install $(sort -u Cortex-Analyzers/analyzers/*/requirements.txt)"), + "pip install $(sort -u Cortex-Analyzers/analyzers/*/requirements.txt) && " + + "apt-get install -y --no-install-recommends python3-setuptools python3-dev zlib1g-dev libxslt1-dev libxml2-dev libpq5 libjpeg-dev && git clone https://github.com/MISP/misp-modules.git && " + + "easy_install3 pip && " + + "(cd misp-modules && pip3 install -I -r REQUIREMENTS && pip3 install -I .) && " + + "rm -rf misp_modules /var/lib/apt/lists/*"), Cmd("ADD", "var", "/var"), Cmd("ADD", "etc", "/etc"), ExecCmd("RUN", "chown", "-R", "daemon:daemon", "/var/log/cortex")) ++ diff --git a/conf/application.sample b/conf/application.sample index bd499d47b..149437e4a 100644 --- a/conf/application.sample +++ b/conf/application.sample @@ -7,12 +7,6 @@ analyzer { path = "path/to/Cortex-Analyzers/analyzers" config { - global { - proxy { - #http="http://PROXY_ADDRESS:PORT", - #https="http://PROXY_ADDRESS:PORT" - } - } CIRCLPassiveDNS { #user= "..." #password= "..." @@ -79,4 +73,81 @@ analyzer { # Max number of threads available for analyze parallelism-max = 4 } -} \ No newline at end of file +} + +misp.modules { + enabled = true + + config { + shodan { + #apikey = "" + } + eupi { + #apikey = "" + #url = "" + } + passivetotal { + #username = "" + #api_key = "" + } + dns { + #nameserver = "" + } + whois { + #server = "" + #port = "" + } + sourcecache { + #archivepath = "" + } + geoip_country { + } + circl_passivessl { + #username = "" + #password = "" + } + iprep { + #apikey = "" + } + countrycode { + } + cve { + } + virustotal { + #apikey = "" + #event_limit = "" + } + ipasn { + #host = "" + #port = "" + #db = "" + } + circl_passivedns { + #username = "" + #password = "" + } + vmray_submit { + #apikey = "" + #url = "" + #shareable = "" + #do_not_reanalyze = "" + #do_not_include_vmrayjobids = "" + } + wiki { + } + domaintools { + #username = "" + #api_key = "" + } + reversedns { + #nameserver = "" + } + threatminer { + } + asn_history { + #host = "" + #port = "" + #db = "" + } + } +} diff --git a/conf/reference.conf b/conf/reference.conf index 8484a2429..229d9624d 100644 --- a/conf/reference.conf +++ b/conf/reference.conf @@ -1,6 +1,12 @@ # handler for errors (transform exception to related http status code play.http.errorHandler = services.ErrorHandler +# MISP modules loader location +misp.modules { + enabled = false + loader = ${play.server.dir}/"contrib/misp-modules-loader.py" +} + analyzer { # Directory that holds analyzers path = analyzers @@ -8,7 +14,7 @@ analyzer { config { dummy = dummy } - + fork-join-executor { # Min number of threads available for analyze parallelism-min = 2 diff --git a/contrib/misp-modules-loader.py b/contrib/misp-modules-loader.py index c265aaaf8..be5d1fea6 100755 --- a/contrib/misp-modules-loader.py +++ b/contrib/misp-modules-loader.py @@ -17,51 +17,61 @@ """ -def run(argv): +def usage(): + print(__file__ + " --list") + print(__file__ + " --info ") + print(__file__ + " --run ") + +def run(argv): mhandlers, modules = misp_modules.load_package_modules() try: - opts, args = getopt.getopt(argv, 'lh:i:r:', ["list", "help", "info=","run="]) + opts, args = getopt.getopt(argv, 'lh:i:r:', ["list", "help", "info=", "run="]) except getopt.GetoptError as err: - print(__file__ + " --info ") - print(__file__ + " --run ") + usage() print(str(err)) sys.exit(2) - module = None - path = None - for opt,arg in opts: + for opt, arg in opts: # TODO: check if module exist else exit if opt in ('-h', '--help'): - print(__file__ + " --info ") - print(__file__ + " --run ") + usage() sys.exit() elif opt in ('-l', '--list'): - modules = [m for m in modules if mhandlers['type:' + m ] == "expansion"] + modules = [m for m in modules if mhandlers['type:' + m] == "expansion"] print(json.dumps(modules)) sys.exit(0) elif opt in ('-r', '--run'): - module = arg + module_name = arg + try: data = json.load(sys.stdin) - print(json.dumps(mhandlers[module].handler(json.dumps(data)))) - sys.exit(0) - - elif opt in ('-i','--info'): - module = arg - - print(json.dumps({'name': module, 'mispattributes': mhandlers[module].mispattributes, - 'moduleinfo':mhandlers[module].moduleinfo})) + print(json.dumps(mhandlers[module_name].handler(json.dumps(data)))) + except: + error = {'error': sys.exc_info()[1].args[0]} + print(json.dumps(error)) + sys.exit(0) + elif opt in ('-i', '--info'): + module_name = arg + try: + config = mhandlers[module_name].moduleconfig + except AttributeError: + config = [] + print(json.dumps({ + 'name': module_name, + 'mispattributes': mhandlers[module_name].mispattributes, + 'moduleinfo': mhandlers[module_name].moduleinfo, + 'config': config + })) if __name__ == '__main__': if len(sys.argv[1:]) > 0: run(sys.argv[1:]) else: - print(__file__ + " --info ") - print(__file__ + " --run ") + usage() sys.exit(2) diff --git a/docs/README.md b/docs/README.md index f8eb06819..6a6eb8c27 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,4 +28,5 @@ Once you have installed Cortex, you need to [install the analyzers](installation - [How to create an analyzer](api/how-to-create-an-analyzer.md) ## Other +- [MISP integration](misp.md) - [FAQ](FAQ.md) diff --git a/docs/installation/docker-guide.md b/docs/installation/docker-guide.md index 13696c543..1cbd71fa8 100644 --- a/docs/installation/docker-guide.md +++ b/docs/installation/docker-guide.md @@ -46,5 +46,6 @@ Docker image accepts more options: - --no-config-secret : do not add random secret to configuration - --secret : secret to secure sessions - --analyzer-path : where analyzers are located + - --no-misp-modules : disabled MISP modules diff --git a/docs/misp.md b/docs/misp.md new file mode 100644 index 000000000..cee49840d --- /dev/null +++ b/docs/misp.md @@ -0,0 +1,56 @@ +# MISP integration + +## Invoke MISP modules in Cortex + +Since version 1.1.1, Cortex can analyze observable using +[MISP expansion modules](https://github.com/MISP/misp-modules#expansion-modules). + +Follow [MISP documentation](https://github.com/MISP/misp-modules#how-to-install-and-start-misp-modules) to install MISP +modules. MISP modules service doesn't need to be started. Modules must be present in the same host than Cortex. +``` +sudo apt-get install python3-dev python3-pip libpq5 libjpeg-dev +cd /usr/local/src/ +sudo git clone https://github.com/MISP/misp-modules.git +cd misp-modules +sudo pip3 install -I -r REQUIREMENTS +sudo pip3 install -I . +``` + +Integration with MISP modules can then be enabled by adding the line `misp.modules.enabled = true` in +Cortex `application.conf`. + +Most MISP modules require configuration. Settings must be placed in misp.modules.config key. If required some +configuration is missing, MISP module is not loaded. + + +``` +misp.modules { + enabled = true + + config { + shodan { + apikey = "" + } + dns { + nameserver = "127.0.0.1" + } + } +} +``` +Cortex uses Python wrapper to run MISP modules. It is located in `contrib` folder. Cortex should be able to locate it +automatically. You can force its location in configuraton under settings: +``` +misp.modules.loader = /path/to/misp-modules-loader.py" +``` + +## Invoke Cortex in MISP + +Cortex can be connected to a MISP instance. Under `Server settings` of MISP `Administration` menu, go to `Plugin +settings` and in Cortex section: + - set `Plugin.Cortex_services_enable` to `true` + - set `Plugin.Cortex_services_url` to `http://127.0.0.1` (replace 127.0.0.1 by Cortex IP address) + - set `Plugin.Plugin.Cortex_services_port` to `9000` (replace 9000 by Cortex port) + +Then Cortex analyzer list should appear in Cortex section. They must be enabled before being available to MISP users. + + \ No newline at end of file diff --git a/package/docker/entrypoint b/package/docker/entrypoint index 08f2fd167..fd2e5a721 100755 --- a/package/docker/entrypoint +++ b/package/docker/entrypoint @@ -4,6 +4,7 @@ CONFIG_SECRET=1 CONFIG=1 CONFIG_FILE=/etc/cortex/application.conf ANALYZER_PATH=/opt/Cortex-Analyzers/analyzers +MISP_MODULE=1 function usage { cat <<- _EOF_ @@ -12,6 +13,7 @@ function usage { --no-config-secret | do not add random secret to configuration --secret | secret to secure sessions --analyzer-path | where analyzers are located + --no-misp-modules | disable MISP modules _EOF_ exit 1 } @@ -24,6 +26,7 @@ do "--no-config-secret") CONFIG_SECRET=0;; "--secret") shift; SECRET=$1;; "--analyzer-path") shift; ANALYZER_PATH=$1;; + "--no-misp-modules") shift; MISP_MODULE=0;; "--") STOP=1;; *) usage esac @@ -45,6 +48,11 @@ then echo analyzer.path=\"$ANALYZER_PATH\" >> $CONFIG_FILE + if test $MISP_MODULE = 1 + then + echo 'misp.modules.enabled = true' >> $CONFIG_FILE + fi + echo 'include file("/etc/cortex/application.conf")' >> $CONFIG_FILE fi From b9924382d901a260dd7bc093f8917b00c06c4411 Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 16 May 2017 13:37:51 +0200 Subject: [PATCH 4/6] #21 Fix MISP modules integration Enable MISP modules in docker package Send config to MISP modules Catch exception in MISP modules loader Update documentation --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 2c82422c4..b3eff5f60 100644 --- a/build.sbt +++ b/build.sbt @@ -136,7 +136,7 @@ dockerCommands ~= { dc => "apt-get install -y --no-install-recommends python3-setuptools python3-dev zlib1g-dev libxslt1-dev libxml2-dev libpq5 libjpeg-dev && git clone https://github.com/MISP/misp-modules.git && " + "easy_install3 pip && " + "(cd misp-modules && pip3 install -I -r REQUIREMENTS && pip3 install -I .) && " + - "rm -rf misp_modules /var/lib/apt/lists/*"), + "rm -rf misp_modules /var/lib/apt/lists/* /tmp/*"), Cmd("ADD", "var", "/var"), Cmd("ADD", "etc", "/etc"), ExecCmd("RUN", "chown", "-R", "daemon:daemon", "/var/log/cortex")) ++ From aa234c198fd80b3e19026853c93c50e22350adf8 Mon Sep 17 00:00:00 2001 From: To-om Date: Wed, 17 May 2017 10:22:32 +0200 Subject: [PATCH 5/6] Move documentation to CortexDocs repository --- docs/FAQ.md | 72 ------ docs/README.md | 32 --- docs/admin/configuration.md | 31 --- docs/api/README.md | 23 -- docs/api/delete-job.md | 26 -- docs/api/get-analyzer-by-type.md | 56 ---- docs/api/get-analyzer.md | 39 --- docs/api/get-job-report.md | 100 -------- docs/api/get-job.md | 44 ---- docs/api/how-to-create-an-analyzer.md | 357 -------------------------- docs/api/list-analyzers.md | 51 ---- docs/api/list-jobs.md | 95 ------- docs/api/run-analyzer.md | 138 ---------- docs/api/wait-and-get-job-report.md | 27 -- docs/images/cortex-report.png | Bin 63845 -> 0 bytes docs/images/long-report.png | Bin 31252 -> 0 bytes docs/images/short-report.png | Bin 17324 -> 0 bytes docs/installation/README.md | 10 - docs/installation/analyzers.md | 121 --------- docs/installation/binary-guide.md | 95 ------- docs/installation/build-guide.md | 144 ----------- docs/installation/deb-guide.md | 13 - docs/installation/docker-guide.md | 51 ---- docs/installation/rpm-guide.md | 18 -- docs/misp.md | 56 ---- 25 files changed, 1599 deletions(-) delete mode 100644 docs/FAQ.md delete mode 100644 docs/README.md delete mode 100644 docs/admin/configuration.md delete mode 100644 docs/api/README.md delete mode 100644 docs/api/delete-job.md delete mode 100644 docs/api/get-analyzer-by-type.md delete mode 100644 docs/api/get-analyzer.md delete mode 100644 docs/api/get-job-report.md delete mode 100644 docs/api/get-job.md delete mode 100644 docs/api/how-to-create-an-analyzer.md delete mode 100644 docs/api/list-analyzers.md delete mode 100644 docs/api/list-jobs.md delete mode 100644 docs/api/run-analyzer.md delete mode 100644 docs/api/wait-and-get-job-report.md delete mode 100644 docs/images/cortex-report.png delete mode 100644 docs/images/long-report.png delete mode 100644 docs/images/short-report.png delete mode 100644 docs/installation/README.md delete mode 100644 docs/installation/analyzers.md delete mode 100644 docs/installation/binary-guide.md delete mode 100644 docs/installation/build-guide.md delete mode 100644 docs/installation/deb-guide.md delete mode 100644 docs/installation/docker-guide.md delete mode 100644 docs/installation/rpm-guide.md delete mode 100644 docs/misp.md diff --git a/docs/FAQ.md b/docs/FAQ.md deleted file mode 100644 index 6d2fbfd22..000000000 --- a/docs/FAQ.md +++ /dev/null @@ -1,72 +0,0 @@ -# Analyzers -- [What version of MaxMind TheHive uses?](https://github.com/CERT-BDF/Cortex/wiki/FAQ#what-version-of-maxmind-cortex-uses) -- [How often are the MaxMind databases refreshed?](https://github.com/CERT-BDF/Cortex/wiki/FAQ#how-often-are-the-databases-refreshed) -- [How shall I configure the MaxMind analyzer?](https://github.com/CERT-BDF/Cortex/wiki/FAQ#how-shall-i-configure-the-maxmind-analyzer) -- [Can I use the commercial versions of the databases?](https://github.com/CERT-BDF/Cortex/wiki/FAQ#can-i-use-the-commercial-versions-of-the-databases) - -## MaxMind -### What version of MaxMind Cortex uses? -The MaxMind analyzer includes the GeoLite2 free City and Country databases. - -### How often are the MaxMind databases refreshed? -Cortex does not refresh those databases. It is up to you to create a cron job to refresh them at the frequency you want. The files to update are: -- `analyzers/MaxMind/GeoLite2-City.mmdb` -- `analyzers/MaxMind/GeoLite2-Country.mmdb` - -You can fetch up-to-date versions from . - -### How shall I configure the MaxMind analyzer? -No configuration is required. If it looks like the analyzer is not working, please clear the cache of your browser and retry. If it still doesn't work, please join [TheHive User Discussion Forum](https://groups.google.com/a/thehive-project.org/d/forum/users) or [open an issue on GitHub](https://github.com/CERT-BDF/Cortex-analyzers/issues/new). - -### Can I use the commercial versions of the databases? -The current version of Cortex does not offer that possibility. If you'd like to have it, please [request it](https://github.com/CERT-BDF/Cortex-analyzers/issues/new). - -# Authentication -- [Does Cortex support authentication?](https://github.com/CERT-BDF/Cortex/wiki/FAQ/does-cortex-support-authentication) -- [How can I make sure that only authorized users get access to Cortex?](https://github.com/CERT-BDF/Cortex/wiki/FAQ/how-can-i-make-sure-that-only-authorized-users-get-access-to-cortex) -- [How can I make sure that only authorized services get access to the Cortex API?](https://github.com/CERT-BDF/Cortex/wiki/FAQ/how-can-i-make-sure-that-only-authorized-services-get-access-to-cortex-api) - -### Does Cortex support authentication? -No. Cortex 1 does not support authentication. Cortex 2, slated for September 2017, [will support local, LDAP and AD authentication](https://github.com/CERT-BDF/Cortex/issues/7). - -### How can I make sure that only authorized users get access to Cortex? -Cortex does not currently support authentication. The next major version (v2), slated for September 2017, [will implement it](https://github.com/CERT-BDF/Cortex/issues/7). In the meantime, you should either install an authenticating reverse proxy in front of Cortex or limit access to it using a firewall or an alternative filtering device. - -If you do not protect your Cortex instance, anyone who has access to your network may run jobs or retrieve existing reports. - -### How can I make sure that only authorized services get access to Cortex API? -Cortex does not currently support service authentication or API keys. The next major version (v2), slated for September 2017, [will implement it](https://github.com/CERT-BDF/Cortex/issues/7). - -Any service may query Cortex without authentication. If you need to let only authorized services get access to your instance(s), make sure to use a filtering device and authorize only the IP addresses of those services. - -# Miscellaneous Questions -- [Can I Enable HTTPS to Connect to Cortex?](https://github.com/CERT-BDF/Cortex/wiki/FAQ#can-i-enable-https-to-connect-to-cortex) - -### Can I Enable HTTPS to Connect to Cortex? -#### TL;DR -Add the following lines to `/etc/cortex/application.conf` - - https.port: 9443 - play.server.https.keyStore { - path: "/path/to/keystore.jks" - type: "JKS" - password: "password_of_keystore" - } - -HTTP can disabled by adding line `http.port=disabled` -#### Details -To enable HTTPS in the application, add the following lines to `/etc/cortex/application.conf`: -``` - https.port: 9443 - play.server.https.keyStore { - path: "/path/to/keystore.jks" - type: "JKS" - password: "password_of_keystore" - } -``` -As HTTPS is enabled HTTP can be disabled by adding `http.port=disabled` in configuration. - -To import your certificate in the keystore, depending on your situation, you can follow [Digital Ocean's tutorial](https://www.digitalocean.com/community/tutorials/java-keytool-essentials-working-with-java-keystores). - -**More information**: -This is a setting of the Play framework that is documented on its website. Please refer to [https://www.playframework.com/documentation/2.5.x/ConfiguringHttps](https://www.playframework.com/documentation/2.5.x/ConfiguringHttps). \ No newline at end of file diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 6a6eb8c27..000000000 --- a/docs/README.md +++ /dev/null @@ -1,32 +0,0 @@ -Cortex tries to solve a common problem frequently encountered by SOCs, CSIRTs and security researchers in the course of threat intelligence, digital forensics and incident response: how to analyze observables they have collected, **at scale, by querying a single tool** instead of several? - -## Hardware pre-requisites -Cortex uses a Java VM. We recommend using a virtual machine with 8vCPU, 8 GB of RAM and 10 GB of disk. You can also use a -physical machine with similar specifications. - -## What's new - -- [Changelog](/CHANGELOG.md) - -## Installation guides - -Cortex can be installed using: -- An [RPM package](installation/rpm-guide.md) -- A [DEB package](installation/deb-guide.md) -- [Docker](installation/docker-guide.md) -- [Binary](installation/binary-guide.md) -- [Ansible script](https://github.com/drewstinnett/ansible-cortex) contributed by -[@drewstinnett](https://github.com/drewstinnett) - -Cortex can also be [built from sources](installation/build-guide.md). - -Once you have installed Cortex, you need to [install the analyzers](installation/analyzers.md). - -## Developer guides - -- [API documentation](api/README.md) -- [How to create an analyzer](api/how-to-create-an-analyzer.md) - -## Other -- [MISP integration](misp.md) -- [FAQ](FAQ.md) diff --git a/docs/admin/configuration.md b/docs/admin/configuration.md deleted file mode 100644 index 627e1a55b..000000000 --- a/docs/admin/configuration.md +++ /dev/null @@ -1,31 +0,0 @@ -# Configuration - -Cortex back-end and analyzers can find their configuration in the same file. - -The only required parameter in order to start Cortex is the key of the server (`play.crypto.secret`). This key is used -to authenticate cookies that contain data, and not only a session id. If Cortex runs in cluster mode, all instance must -share the same key. - -You should generate a random key using the following command line: - -``` -sudo mkdir /etc/cortex -(cat << _EOF_ -# Secret key -# ~~~~~ -# The secret key is used to secure cryptographics functions. -# If you deploy your application to several instances be sure to use the same key! -play.crypto.secret="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1)" -_EOF_ -) | sudo tee -a /etc/cortex/application.conf - -``` - -Please, note that this secret key is mandatory to start Cortex application. With this configuration, you will only be -able to run analyzers that do not require any configuration parameter, an API key for instance. To configure other -analyzers, refer to [analyzers](../installation/analyzers.md). - -**Warning**: By default, Cortex run an HTTP service on port `9000/tcp`. You can change the port by adding -`http.port=8080` in the configuration file or add the `-Dhttp.port=8080` parameter to the command line below. If you run -Cortex using a non-privileged user, you can't bind a port under 1024. If you run TheHive on the same system beware to -use two different TCP ports. diff --git a/docs/api/README.md b/docs/api/README.md deleted file mode 100644 index 164c5e279..000000000 --- a/docs/api/README.md +++ /dev/null @@ -1,23 +0,0 @@ - -If you are using [TheHive](https://github.com/CERT-BDF/TheHive) as a SIRP (Security Incident Response Platform), you -don't need to master the Cortex REST API. If you have a different SIRP or would like to interface other tools with -Cortex, please read on. - -## TL;DR -The current Cortex version doesn't require authentication and all API call results are provided in JSON format. - -## Available API Calls - -- [List analyzers](list-analyzers.md) -- [Get an analyzer's definition](get-analyzer.md) -- [List analyzers for a given datatype](get-analyzer-by-type.md) -- [Run an analyzer](run-analyzer.md) -- [List jobs](list-jobs.md) -- [Get a job definition](get-job.md) -- [Delete a job](delete-job.md) -- [Get a job report](get-job-report.md) -- [Wait and get a job report](wait-and-get-job-report.md) - -## How to create an analyzer - -If you want to create an analyzer, follow this [guide](how-to-create-an-analyzer.md). \ No newline at end of file diff --git a/docs/api/delete-job.md b/docs/api/delete-job.md deleted file mode 100644 index b48667007..000000000 --- a/docs/api/delete-job.md +++ /dev/null @@ -1,26 +0,0 @@ -This API call lets you delete an existing job, identified by its ID. - -**URL** -``` -DELETE /api/job/ -``` - -`JOB_ID` must be a valid job `id`. - -**Output** - -This API call doesn't produce any output. - -**Response codes** - -| Status Code | Description | -| ------------ | ------------- | -| 200 | The deletion has been made successfully | -| 404 | **TBD**: The job is unknown | -| 500 | An expected error occurred | - -**How to use it** - -``` -curl -XDELETE http://:/api/job/ -``` diff --git a/docs/api/get-analyzer-by-type.md b/docs/api/get-analyzer-by-type.md deleted file mode 100644 index edfd2d05f..000000000 --- a/docs/api/get-analyzer-by-type.md +++ /dev/null @@ -1,56 +0,0 @@ -This API call returns the list of all the analyzers that can act upon a given datatype (IP address, hash, domain...). - -**URL** -``` -GET /api/analyzer/type/ -``` - -where `DATATYPE` is a valid observable datatype: ip, url, domain, and so on. - -**Output** - -Returns a JSON array representing a list of all the analyzers that can act upon that specific datatype. Each entry includes the following attributes: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| id | String | The analyzer's identifier | -| name | String| The analyzer's name | -| version | String | The analyzer's version | -| dataTypeList | String[] | An array of the observable datatypes that the analyzer can act upon | - -*Example* - -```json -[ - { - "name": "Abuse_Finder", - "version": "1.0", - "description": "Use CERT-SG's Abuse Finder to find the abuse contact associated with domain names, URLs, IP and email addresses.", - "dataTypeList": [ - "ip", - "domain", - "url", - "email" - ], - "id": "Abuse_Finder_1_0" - }, - { - "name": "HippoMore", - "version": "1.0", - "description": "Hippocampe detailed report: provides the last detailed report for an IP, domain or a URL", - "dataTypeList": [ - "ip", - "domain", - "fqdn", - "url" - ], - "id": "HippoMore_1_0" - } -] -``` - -**How to use it** - -``` -curl http://:/api/analyzer/type/domain -``` \ No newline at end of file diff --git a/docs/api/get-analyzer.md b/docs/api/get-analyzer.md deleted file mode 100644 index f12de90dc..000000000 --- a/docs/api/get-analyzer.md +++ /dev/null @@ -1,39 +0,0 @@ -This API call returns the details of a given analyzer when you supply its ID. If you don't know the ID of the analyzer, you can get a list of all the available analyzers and the corresponding IDs by referring to the [List analyzers](List-analyzers-API) page. - -**URL** -``` -GET /api/analyzer/ -``` - -`ANALYZER_ID` should be a valid analyzer `id`. - -**Output** - -Returns a JSON object representing an analyzer, with the following attributes: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| id | String | The analyzer's identifier | -| name | String| The analyzer's name | -| version | String | The analyzer's version | -| dataTypeList | String[] | An array of the observable datatypes that the analyzer can act upon | - -*Example* - -```json -{ - "name": "File_Info", - "version": "1.0", - "description": "Parse files in several formats such as OLE and OpenXML to detect VBA macros, extract their source code, generate useful information on PE, PDF files and much more.", - "dataTypeList": [ - "file" - ], - "id": "File_Info_1_0" -} -``` - -**How to use it** - -``` -curl http://:/api/analyzer/ -``` \ No newline at end of file diff --git a/docs/api/get-job-report.md b/docs/api/get-job-report.md deleted file mode 100644 index 30f335fb5..000000000 --- a/docs/api/get-job-report.md +++ /dev/null @@ -1,100 +0,0 @@ -This API call returns the details and report of a given job, identified by its ID. - -**URL** -``` -GET /api/job//report -``` - -`JOB_ID` must be a valid job `id`. - -**Output** - -Returns a JSON object representing a job, with the following attributes: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| id | String | The job's id | -| analyzerId | String| The analyzer's id | -| status | String | The job's status: `Success`, `InProgress` or `Failure` | -| date | Number | A timestamp which represent the job's start date | -| artifact | Object | The observable details | -| report | `` Object | The job report | - - -The `` could be any JSON object, but Cortex uses some conventions. The structure of the `` object as defined by Cortex is described below: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| success | Boolean | True if the job is successful, False if it failed | -| errorMessage | String | Contains the error message if the job failed | -| summary | Object | A custom JSON object with any content (based on the analyzer) | -| artifacts | ``[] | An array of the artifacts extracted from the analysis | -| full | Object | A custom JSON object with any content (based on the analyzer). Represents the full analysis report | - - -The `` is an object representing an observable and has two attributes: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| type | String | The artifact's datatype (url, hash, ip, domain...) | -| value | String | The observable's value | - - -*Example* - -```json -{ - "id": "vVQu93ps4PwHOtLv", - "analyzerId": "File_Info_1_0", - "status": "Success", - "date": 1490204071457, - "artifact": { - "attributes": { - "dataType": "file", - "tlp": 2, - "content-type": "text/x-python-script", - "filename": "sample.py" - } - }, - "report": { - "artifacts": [ - { - "type": "sha1", - "value": "cd1c2da4de388a4b5b60601f8b339518fe8fbd31" - }, - { - "type": "sha256", - "value": "fd1755c7f1f0f85597cf2a1f13f5cbe0782d9e5597aca410da0d5f26cda26b97" - }, - { - "type": "md5", - "value": "3aa598d1f0d50228d48fe3a792071dde" - } - ], - "full": { - "Mimetype": "text/x-python", - "Identification": { - "ssdeep": "24:8ca1hbLcd8yutXHbLcTtvbrbLcvtEbLcWmtlbLca66/5:8zHbLcdbOXbLc5jrbLcVEbLcPlbLcax", - "SHA1": "cd1c2da4de388a4b5b60601f8b339518fe8fbd31", - "SHA256": "fd1755c7f1f0f85597cf2a1f13f5cbe0782d9e5597aca410da0d5f26cda26b97", - "MD5": "3aa598d1f0d50228d48fe3a792071dde" - }, - "filetype": "python script", - "Magic": "Python script, ASCII text executable", - "Exif": { - "ExifTool:ExifToolVersion": 10.36 - } - }, - "success": true, - "summary": { - "filetype": "python script" - } - } -} -``` - -**How to use it** - -``` -curl http://:/api/job//report -``` \ No newline at end of file diff --git a/docs/api/get-job.md b/docs/api/get-job.md deleted file mode 100644 index 321e6e200..000000000 --- a/docs/api/get-job.md +++ /dev/null @@ -1,44 +0,0 @@ -This API call returns the details of a given job, identified by its ID. It doesn't include the job's report, which can be fetched using the [Get job report API](Get-job-report-API). - -**URL** -``` -GET /api/job/ -``` - -`JOB_ID` must be a valid job `id`. - -**Output** - -Returns a JSON object representing a job with the following attributes: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| id | String | The job's id | -| analyzerId | String| The analyzer's id | -| status | String | The job's status: `Success`, `InProgress` or `Failure` | -| date | Number | A timestamp which represents the job's start date | -| artifact | Object | The observable details | - -*Example* - -```json -{ - "id": "c9uZDbHBf32DdIVJ", - "analyzerId": "MaxMind_GeoIP_2_0", - "status": "Success", - "date": 1490194495262, - "artifact": { - "data": "8.8.8.8", - "attributes": { - "dataType": "ip", - "tlp": 2 - } - } -} -``` - -**How to use it** - -``` -curl http://:/api/job/ -``` \ No newline at end of file diff --git a/docs/api/how-to-create-an-analyzer.md b/docs/api/how-to-create-an-analyzer.md deleted file mode 100644 index 9b10a4aee..000000000 --- a/docs/api/how-to-create-an-analyzer.md +++ /dev/null @@ -1,357 +0,0 @@ -# Overview -The main goal of Cortex is to run analysis on a given observable, defined by its data type, and a value. This observable could be of any type: IP, domain, URL, email, file... - -The programs that Cortex invokes to analyze observables are called **Analyzers**. An **analyzer** could be written in any programming language supported by Linux as long as the resulting program is on the same machine as Cortex and is executable. - -# Create a Basic Analyzer -From a technical standpoint, a minimal **analyzer** would be defined by: -- A JSON definition file -- An executable script. As of this writing, all the available analyzers are written in Python. However, analyzers can be written in any programming language supported by Linux - -Throughout this document, we will use the [Hippocampe_More](https://github.com/CERT-BDF/Cortex-Analyzers/tree/master/analyzers/Hippocampe) analyzer as an example to teach you how to write your own analyzer. - -Our **analyzer** will be defined inside a folder called `Hippocampe`. We use a convention. The folder where the analyzer is located is named after the product or service it leverages to do its work: MISP, MaxMind, PassiveTotal, VirusTotal, DomainTools... - -## The JSON Definition File -As its name implies, the JSON definition file contain metadata describing the analyzer. The associated filename must be `_.json`. For example: `Hippocampe_more.json` and `Hippocampe_hipposcore.json`. - -The structure of this file is described in the table below: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| name | String, `REQUIRED` | The analyzer's name | -| version | String `REQUIRED` | The analyzer's version | -| description | String `REQUIRED`| The analyzer's description | -| dataTypeList | String[] `REQUIRED` | An array of strings, listing the observable data types that could be analyzed | -| command | String `REQUIRED` | The command to invoke the analyzer's script. It must be relative to the root directory that contain all the analyzer folders | -| baseConfig* | String `OPTIONAL` | The name of the configuration attribute defined in the Cortex configuration file | -| config* | Object `OPTIONAL` | A configuration object that will be passed to the analyzer's command | - -**Note**: Fields marked with a star (*) will be described in depth later in this document. - -**Example** -```json -{ - "name": "HippoMore", - "version": "1.0", - "description": "Hippocampe detailed report: provides the last detailed report for an IP, domain or a URL", - "dataTypeList": ["ip", "domain", "fqdn", "url"], - "baseConfig": "Hippocampe", - "config": { - "check_tlp": false, - "max_tlp":3, - "service": "more" - }, - "command": "Hippocampe/hippo.py" -} -``` - -## The Script -The analyzer script must be an executable script that Cortex runs using the `command` provided within the JSON definition file. The script could be written in any programming language, as long as it could be executed using a shell command. - -When running the analyzer's script file, Cortex provides some input data through the standard input, and expects an output through the standard output. - -### Analyzer Input -In Cortex, we distinguish between two types of observables: -- Value-based observables -- File-based observables - -The input sent by Cortex to the analyzers depend on the observable type. - -*Note*: when using Cortex with [TheHive](https://github.com/CERT-BDF/TheHive/), we use some output conventions that allow us to normalize the way TheHive displays the analysis reports. - -#### Input for Value-based Observables -The input for value-based observables must have the following structure: - -```json -{ - "dataType": "ip", - "data": "8.8.8.8", - "config": {} -} -``` - -The following table explains the JSON schema of the input: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| dataType | String, `REQUIRED` | The observables's type | -| data | String `REQUIRED` | The observable's value | -| config* | Object `OPTIONAL` | A config object, representing the analyzer's options and parameters if any | - - -#### Input for File-based Observables -The input for file-based observables must have the following structure: -```json -{ - "dataType": "file", - "attachment": { - "name": "" - }, - "file": "/path/to/file/observable", - "config": {} -} -``` - -The following table explains the JSON schema of the input: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| dataType | String, `REQUIRED` | The observable's type (`file` in this case) | -| file | String `REQUIRED` | The observable's file path | -| attachment.name | String `OPTIONAL` | The observable's file name | -| config* | Object `OPTIONAL` | A config object, representing the analyzer's options and parameters if any | - -#### The Analyzer Configuration Object -An analyzer can define a default configuration object in its JSON definition file. Cortex can override or add additional configuration properties using the Cortex's configuration file. - -Based on that, the `config` object passed to the analyzer's script results from the merge operation of three objects: -- the `config` object defined in the analyzer's JSON definition file (defines the default values of the analyzer's config) -- the `baseConfig` object defined in the Cortex's configuration file using the `analyzer.config.` property (used to hold sensitive properties like API keys or credentials) -- the *global* analyzers configuration defined in the Cortex's configuration file using `analyzer.config.global` (generally contains proxy configuration information) - - -### Analyzer Output -The output from Cortex could technically be any JSON object. That said, Cortex's UI might rely on a specific attribute to decide if the job failed or succeeded. The property is named `success` and must be a Boolean value. - -In the existing analyzers we tried to stick to some conventions where we defined the formats defined below. - -#### Successful Analysis -```json -{ - "success": true, - "summary": {}, - "artifacts": [], - "full": {} -} -``` - -The following table explains the JSON conventions of the output: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| success | Boolean `REQUIRED` | The analysis success flag | -| summary | Object `OPTIONAL` | The analysis summary: a small report | -| full | Object `REQUIRED` | The analysis complete report | -| artifacts | Array[``] `OPTIONAL` | An array of artifacts discovered by the analyzer | - -The `` object has the following structure: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| type | String `REQUIRED` | The artifact data type | -| value | String `REQUIRED` | The artifact value | - -**Note**: the `artifacts` array will be used in the future by [TheHive](https://github.com/CERT-BDF/TheHive/) to display or import the extracted artifacts from an analysis report. - -#### Unsuccessful Analysis -```json -{ - "success": false, - "errorMessage": "" -} -``` - -# The Cortexutils Library -`cortexutils` is a Python library available on `pip`. It provides a Python class that facilitates the creation of analyzer script files. It includes an abstract `Analyzer` class that a programmer may inherit and override in their script. It also provides some methods to quickly format the output to be compliant with the JSON schema expected by [TheHive](https://github.com/CERT-BDF/TheHive/). - -To create an analyzer class, developers have to: - -1. Create a subclass of `cortexutils.analyzer.Analyzer` -2. Override the constructor, call the super constructor and if needed, read the specific analyzer's options (read specific configuration properties from the config object) -3. Override the `run` method. It must either return a report, using the `report` method, or an error using the `error`method. If `run`is not overridden, the analyzer returns an empty report -3. Optionally override the `summary` method. It should return a JSON object representing a summary of the analyzer report. If not overridden, the analyzer returns an empty summary -3. Optionally override the `artifacts` method. It should return a JSON array representing a list of `artifact` objects (as described above). If not overridden, the analyzer returns the result of an `ioc-parser`, ran over the full JSON report. - -Below is an example of a basic analyzer that can handle IPs and domains: - -```python -#!/usr/bin/env python -# encoding: utf-8 - -from cortexutils.analyzer import Analyzer - -# Define analyzer's class -class BasicExampleAnalyzer(Analyzer): - # Analyzer's constructor - def __init__(self): - # Call the constructor of the super class - Analyzer.__init__(self) - - # Read specific config options - self.optional_prop = self.getParam('config.optional_prop', '') - self.required_prop = self.getParam('config.required_prop', None, 'Error: Missing required_prop') - - # Override the report method. This is the analyzer's entry point - def run(self): - # Put your analyzer's logic here - result = {} - - # This is just an example - if self.data_type == 'ip': - result['findings'] = ['1.1.1.1', '2.2.2.2', '3.3.3.3'] - elif self.data_type == 'domain': - result['findings'] = ['domain1.com', 'domain2.com', 'domain3.com'] - else: - return self.error('Unsupported observable data type') - - # Return the report - return self.report(result) - - # Override the summary method - def summary(self, raw_report): - return { - 'count': len(raw_report['findings']) - } - - # Override the artifacts method - def artifacts(self, raw_report): - result = [] - if 'findings' in raw_report: - for item in raw_report['findings']: - result.append({'type': self.data_type, 'value': item}) - - return result - -# Invoke the analyzer -if __name__ == '__main__': - BasicExampleAnalyzer().run() - -``` - -To call this analyzer, we can run the following command: - -``` -python sample-analyzer.py <<< '{ - "dataType":"ip", - "data": "8.8.8.8", - "config":{ - "required_prop": "anyvalue" - } -}' -``` - -This will generate the following output: - -```json -{ - "success" : true, - "artifacts" : [ - { - "value" : "1.1.1.1", - "type" : "ip" - }, - { - "value" : "2.2.2.2", - "type" : "ip" - }, - { - "value" : "3.3.3.3", - "type" : "ip" - } - ], - "summary" : { - "count" : 3 - }, - "full" : { - "findings" : [ - "1.1.1.1", - "2.2.2.2", - "3.3.3.3" - ] - } -} -``` - -And in Cortex ![](../images/cortex-report.png) - -# TheHive and Cortex analyzers -Using Cortex from an instance of [TheHive](https://github.com/CERT-BDF/TheHive/) helps the users improve the analysis report visualization. In fact, TheHive uses the outputs generated from Cortex analyzers in two ways: - -- Store the `summary` content as part of the observable's data. This is available for successful analysis jobs only. -- Display the `full`report using the report templates defined within TheHive. - -## Report templates -[TheHive](https://github.com/CERT-BDF/TheHive/) is based on Angular 1 and report templates have to be Angular templates which we try to fill using the job's report data. - -We distinguish 2 types of report templates: - -### Short reports -Generates what we call **mini reports**, to be displayed in the observable's details page and observables list. Short report templates receive the following data: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| name | String | Analyzer's ID | -| content | Object | The job report's `summary` object | -| artifact | Object | The observable details, as stored in TheHive | - -For example, if we want to create a short report template for the `BasicExampleAnalyzer`, we could write the following HTML short report file: - -```html -Basic: {{content.count || 0}} record(s) -``` - -`content` being the following: - -```json -{ - "count" : 3 -} -``` - -The result in TheHive will be ![](../images/short-report.png) - -### Long reports -Like short reports, the long reports are used to render the content of the `full` attribute of a job JSON report. - -Long report templates receive the following data: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| name | String | Analyzer's ID | -| status | String | The job's status: `Success`, `Failure`, `InProgress` | -| success | Boolean | The job's success status | -| content | Object | The job report's `full` object | -| artifact | Object | The observable details, as stored in TheHive | - - -For example, if we want to create a long report template for the `BasicExampleAnalyzer`, we could write the following HTML long report file: - -```html - -
-
- {{name}} -
-
-
{{content.findings.length}} {{artifact.dataType | uppercase}}(s) found form {{artifact.data | fang}}
-
    -
  • {{finding}}
  • -
-
-
- - -
-
- {{artifact.data | fang}} -
-
- {{content.errorMessage}} -
-
-``` - -`content` being the following: - -```json -{ - "findings" : [ - "1.1.1.1", - "2.2.2.2", - "3.3.3.3" - ] -} -``` - -The result in TheHive will be ![](../images/long-report.png) \ No newline at end of file diff --git a/docs/api/list-analyzers.md b/docs/api/list-analyzers.md deleted file mode 100644 index d1341a393..000000000 --- a/docs/api/list-analyzers.md +++ /dev/null @@ -1,51 +0,0 @@ -This API call returns the list of all the analyzers enabled within Cortex. - -**URL** -``` -GET /api/analyzer -``` - -**Output** - -Returns a JSON array representing a list of all the enabled analyzers. Each entry includes the following attributes: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| id | String | The analyzer's identifier | -| name | String| The analyzer's name | -| version | String | The analyzer's version | -| dataTypeList | String[] | An array of the observable datatypes that the analyzer can act upon | - -*Example* - -```json -[ - { - "name": "File_Info", - "version": "1.0", - "description": "Parse files in several formats such as OLE and OpenXML to detect VBA macros, extract their source code, generate useful information on PE, PDF files and much more.", - "dataTypeList": [ - "file" - ], - "id": "File_Info_1_0" - }, - { - "name": "HippoMore", - "version": "1.0", - "description": "Hippocampe detailed report: provides the last detailed report for an IP, domain or a URL", - "dataTypeList": [ - "ip", - "domain", - "fqdn", - "url" - ], - "id": "HippoMore_1_0" - } -] -``` - -**How to use it** - -``` -curl http://:/api/analyzer -``` \ No newline at end of file diff --git a/docs/api/list-jobs.md b/docs/api/list-jobs.md deleted file mode 100644 index 797be3e43..000000000 --- a/docs/api/list-jobs.md +++ /dev/null @@ -1,95 +0,0 @@ -This API call returns a list of analysis jobs. - -**URL** -``` -GET /api/job -``` - -**Input** - -This API call supports a list of filters and pagination parameters can be provided in the query: - -| Query Parameter | Default value | Description | -| ------------ | ------------- | ------------- | -| dataTypeFilter | Empty | A datatype value: ip, domain, hash etc... | -| dataFilter | Empty | A string representing a part of an observable value. Could be an IP or part of an IP, a domain, url and so on | -| analyzerFilter | Empty | An analyzer's ID | -| start | 0 | A number representing the index of the page start | -| limit | 10 | A number representing a page size | - -*Example* -``` -GET /api/job?analyzerFilter=Abuse_Finder_1_0&dataTypeFilter=domain&dataFilter=.com&start=0&limit=50 -``` - -should return the list of Abuse_Finder jobs corresponding to domains which include `.com`. - -**Output** - -Returns a JSON array representing a list of jobs. Each entry includes the following attributes: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| id | String | The job's id | -| analyzerId | String| The analyzer's id | -| status | String | The job's status: `Success`, `InProgress` or `Failure` | -| date | Number | A timestamp which corresponds to the job's start date | -| artifact | Object | The observable details | - -*Example* - -```json -[ - { - "id": "OsmbnQJGmeCgvDxP", - "analyzerId": "OTXQuery_1_0", - "status": "Failure", - "date": 1490194495264, - "artifact": { - "data": "8.8.8.8", - "attributes": { - "dataType": "ip", - "tlp": 2 - } - } - }, - { - "id": "c9uZDbHBf32DdIVJ", - "analyzerId": "MaxMind_GeoIP_2_0", - "status": "Success", - "date": 1490194495262, - "artifact": { - "data": "8.8.8.8", - "attributes": { - "dataType": "ip", - "tlp": 2 - } - } - }, - { - "id": "OcFlZbLNNUsIiJZq", - "analyzerId": "HippoMore_1_0", - "status": "InProgress", - "date": 1490194495259, - "artifact": { - "data": "8.8.8.8", - "attributes": { - "dataType": "ip", - "tlp": 2 - } - } - } -] -``` - -**How to use it** - -``` -curl http://:/api/job -``` - -or - -``` -curl 'http://:/api/job?start=0&limit=100' -``` \ No newline at end of file diff --git a/docs/api/run-analyzer.md b/docs/api/run-analyzer.md deleted file mode 100644 index 836063002..000000000 --- a/docs/api/run-analyzer.md +++ /dev/null @@ -1,138 +0,0 @@ -The API calls described below will let you run analyzers on one observable at a time. Two types of observables can be analyzed: -- Value-based: generally string values such as IP addresses, domains, hashes and so on. -- File-based: the file that you'd like to analyze must be submitted. - -## Analyze Value-based Observables - -**URL** -``` -POST /api/analyzer//run -``` - -`ANALYZER_ID` must be a valid analyzer `id`. - -**Input** - -This API call requires a JSON POST body describing an observable and the following attributes: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| data | String | The observable's value | -| attributes | ``Object | The observable's attributes | - - -The `` object structure is the following: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| dataType | String | The observable's data type | -| tlp | Number | The observable's TLP: 0 for `WHITE`, 1 for `GREEN`, 2 for `AMBER`, 3 for `RED` | - -*Example* - -``` -curl -XPOST -H 'Content-Type: application/json' http://:/api/analyzer/Hipposcore_1_0/run -d '{ - "data":"mydomain.com", - "attributes":{ - "dataType":"domain", - "tlp":2 - } -}' -``` - -This returns the details of the created analysis job. - -**Output** - -Returns a JSON object representing the started analysis job: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| id | String | The job's ID | -| analyzerId | String| The analyzer's ID | -| status | String | The job's status: `Success`, `InProgress` or `Failure` | -| date | Number | A timestamp which represents the job's start date | -| artifact | Object | The observable details | - -*Example* - -```json -{ - "id": "ymlrxZB8efyZhFEg", - "analyzerId": "Hipposcore_1_0", - "status": "Success", - "date": 1490263456480, - "artifact": { - "data": "mydomain.com", - "attributes": { - "dataType": "domain", - "tlp": 2 - } - } -} -``` - -## Analyze File-based observables - -**URL** -``` -POST /api/analyzer//run -``` - -`ANALYZER_ID` must be a valid analyzer `id`. - -**Input** - -This API call requires submitting the file to be analyzed by sending a request as a multipart format: - -- The first part must be named `data` and must contain the file. -- The second part must be named `_json` and must specify the observable details in JSON format, as described below: - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| dataType | String | The observable's data type | -| tlp | Number | The observable's TLP: 0 for `WHITE`, 1 for `GREEN`, 2 for `AMBER`, 3 for `RED` | - -*Example* - -``` -curl -XPOST http://:/api/analyzer/File_Info_1_0/run \ - -F '_json={ - "dataType":"file", - "tlp":2 - };type=application/json' \ - -F 'attachment=@file.png;type=image/png' -``` - -This returns the details of the created analysis job. - -**Output** - -Returns a JSON object representing the started analysis job. - -| Attribute | Type | Description | -| ------------ | ------------- | ------------- | -| id | String | The job id | -| analyzerId | String| The analyzer's id | -| status | String | The job's status: `Success`, `InProgress` or `Failure` | -| date | Number | A timestamp which represents the job's start date | -| artifact | Object | The observable details | - -*Example* - -```json -{ - "id": "LOcqObDtJEOayPuV", - "analyzerId": "File_Info_1_0", - "status": "Success", - "date": 1490265356725, - "artifact": { - "attributes": { - "dataType": "file", - "tlp": 2, - "content-type": "image/png", - "filename": "file.png" - } - } -} -``` diff --git a/docs/api/wait-and-get-job-report.md b/docs/api/wait-and-get-job-report.md deleted file mode 100644 index a81e6ccd7..000000000 --- a/docs/api/wait-and-get-job-report.md +++ /dev/null @@ -1,27 +0,0 @@ -This API call is almost the same as [Get Job Report API](Get-job-report-API) but introduces an asynchronous behavior. It means that this API can wait for a given amount of time until the job completes. It also supports a timeout parameter. - -Instead of returning the details of an in-progress job, it will wait until it finishes then returns the report, provided it doesn't timeout. - -**URL** -``` -GET /api/job//waitreport?atMost= -``` - -`JOB_ID` must be a valid job `id`. -`DURATION` should be a valid duration, default to infinite. The duration format is a string composed by a number and a unit (based on [Scala durations](http://www.scala-lang.org/api/2.9.3/scala/concurrent/duration/Duration.html)), for example: -- 10seconds -- 1minute -- 10minutes -- 2hours - -If `atMost`query parameter is not specified, it defaults to *Infinite* (which can be a very, very long time considering the age of the Universe). - -**Output** - -Same output as [Get Job Report API](Get-job-report-API). - -**How to use it** - -``` -curl http://:/api/job//waitreport -``` \ No newline at end of file diff --git a/docs/images/cortex-report.png b/docs/images/cortex-report.png deleted file mode 100644 index d10e618e18bd05d1d937c74b4ff52c40b3bd9cab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63845 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 UJPC$b_;24HwLcU8ICS>^0i6c?y#N3J diff --git a/docs/images/long-report.png b/docs/images/long-report.png deleted file mode 100644 index 6a384959e9139f9960da3cde690a8dad9d142e0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31252 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 R^!qbK2@zT0O2MxI{|8{wWrF|! diff --git a/docs/installation/README.md b/docs/installation/README.md deleted file mode 100644 index e35be780a..000000000 --- a/docs/installation/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Installation guides - -TheHive can be installed using: -- [rpm package](rpm-guide.md) -- [deb package](deb-guide.md) -- [docker](docker-guide.md) -- [binary](binary-guide.md) -- [ansible script](https://github.com/drewstinnett/ansible-thehive) contributed by -[@drewstinnett](https://github.com/drewstinnett) -- [build from sources](build-guide.md) \ No newline at end of file diff --git a/docs/installation/analyzers.md b/docs/installation/analyzers.md deleted file mode 100644 index 59dc5c166..000000000 --- a/docs/installation/analyzers.md +++ /dev/null @@ -1,121 +0,0 @@ -# Cortex analyzers - -Analyzers are autonomous applications managed by and run through the Cortex core engine. Analyzers have their -[own dedicated GitHub repository](https://github.com/CERT-BDF/Cortex-Analyzers). They are included in the Cortex binary -package but you have to get them from the repository if you decide to build Cortex from sources or if you need to update -them. - -## Pre-requisites -Currently, all provided analyzers are written in Python. They don't require any build phase but their dependencies have -to be installed. Before proceeding, you'll need to install the system package dependencies that are required by some of -them: - -``` -apt-get install python-pip python2.7-dev ssdeep libfuzzy-dev libfuzzy2 libimage-exiftool-perl libmagic1 build-essential -``` - -Each analyzer comes with its own, pip compatible `requirements.txt` file. You can install all requirements with the -following commands: - -``` -cd analyzers -sudo pip install $(cat */requirements.txt | sort -u) -``` - -## From repository -If you want to get up-to-date analyzers, you can clone the GitHub repository: - -``` -git clone https://github.com/CERT-BDF/Cortex-Analyzers -``` - -Next, you'll need to tell Cortex where to find the analyzers. Currently, all the analyzers must be in the same -directory. Add the following to the Cortex configuration file (`application.conf`): - -``` -analyzer { - path = "path/to/analyzers" -} -``` -## Configuration - -Analyzers configuration is stored in Cortex configuration file (application.conf) in `analyzer.config` section. There is -one subsection for each analyzer group. The configuration provided to analyzer is the merge of: - - the global configuration: all item in `analyzer.config.global` section. This settings are applied for all analyzers. - It is particularly useful for proxy settings (cf. example below) - - the analyzer group configuration. Some analyzers shares configuration items, VirusTotal API key for all VirusTotal - analyzers for example. Group name can be found in JSON description file in analyzer folder, under `baseConfig` key. - - the analyzer configuration defined in JSON description file, under `config` key. - -Here is the complete configuration you should provide to make all analyzers work: - - -``` -analyzer { - path = "path/to/Cortex-Analyzers/analyzers" - config { - global { - proxy { - http="http://PROXY_ADDRESS:PORT", - https="http://PROXY_ADDRESS:PORT" - } - } - CIRCLPassiveDNS { - user= "..." - password= "..." - } - CIRCLPassiveSSL { - user= "..." - password= "..." - } - DNSDB { - server="https://api.dnsdb.info" - key="..." - } - DomainTools { - username="..." - key="..." - } - GoogleSafebrowsing { - key = "..." - } - Hippocampe { - url="..." - } - JoeSandbox { - url = "..." - apikey = "..." - } - Nessus { - url ="..." - login="..." - password="..." - policy="..." - ca_bundle="..." - allowed_network="..." - } - OTXQuery { - key="..." - } - PassiveTotal { - key="..." - username="..." - } - PhishingInitiative { - key="..." - } - PhishTank { - key="..." - } - Virusshare { - path = "..." - } - VirusTotal { - key="..." - } - Yara { - rules=["..."] - } - } -} -``` diff --git a/docs/installation/binary-guide.md b/docs/installation/binary-guide.md deleted file mode 100644 index 88a381a9c..000000000 --- a/docs/installation/binary-guide.md +++ /dev/null @@ -1,95 +0,0 @@ -# Installation Guide for Ubuntu 16.04 LTS - -This guide describes the manual installation of Cortex from binaries in Ubuntu 16.04. - -# 1. Minimal Ubuntu Installation - -Install a minimal Ubuntu 16.04 system with the following software: - * Java runtime environment 1.8+ (JRE) - -Make sure your system is up-to-date: - -``` -sudo apt-get update -sudo apt-get upgrade -``` - -# 2. Install a Java Virtual Machine -You can install either Oracle Java or OpenJDK. - -## 2.1. Oracle Java -``` -echo 'deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main' | sudo tee -a /etc/apt/sources.list.d/java.list -sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key EEA14886 -sudo apt-get update -sudo apt-get install oracle-java8-installer -``` - -## 2.2 OpenJDK -``` -sudo add-apt-repository ppa:openjdk-r/ppa -sudo apt-get update -sudo apt-get install openjdk-8-jre-headless - -``` - -# 3. Install Cortex - -Binary package can be downloaded at [thehive-cortex.zip](https://dl.bintray.com/cert-bdf/cortex/cortex-latest.zip) - -Download and unzip the chosen binary package. TheHive files can be installed wherever you want on the filesystem. In -this guide, we decided to set it in `/opt`. - -``` -cd /opt -wget https://dl.bintray.com/cert-bdf/cortex/cortex-latest.zip -unzip cortex-latest.zip -ln -s cortex-x.x.x cortex -``` - - -# 4. First start - -Change your current directory to Cortex installation directory (`/opt/cortex` in this guide), then execute: - -``` -bin/cortex -Dconfig.file=/etc/cortex/application.conf -``` - -It is recommended to use a dedicated non-privilege user to start Cortex. If so, make sure that your user can create log file in `/opt/cortex/logs` - -If you'd rather start the application as a service, do the following: -``` -sudo addgroup cortex -sudo adduser --system cortex -sudo cp /opt/cortex/package/cortex.service /usr/lib/systemd/system -sudo chown -R cortex:cortex /opt/cortex -sudo chgrp cortex /etc/cortex/application.conf -sudo chmod 640 /etc/cortex/application.conf -sudo systemctl enable cortex -sudo service cortex start -``` - -Please note that the service may take some time to start. - -Cortex comes with a simplistic frontend. Open your browser and connect to `http://YOUR_SERVER_ADDRESS:9000/` - -# 5. Plug analysers - -Now that Cortex starts successfully, downloads `Cortex-Analyzers` and edit the configuration file and set the path to -`Cortex-Analyzers/analyzers`. Follow details available in the [analyzers page](analyzers.md). - -## 6. Update - -To update Cortex from binaries, just stop the service, download the latest package, rebuild the link `/opt/cortex` and -restart the service. - -``` -service cortex stop -cd /opt -wget https://dl.bintray.com/cert-bdf/cortex/cortex-latest.zip -unzip cortex-latest.zip -rm /opt/cortex && ln -s cortex-x.x.x cortex -chown -R cortex:cortex /opt/cortex /opt/cortex-x.x.x -service cortex start -``` diff --git a/docs/installation/build-guide.md b/docs/installation/build-guide.md deleted file mode 100644 index adb7e0e26..000000000 --- a/docs/installation/build-guide.md +++ /dev/null @@ -1,144 +0,0 @@ -# Build from sources - -This document is a step-by-step guide to build Cortex from sources. - -## 1. Pre-requisites - -The following softwares are required to download and build Cortex. - -* Java Development Kit 8 (JDK) - * downloadable from http://www.oracle.com/technetwork/java/javase/downloads/index.html -* git - * Use the system package or downloadable it from http://www.git-scm.com/downloads -* NodeJs with its package manager (NPM) - * downloadable from https://nodejs.org/en/download/ -* Grunt - * After NodeJs installation, run `sudo npm install -g grunt-cli` -* Bower - * After NodeJs installation, run `sudo npm install -g bower` - - -# 2. Quick Build Guide - -To install the requirements and build Cortex from sources, please follow the instructions below depending on your -operating system. - -## 2.1. CentOS/RHEL - -### 2.1.1. Packages - -``` -sudo yum -y install git bzip2 -``` - -### 2.1.2. Installation of OpenJDK - -``` -sudo yum -y install java-1.8.0-openjdk-devel -``` - -### 2.1.3. Installation of NodeJs - -Install the EPEL Repository: - -You should have the "extras" repository enabled, then: -``` -sudo yum -y install epel-release -``` - -Then, you can install NodeJs: - -``` -sudo yum -y install nodejs -``` - -### 2.1.4. Installation of bower and grunt - -``` -sudo npm install -g bower grunt-cli -``` - -## 2.2. Ubuntu - -### 2.2.1. Packages - -``` -sudo apt-get install git wget -``` - -### 2.2.2. Installation of Oracle JDK - -``` -echo 'deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main' | sudo tee -a /etc/apt/sources.list.d/java.list -sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key EEA14886 -sudo apt-get update -sudo apt-get install oracle-java8-installer -``` - -### 2.2.3. Installation of NodeJs - -``` -sudo apt-get install wget -wget -qO- https://deb.nodesource.com/setup_4.x | sudo bash - -sudo apt-get install nodejs -``` - -### 2.2.4. Installation of bower and grunt - -``` -sudo npm install -g bower grunt-cli -``` - -## 2.3. Cortex - -### Download sources - -``` -git clone https://github.com/CERT-BDF/Cortex.git -``` - -### Build the projects - -``` -cd Cortex -bin/activator clean stage -``` - -It will download all dependencies (could be long) then build the back-end. -This command clean previous build files and create an autonomous package in `target/universal/stage` directory. This -packages contains Cortex binaries with required libraries (`/lib`), analyzers (`/analyzers`), configuration files -(`/conf`) and startup scripts (`/bin`). - -Binaries are built and stored in `Cortex/target/universal/stage/`. Install them in `/opt/cortex` for example. - -``` -sudo cp -r Cortex/target/universal/stage /opt/cortex -``` - -Follow the [configuration guide](../admin/configuration.md) to run Cortex. - - -### First start - -Follow [First start in the Installation guide](binary-guide.md#4-first-start) to start using Cortex. - - -## Build the front-end only -Building back-end builds also front-end, so you don't need to build front-end separately. This section is useful only -for troubleshooting or in order to install front-end in a reverse proxy. - -Go to front-end directory: -``` -cd Cortex/ui -``` - -Install NodeJs libraries (required by building step), bower libraries (javascript libraries downloaded by browser). Then -build the front-end : -``` -npm install -bower install -grunt build -``` - -This step generates static files (html, javascript and related resources) in `dist` directory. These files are ready to -be imported in http server. diff --git a/docs/installation/deb-guide.md b/docs/installation/deb-guide.md deleted file mode 100644 index 7fbecf1af..000000000 --- a/docs/installation/deb-guide.md +++ /dev/null @@ -1,13 +0,0 @@ -# Installation of TheHive using DEB package - -Debian packages are published on Bintray repository. All packages are signed using the key [562CBC1C](/PGP-PUBLIC-KEY) -(fingerprint: 0CD5 AC59 DE5C 5A8E 0EE1 3849 3D99 BB18 562C BC1C): - -``` -echo 'deb https://dl.bintray.com/cert-bdf/debian any main' | sudo tee -a /etc/apt/sources.list.d/thehive-project.list -sudo apt-key adv --keyserver hkp://pgp.mit.edu --recv-key 562CBC1C -sudo apt-get update -sudo apt-get install cortex -``` - -After package installation, you should configure Cortex (see [configuration guide](../admin/configuration.md)) \ No newline at end of file diff --git a/docs/installation/docker-guide.md b/docs/installation/docker-guide.md deleted file mode 100644 index 1cbd71fa8..000000000 --- a/docs/installation/docker-guide.md +++ /dev/null @@ -1,51 +0,0 @@ -# Install TheHive using docker - -This guide assume that you will use docker. - -## How to use this image - -Easiest way to start Cortex: -``` -docker run certbdf/cortex -``` - -From version 1.1.0, we don't provide the all-in-one docker (an image which contained TheHive and Cortex). If you want to -run TheHive and Cortex in docker, follow the -[TheHive docker guide](https://github.com/CERT-BDF/TheHive/blob/master/docs/installation/docker-guide.md). - -## Analyzers - -Analyzers are embedded in docker image in /opt/Cortex-Analyzers/analyzers. If you want to update then, you should -install them outside docker and overwrite existing ones: -``` -docker run --volume /path/to/analyzers:/opt/Cortex-Analyzers/analyzers:ro certbdf/cortex:latest -``` - -Most analyzers require configuration. You can inject configuration file using volume argument: -``` -docker run --volume /path/to/your/configuration:/etc/cortex/application.conf:ro certbdf/cortex:latest -``` - -You should also publish HTTP service to make Cortex available. This is done by adding publish parameter: -``` -docker run --publish 0.0.0.0:8080:9000 certbdf/cortex:latest -``` -This command exposes Cortex service on port 8080/tcp. - -## Customize Cortex docker - -By Default, Cortex docker add minimal configuration: - - choose a random secret (play.crypto.secret) - - configure analyzer path - -This behavious can be disabled by adding `--no-config` to docker command line: -`docker run certbdf/cortex:latest --no-config`. - -Docker image accepts more options: - - --no-config : do not try to configure Cortex (add secret and analyzers location) - - --no-config-secret : do not add random secret to configuration - - --secret : secret to secure sessions - - --analyzer-path : where analyzers are located - - --no-misp-modules : disabled MISP modules - - diff --git a/docs/installation/rpm-guide.md b/docs/installation/rpm-guide.md deleted file mode 100644 index 8cc639d6c..000000000 --- a/docs/installation/rpm-guide.md +++ /dev/null @@ -1,18 +0,0 @@ -# Installation of TheHive using RPM package - -RPM packages are published on Bintray repository. All packages are signed using the key [562CBC1C](/PGP-PUBLIC-KEY) -(fingerprint: 0CD5 AC59 DE5C 5A8E 0EE1 3849 3D99 BB18 562C BC1C) - -First install rpm release package: -``` -yum install install https://dl.bintray.com/cert-bdf/rpm/thehive-project-release-1.0.0-3.noarch.rpm -``` -This will install TheHive Project repository (in /etc/yum.repos.d/thehive-rpm.repo) and the GPG public key (in -/etc/pki/rpm-gpg/GPG-TheHive-Project). - -Then you will able to install Cortex package using yum -``` -yum install cortex -``` - -After package installation, you should configure Cortex (see [configuration guide](../admin/configuration.md)) \ No newline at end of file diff --git a/docs/misp.md b/docs/misp.md deleted file mode 100644 index cee49840d..000000000 --- a/docs/misp.md +++ /dev/null @@ -1,56 +0,0 @@ -# MISP integration - -## Invoke MISP modules in Cortex - -Since version 1.1.1, Cortex can analyze observable using -[MISP expansion modules](https://github.com/MISP/misp-modules#expansion-modules). - -Follow [MISP documentation](https://github.com/MISP/misp-modules#how-to-install-and-start-misp-modules) to install MISP -modules. MISP modules service doesn't need to be started. Modules must be present in the same host than Cortex. -``` -sudo apt-get install python3-dev python3-pip libpq5 libjpeg-dev -cd /usr/local/src/ -sudo git clone https://github.com/MISP/misp-modules.git -cd misp-modules -sudo pip3 install -I -r REQUIREMENTS -sudo pip3 install -I . -``` - -Integration with MISP modules can then be enabled by adding the line `misp.modules.enabled = true` in -Cortex `application.conf`. - -Most MISP modules require configuration. Settings must be placed in misp.modules.config key. If required some -configuration is missing, MISP module is not loaded. - - -``` -misp.modules { - enabled = true - - config { - shodan { - apikey = "" - } - dns { - nameserver = "127.0.0.1" - } - } -} -``` -Cortex uses Python wrapper to run MISP modules. It is located in `contrib` folder. Cortex should be able to locate it -automatically. You can force its location in configuraton under settings: -``` -misp.modules.loader = /path/to/misp-modules-loader.py" -``` - -## Invoke Cortex in MISP - -Cortex can be connected to a MISP instance. Under `Server settings` of MISP `Administration` menu, go to `Plugin -settings` and in Cortex section: - - set `Plugin.Cortex_services_enable` to `true` - - set `Plugin.Cortex_services_url` to `http://127.0.0.1` (replace 127.0.0.1 by Cortex IP address) - - set `Plugin.Plugin.Cortex_services_port` to `9000` (replace 9000 by Cortex port) - -Then Cortex analyzer list should appear in Cortex section. They must be enabled before being available to MISP users. - - \ No newline at end of file From 585778727f46db0906e88550a8945678a0020da5 Mon Sep 17 00:00:00 2001 From: To-om Date: Wed, 17 May 2017 17:08:52 +0200 Subject: [PATCH 6/6] Update changelog --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63e644c68..c409bc41f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## [1.1.1](https://github.com/CERT-BDF/Cortex/tree/1.1.1) (2017-05-17) + +[Full Changelog](https://github.com/CERT-BDF/Cortex/compare/1.1.0...1.1.1) + +**Implemented enhancements:** + +- Missing logos and favicons [\#25](https://github.com/CERT-BDF/Cortex/issues/25) +- MISP integration feature request [\#21](https://github.com/CERT-BDF/Cortex/issues/21) + ## [1.1.0](https://github.com/CERT-BDF/Cortex/tree/1.1.0) (2017-05-12) [Full Changelog](https://github.com/CERT-BDF/Cortex/compare/1.0.2...1.1.0) @@ -8,7 +17,6 @@ - Add support to .deb and .rpm package generation [\#20](https://github.com/CERT-BDF/Cortex/issues/20) - Display analyzers metadata [\#18](https://github.com/CERT-BDF/Cortex/issues/18) -- MISP integration feature request [\#21](https://github.com/CERT-BDF/Cortex/issues/21) **Closed issues:**