From 5735f1443767fd956834a1d3bc4bfae455dd5d96 Mon Sep 17 00:00:00 2001 From: Jurriaan Bremer Date: Sun, 29 Apr 2018 21:06:12 +0200 Subject: [PATCH] allow port redirection in inetsim routing mode --- cuckoo/apps/rooter.py | 31 ++++++++++++++++++++++++++-- cuckoo/common/config.py | 1 + cuckoo/compat/config.py | 1 + cuckoo/core/scheduler.py | 8 ++++--- cuckoo/private/cwd/conf/routing.conf | 5 +++++ tests/test_config.py | 6 +++++- tests/test_scheduler.py | 4 ++-- 7 files changed, 48 insertions(+), 8 deletions(-) diff --git a/cuckoo/apps/rooter.py b/cuckoo/apps/rooter.py index c5890804e..419fb24fb 100644 --- a/cuckoo/apps/rooter.py +++ b/cuckoo/apps/rooter.py @@ -180,8 +180,32 @@ def srcroute_disable(rt_table, ipaddr): run(s.ip, "rule", "del", "from", ipaddr, "table", rt_table) run(s.ip, "route", "flush", "cache") -def inetsim_enable(ipaddr, inetsim_ip, machinery_iface, resultserver_port): +def inetsim_redirect_port(action, srcip, dstip, ports): + """Note that the parameters (probably) mean the opposite of what they + imply; this method adds or removes an iptables rule for redirect traffic + from (srcip, srcport) to (dstip, dstport). + E.g., if 192.168.56.101:80 -> 192.168.56.1:8080, then it redirects + outgoing traffic from 192.168.56.101 to port 80 to 192.168.56.1:8080. + """ + for entry in ports.split(): + if entry.count(":") != 1: + log.debug("Invalid inetsim ports entry: %s", entry) + continue + srcport, dstport = entry.split(":") + if not srcport.isdigit() or not dstport.isdigit(): + log.debug("Invalid inetsim ports entry: %s", entry) + continue + run( + s.iptables, "-t", "nat", action, "PREROUTING", "--source", srcip, + "-p", "tcp", "--syn", "--dport", srcport, + "-j", "DNAT", "--to-destination", "%s:%s" % (dstip, dstport) + ) + +def inetsim_enable(ipaddr, inetsim_ip, machinery_iface, resultserver_port, + ports): """Enable hijacking of all traffic and send it to InetSim.""" + inetsim_redirect_port("-A", ipaddr, inetsim_ip, ports) + run( s.iptables, "-t", "nat", "-A", "PREROUTING", "--source", ipaddr, "-p", "tcp", "--syn", "!", "--dport", resultserver_port, @@ -208,8 +232,11 @@ def inetsim_enable(ipaddr, inetsim_ip, machinery_iface, resultserver_port): run(s.iptables, "-A", "OUTPUT", "-s", ipaddr, "-j", "DROP") -def inetsim_disable(ipaddr, inetsim_ip, machinery_iface, resultserver_port): +def inetsim_disable(ipaddr, inetsim_ip, machinery_iface, resultserver_port, + ports): """Enable hijacking of all traffic and send it to InetSim.""" + inetsim_redirect_port("-D", ipaddr, inetsim_ip, ports) + run( s.iptables, "-D", "PREROUTING", "-t", "nat", "--source", ipaddr, "-p", "tcp", "--syn", "!", "--dport", resultserver_port, "-j", "DNAT", diff --git a/cuckoo/common/config.py b/cuckoo/common/config.py index 6d4384f12..6259d2e18 100644 --- a/cuckoo/common/config.py +++ b/cuckoo/common/config.py @@ -792,6 +792,7 @@ class Config(object): "inetsim": { "enabled": Boolean(False), "server": String("192.168.56.1"), + "ports": String(), }, "tor": { "enabled": Boolean(False), diff --git a/cuckoo/compat/config.py b/cuckoo/compat/config.py index cc18e93f5..fb50674a7 100644 --- a/cuckoo/compat/config.py +++ b/cuckoo/compat/config.py @@ -703,6 +703,7 @@ def _205_206(c): "guacd_port": 4822, } c["virtualbox"]["controlports"] = "5000-5050" + c["routing"]["inetsim"]["ports"] = None return c migrations = { diff --git a/cuckoo/core/scheduler.py b/cuckoo/core/scheduler.py index 30172393f..c13c02e54 100644 --- a/cuckoo/core/scheduler.py +++ b/cuckoo/core/scheduler.py @@ -306,7 +306,8 @@ class AnalysisManager(threading.Thread): "inetsim_enable", self.machine.ip, config("routing:inetsim:server"), config("%s:%s:interface" % (machinery, machinery)), - str(config("cuckoo:resultserver:port")) + str(config("cuckoo:resultserver:port")), + config("routing:inetsim:ports") or "" ) if self.route == "tor": @@ -344,7 +345,7 @@ class AnalysisManager(threading.Thread): "srcroute_disable", self.rt_table, self.machine.ip ) - if self.route != "none": + if self.route == "drop" or self.route == "internet": rooter( "drop_disable", self.machine.ip, config("cuckoo:resultserver:ip"), @@ -357,7 +358,8 @@ class AnalysisManager(threading.Thread): "inetsim_disable", self.machine.ip, config("routing:inetsim:server"), config("%s:%s:interface" % (machinery, machinery)), - str(config("cuckoo:resultserver:port")) + str(config("cuckoo:resultserver:port")), + config("routing:inetsim:ports") or "" ) if self.route == "tor": diff --git a/cuckoo/private/cwd/conf/routing.conf b/cuckoo/private/cwd/conf/routing.conf index 7b1c6d1e0..8315d7fbf 100644 --- a/cuckoo/private/cwd/conf/routing.conf +++ b/cuckoo/private/cwd/conf/routing.conf @@ -49,6 +49,11 @@ drop = {{ routing.routing.drop }} enabled = {{ routing.inetsim.enabled }} server = {{ routing.inetsim.server }} +# Redirect TCP ports (should we also support UDP?). If specified, this should +# represent whitespace-separated src:dst pairs. E.g., "80:8080 443:8080" will +# redirect all 80/443 traffic to 8080 on the specified InetSim host. +ports = {{ routing.inetsim.ports }} + [tor] # Route a VM through Tor, requires a local setup of Tor (please refer to our # documentation). diff --git a/tests/test_config.py b/tests/test_config.py index c55eae658..af46f844e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1140,6 +1140,10 @@ def test_migration_205_206(): machines = vbox1 [vbox1] mode = headless + """) + Files.create(cwd("conf"), "routing.conf", """ +[inetsim] +enabled = yes """) cfg = Config.from_confdir(cwd("conf"), loose=True) cfg = migrate(cfg, "2.0.5", "2.0.6") @@ -1147,8 +1151,8 @@ mode = headless assert cfg["cuckoo"]["remotecontrol"]["enabled"] == False assert cfg["cuckoo"]["remotecontrol"]["guacd_host"] == "localhost" assert cfg["cuckoo"]["remotecontrol"]["guacd_port"] == 4822 - assert cfg["virtualbox"]["controlports"] == "5000-5050" + assert cfg["routing"]["inetsim"]["ports"] is None class FullMigration(object): DIRPATH = None diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py index e76bd673c..20cab432b 100644 --- a/tests/test_scheduler.py +++ b/tests/test_scheduler.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017 Cuckoo Foundation. +# Copyright (C) 2017-2018 Cuckoo Foundation. # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org # See the file 'docs/LICENSE' for copying permission. @@ -134,7 +134,7 @@ def test_route_inetsim(p): assert am.interface is None assert am.rt_table is None p.assert_called_once_with( - "inetsim_enable", "1.2.3.4", "2.3.4.5", "vboxnet0", "2042" + "inetsim_enable", "1.2.3.4", "2.3.4.5", "vboxnet0", "2042", "" ) am.db.set_route.assert_called_once_with(1234, "inetsim")