summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler St. Onge <tylertstonge@gmail.com>2020-07-25 22:38:14 -0400
committerTyler St. Onge <tylertstonge@gmail.com>2020-07-25 22:38:14 -0400
commit747b6af76b23650756811d896bf76b4331419784 (patch)
tree2e942c8296567cbe4d57f205f72029a62829bdda
parent945332ca057383f258c78fd15cbc22f8b8d58a83 (diff)
refine ftp component and add configuration capabilities
-rw-r--r--.idea/modules/honeypot-build.iml4
-rw-r--r--.idea/modules/honeypot.iml27
-rw-r--r--build.sbt6
-rw-r--r--config.json9
-rw-r--r--src/main/scala/com/tylerstonge/honeypot/Main.scala7
-rw-r--r--src/main/scala/com/tylerstonge/honeypot/Supervisor.scala32
-rw-r--r--src/main/scala/com/tylerstonge/honeypot/ftp/FtpHandler.scala29
-rw-r--r--src/main/scala/com/tylerstonge/honeypot/ftp/FtpListener.scala6
-rw-r--r--src/main/scala/com/tylerstonge/honeypot/http/HttpListener.scala27
-rw-r--r--src/main/scala/com/tylerstonge/honeypot/http/SimplisticHandler.scala11
-rw-r--r--src/main/scala/com/tylerstonge/honeypot/messages/MStartComponent.scala7
-rw-r--r--src/test/scala/com/tylerstonge/honeypot/ftp/FtpHandlerTest.scala48
12 files changed, 149 insertions, 64 deletions
diff --git a/.idea/modules/honeypot-build.iml b/.idea/modules/honeypot-build.iml
index 725673b..5767c79 100644
--- a/.idea/modules/honeypot-build.iml
+++ b/.idea/modules/honeypot-build.iml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<module external.linked.project.id="honeypot-build" external.linked.project.path="$MODULE_DIR$/../../project" external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" sbt.imports="_root_.sbt.Keys._, _root_.sbt.ScriptedPlugin.autoImport._, _root_.sbt._, _root_.sbt.nio.Keys._, _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.ScriptedPlugin, _root_.sbt.plugins.SbtPlugin, _root_.sbt.plugins.SemanticdbPlugin, _root_.sbt.plugins.JUnitXmlReportPlugin, _root_.sbt.plugins.Giter8TemplatePlugin, _root_.scala.xml.{TopScope=&amp;gt;SUB:DOLLARscope}" sbt.resolvers="https://repo1.maven.org/maven2/|maven|public, /home/dropkick/.ivy2/cache|ivy|Local cache, file:/home/dropkick/.sbt/preloaded|maven|local-preloaded" type="SBT_MODULE" version="4">
+<module external.linked.project.id="honeypot-build" external.linked.project.path="$MODULE_DIR$/../../project" external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" sbt.imports="_root_.sbt.Keys._, _root_.sbt.ScriptedPlugin.autoImport._, _root_.sbt._, _root_.sbt.nio.Keys._, _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.ScriptedPlugin, _root_.sbt.plugins.SbtPlugin, _root_.sbt.plugins.SemanticdbPlugin, _root_.sbt.plugins.JUnitXmlReportPlugin, _root_.sbt.plugins.Giter8TemplatePlugin, _root_.scala.xml.{TopScope=&amp;gt;SUB:DOLLARscope}" sbt.resolvers="file:/$USER_HOME$/.sbt/preloaded|maven|local-preloaded, https://repo1.maven.org/maven2/|maven|public, C:\Users\dropkick\.ivy2\cache|ivy|Local cache" type="SBT_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/../../project/target/idea-classes" />
<output-test url="file://$MODULE_DIR$/../../project/target/idea-test-classes" />
@@ -108,7 +108,7 @@
</orderEntry>
</component>
<component name="SbtModule">
- <option name="buildForURI" value="file:$MODULE_DIR$/../../" />
+ <option name="buildForURI" value="file:/$MODULE_DIR$/../../" />
<option name="imports" value="_root_.sbt.Keys._, _root_.sbt.ScriptedPlugin.autoImport._, _root_.sbt._, _root_.sbt.nio.Keys._, _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.ScriptedPlugin, _root_.sbt.plugins.SbtPlugin, _root_.sbt.plugins.SemanticdbPlugin, _root_.sbt.plugins.JUnitXmlReportPlugin, _root_.sbt.plugins.Giter8TemplatePlugin, _root_.scala.xml.{TopScope=&gt;SUB:DOLLARscope}" />
</component>
</module> \ No newline at end of file
diff --git a/.idea/modules/honeypot.iml b/.idea/modules/honeypot.iml
index e735fa7..a948c8e 100644
--- a/.idea/modules/honeypot.iml
+++ b/.idea/modules/honeypot.iml
@@ -1,18 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
-<module external.linked.project.id="honeypot [file:/home/dropkick/dev/honeypot/honeypot/]" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" type="JAVA_MODULE" version="4">
+<module external.linked.project.id="honeypot [file:/C:/Users/dropkick/Documents/dev/honeypot/]" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/../../target/scala-2.13/classes" />
<output-test url="file://$MODULE_DIR$/../../target/scala-2.13/test-classes" />
<exclude-output />
<content url="file://$MODULE_DIR$/../..">
<sourceFolder url="file://$MODULE_DIR$/../../src/main/scala" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/../../src/test/scala" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/../../target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
- <orderEntry type="library" name="sbt: com.typesafe.akka:akka-actor_2.13:2.6.6:jar" level="project" />
+ <orderEntry type="library" name="sbt: com.typesafe.akka:akka-actor_2.13:2.6.8:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: com.typesafe.akka:akka-testkit_2.13:2.6.8:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe:config:1.4.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang.modules:scala-java8-compat_2.13:0.9.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scala-lang.modules:scala-xml_2.13:1.2.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang:scala-library:2.13.1:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scala-lang:scala-reflect:2.13.1:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalactic:scalactic_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-compatible:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-core_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-diagrams_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-featurespec_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-flatspec_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-freespec_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-funspec_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-funsuite_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-matchers-core_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-mustmatchers_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-propspec_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-refspec_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-shouldmatchers_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest-wordspec_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest_2.13:3.2.0:jar" level="project" />
+ <orderEntry type="library" name="sbt: org.typelevel:jawn-ast_2.13:1.0.0:jar" level="project" />
+ <orderEntry type="library" name="sbt: org.typelevel:jawn-parser_2.13:1.0.0:jar" level="project" />
+ <orderEntry type="library" name="sbt: org.typelevel:jawn-util_2.13:1.0.0:jar" level="project" />
</component>
</module> \ No newline at end of file
diff --git a/build.sbt b/build.sbt
index 04e6aec..7a7a1ff 100644
--- a/build.sbt
+++ b/build.sbt
@@ -4,4 +4,8 @@ name := "honeypot"
organization := "com.tylerstonge"
version := "1.0"
-libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.6.6"
+libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.6.8"
+libraryDependencies += "org.typelevel" %% "jawn-ast" % "1.0.0"
+
+libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.0" % Test
+libraryDependencies += "com.typesafe.akka" %% "akka-testkit" % "2.6.8" % Test \ No newline at end of file
diff --git a/config.json b/config.json
new file mode 100644
index 0000000..792f931
--- /dev/null
+++ b/config.json
@@ -0,0 +1,9 @@
+{
+ "components": [
+ {
+ "name": "ftp-listener",
+ "type": "ftp",
+ "port": 21
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/main/scala/com/tylerstonge/honeypot/Main.scala b/src/main/scala/com/tylerstonge/honeypot/Main.scala
index 3c01fc8..0d47de4 100644
--- a/src/main/scala/com/tylerstonge/honeypot/Main.scala
+++ b/src/main/scala/com/tylerstonge/honeypot/Main.scala
@@ -1,9 +1,8 @@
package com.tylerstonge.honeypot
import akka.actor.{ActorSystem, Props}
-import com.tylerstonge.honeypot.ftp.FtpListener
object Main extends App {
- val system = ActorSystem("hello-system")
- val listener = system.actorOf(Props[FtpListener], name = "ftp-listener")
-}
+ val system = ActorSystem("honeypot_system")
+ val listener = system.actorOf(Props[Supervisor], name = "honeypot-supervisor")
+} \ No newline at end of file
diff --git a/src/main/scala/com/tylerstonge/honeypot/Supervisor.scala b/src/main/scala/com/tylerstonge/honeypot/Supervisor.scala
index a04a6e8..ee41ea2 100644
--- a/src/main/scala/com/tylerstonge/honeypot/Supervisor.scala
+++ b/src/main/scala/com/tylerstonge/honeypot/Supervisor.scala
@@ -1,14 +1,38 @@
package com.tylerstonge.honeypot
-import akka.actor.Actor
+import java.nio.file.Paths
+
+import akka.actor.{Actor, Props}
+import akka.event.{Logging, LoggingAdapter}
+import com.tylerstonge.honeypot.ftp.FtpListener
+import com.tylerstonge.honeypot.messages.MStartComponent
+import org.typelevel.jawn.ast.{JArray, JParser}
class Supervisor extends Actor {
- override def receive: Receive = {
- case _ => println("kk dood")
+ val log: LoggingAdapter = Logging(context.system, this)
+
+ override def preStart: Unit = {
+ val cfg = JParser.parseFromFile(Paths.get("config.json").toFile).get
+ cfg.get("components").asInstanceOf[JArray].vs.foreach(c => {
+ self ! MStartComponent(c.get("name").asString, c.get("type").asString, c.get("port").asInt)
+ })
}
override def postStop {
- println("Supervisor::postStop")
+ log.debug("supervisor is shutting down")
+ }
+
+ override def receive: Receive = {
+ case msg: MStartComponent => startComponent(msg)
+ case _ => log.debug("supervisor received unhandled message")
+ }
+
+ private def startComponent(msg: MStartComponent) {
+ log.info("starting component :: {}", msg.name)
+ msg.ctype match {
+ case "ftp" => context.actorOf(Props(new FtpListener(msg.port)), name = msg.name)
+ case _ => log.error("unknown component type: ", msg.ctype, msg.name);
+ }
}
} \ No newline at end of file
diff --git a/src/main/scala/com/tylerstonge/honeypot/ftp/FtpHandler.scala b/src/main/scala/com/tylerstonge/honeypot/ftp/FtpHandler.scala
index 8a55396..48db3f5 100644
--- a/src/main/scala/com/tylerstonge/honeypot/ftp/FtpHandler.scala
+++ b/src/main/scala/com/tylerstonge/honeypot/ftp/FtpHandler.scala
@@ -6,22 +6,31 @@ import akka.io.Tcp.{PeerClosed, Received, Write}
import akka.util.ByteString
class FtpHandler extends Actor {
+
val log: LoggingAdapter = Logging(context.system, this)
override def receive: Receive = {
- case Received(data) =>
- log.info(">> {}", data.utf8String)
- sender() ! Write(ByteString.apply(parse(data.utf8String)))
+ case Received(data) => sender() ! Write(ByteString.apply(parse(sanitize(data))))
case PeerClosed =>
- log.info("closing connection")
+ log.info("peer closed connection")
context.stop(self)
}
- def parse(msg: String): String = msg match {
- case "USER anonymous\n" => "331 Please specify password.\n"
- case "PASS password\n" => "230 Login successful.\n"
- case "PWD\n" => "257 \"/\" is the current directory\n"
- case "QUIT\n" => "221 Goodbye.\n"
- case _ => "200 sure\n"
+ def parse(msg: Array[String]): String = msg(0) match {
+ case "user" =>
+ log.info("attempted login with username: {}", msg(1))
+ "331 Please specify password.\n"
+ case "pass" =>
+ log.info("attempted login with password: {}", msg(1))
+ "230 Login successful.\n"
+ case "pwd" => "257 \"/\" is the current directory\n"
+ case "quit" => "221 Goodbye.\n"
+ case _ =>
+ log.info("unsupported command received: {}", msg.mkString(" "))
+ "451 Requested action aborted. Local error in processing.\n"
+ }
+
+ def sanitize(data: ByteString): Array[String] = {
+ data.utf8String.trim.toLowerCase().split(" ")
}
}
diff --git a/src/main/scala/com/tylerstonge/honeypot/ftp/FtpListener.scala b/src/main/scala/com/tylerstonge/honeypot/ftp/FtpListener.scala
index 3e6606a..b988ba3 100644
--- a/src/main/scala/com/tylerstonge/honeypot/ftp/FtpListener.scala
+++ b/src/main/scala/com/tylerstonge/honeypot/ftp/FtpListener.scala
@@ -8,16 +8,16 @@ import akka.io.Tcp._
import akka.io.{IO, Tcp}
import akka.util.ByteString
-class FtpListener extends Actor {
+class FtpListener (port: Int) extends Actor {
val log: LoggingAdapter = Logging(context.system, this)
- IO(Tcp)(context.system) ! Bind(self, new InetSocketAddress("localhost", 2121))
+ IO(Tcp)(context.system) ! Bind(self, new InetSocketAddress("localhost", port))
override def receive: Receive = {
case Bound(localAddress) =>
log.info("listening on {}", localAddress)
case CommandFailed(_: Bind) => context.stop(self)
- case Connected(remote, local) =>
+ case Connected =>
val handler = context.actorOf(Props[FtpHandler])
val connection = sender()
connection ! Register(handler)
diff --git a/src/main/scala/com/tylerstonge/honeypot/http/HttpListener.scala b/src/main/scala/com/tylerstonge/honeypot/http/HttpListener.scala
deleted file mode 100644
index 8943ee0..0000000
--- a/src/main/scala/com/tylerstonge/honeypot/http/HttpListener.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.tylerstonge.honeypot.http
-
-import java.net.InetSocketAddress
-
-import akka.actor.{Actor, Props}
-import akka.event.Logging
-import akka.io.Tcp._
-import akka.io.{IO, Tcp}
-
-
-class HttpListener extends Actor {
- val log = Logging(context.system, this)
-
- import context.system
-
- IO(Tcp) ! Bind(self, new InetSocketAddress("localhost", 7333))
-
- override def receive: Receive = {
- case b@Bound(localAddress) => context.parent ! b
- case CommandFailed(_: Bind) => context.stop(self)
- case c@Connected(remote, local) =>
- val handler = context.actorOf(Props[SimplisticHandler])
- val connection = sender()
- connection ! Register(handler)
- }
-
-} \ No newline at end of file
diff --git a/src/main/scala/com/tylerstonge/honeypot/http/SimplisticHandler.scala b/src/main/scala/com/tylerstonge/honeypot/http/SimplisticHandler.scala
deleted file mode 100644
index 2fe1409..0000000
--- a/src/main/scala/com/tylerstonge/honeypot/http/SimplisticHandler.scala
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tylerstonge.honeypot.http
-
-import akka.actor.Actor
-import akka.io.Tcp.{PeerClosed, Received, Write}
-
-class SimplisticHandler extends Actor {
- def receive: Receive = {
- case Received(data) => sender() ! Write(data)
- case PeerClosed => context.stop(self)
- }
-}
diff --git a/src/main/scala/com/tylerstonge/honeypot/messages/MStartComponent.scala b/src/main/scala/com/tylerstonge/honeypot/messages/MStartComponent.scala
new file mode 100644
index 0000000..e4109b0
--- /dev/null
+++ b/src/main/scala/com/tylerstonge/honeypot/messages/MStartComponent.scala
@@ -0,0 +1,7 @@
+package com.tylerstonge.honeypot.messages
+
+/**
+ *
+ * @author Tyler St. Onge <tylertstonge@gmail.com>
+ */
+case class MStartComponent (name: String, ctype: String, port: Int)
diff --git a/src/test/scala/com/tylerstonge/honeypot/ftp/FtpHandlerTest.scala b/src/test/scala/com/tylerstonge/honeypot/ftp/FtpHandlerTest.scala
new file mode 100644
index 0000000..bc9b799
--- /dev/null
+++ b/src/test/scala/com/tylerstonge/honeypot/ftp/FtpHandlerTest.scala
@@ -0,0 +1,48 @@
+package com.tylerstonge.honeypot.ftp
+
+import akka.actor.{ActorSystem, Props}
+import akka.io.Tcp.{Received, Write}
+import akka.testkit.{ImplicitSender, TestKit}
+import akka.util.ByteString
+import org.scalatest.BeforeAndAfterAll
+import org.scalatest.matchers.should.Matchers
+import org.scalatest.wordspec.AnyWordSpecLike
+
+/**
+ *
+ * @author Tyler St. Onge <tylertstonge@gmail.com>
+ */
+class FtpHandlerTest extends TestKit(ActorSystem("honeypot-system")) with ImplicitSender with AnyWordSpecLike with Matchers with BeforeAndAfterAll {
+
+ override def afterAll: Unit = {
+ TestKit.shutdownActorSystem(system)
+ }
+
+ "An FtpHandler actor" must {
+ "return 331 in response to USER" in {
+ val handler = system.actorOf(Props[FtpHandler])
+ handler ! Received(ByteString("USER anonymous"))
+ val msg = expectMsgType[Write]
+ assert(msg.data.utf8String.startsWith("331"))
+ }
+ "return 230 in response to PASS" in {
+ val handler = system.actorOf(Props[FtpHandler])
+ handler ! Received(ByteString("PASS password"))
+ val msg = expectMsgType[Write]
+ assert(msg.data.utf8String.startsWith("230"))
+ }
+ "return 257 in response to PWD" in {
+ val handler = system.actorOf(Props[FtpHandler])
+ handler ! Received(ByteString("PWD"))
+ val msg = expectMsgType[Write]
+ assert(msg.data.utf8String.startsWith("257"))
+ }
+ "return 221 in response to QUIT" in {
+ val handler = system.actorOf(Props[FtpHandler])
+ handler ! Received(ByteString("quit"))
+ val msg = expectMsgType[Write]
+ assert(msg.data.utf8String.startsWith("221"))
+ }
+ }
+
+}