Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/kevoreilly/CAPEv2
Browse files Browse the repository at this point in the history
  • Loading branch information
doomedraven committed Feb 6, 2025
2 parents 0b8fe34 + 6edbe7f commit 9d48bcd
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 6 deletions.
23 changes: 19 additions & 4 deletions docs/book/src/installation/host/routing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,16 @@ Following is the list of available routing options.
+-------------------------+--------------------------------------------------+
| :ref:`routing_tor` | Routes all traffic through Tor. |
+-------------------------+--------------------------------------------------+
| :ref:`routing_tun` | Route traffic though any "tun" interface |
+-------------------------+--------------------------------------------------+
| :ref:`routing_vpn` | Routes all traffic through one of perhaps |
| | multiple pre-defined VPN endpoints. |
+-------------------------+--------------------------------------------------+
| :ref:`routing_socks` | Routes all traffic through one of perhaps |
| | multiple pre-defined VPN endpoints. |
+-------------------------+--------------------------------------------------+


Using Per-Analysis Network Routing
==================================

Expand Down Expand Up @@ -358,6 +361,18 @@ correctly.

.. _`latest stable version of Tor here`: https://www.torproject.org/docs/debian.html.en


.. _routing_tun:

Tun Routing
^^^^^^^^^^^
This allows you to route via any ``tun`` interface. You can pass the tun
interface name on demand per analysis. The interface name can be ``tunX``
or ``tun_foo``. This assumes you create the tunnel inferface outside of CAPE.

Then you set the ``route=tun_foo`` on the ``/apiv2/tasks/create/file/``
API call.

.. _routing_vpn:

VPN Routing
Expand Down Expand Up @@ -454,13 +469,13 @@ VPN persistence & auto-restart `source`_::
6. Reload the daemons:
# sudo systemctl daemon-reload

1. Start the OpenVPN service:
7. Start the OpenVPN service:
# sudo systemctl start openvpn

2. Test if it is working by checking the external IP:
8. Test if it is working by checking the external IP:
# curl ifconfig.co

3. If curl is not installed:
9. If curl is not installed:
# sudo apt install curl

.. _`source`: https://www.ivpn.net/knowledgebase/linux/linux-autostart-openvpn-in-systemd-ubuntu/
Expand Down Expand Up @@ -568,7 +583,7 @@ Assuming you already have any VM running, to test the internet connection using
$ sudo python3 router_manager.py -r internet -e --vm-name win1 --verbose
$ sudo python3 router_manager.py -r internet -d --vm-name win1 --verbose

The ``-e`` flag is used to enable a route and ``-d`` is used to disable it. You can read more about all the options the utility has by running::
The ``-e`` flag is used to enable a route and ``-d`` is used to disable it. You can read more about all the options the utility has by running::

$ sudo python3 router_manager.py -h

Expand Down
16 changes: 16 additions & 0 deletions lib/cuckoo/core/analysis_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

# os.listdir('/sys/class/net/')
HAVE_NETWORKIFACES = False

try:
import psutil

Expand All @@ -43,6 +44,12 @@
latest_symlink_lock = threading.Lock()


def is_network_interface(intf: str):
global network_interfaces
network_interfaces = list(psutil.net_if_addrs().keys())
return intf in network_interfaces


class CuckooDeadMachine(Exception):
"""Exception thrown when a machine turns dead.
Expand Down Expand Up @@ -536,6 +543,9 @@ def route_network(self):
self.rt_table = vpns[self.route].rt_table
elif self.route in self.socks5s:
self.interface = ""
elif self.route[:3] == "tun" and is_network_interface(self.route):
# tunnel interface starts with "tun" and interface exists on machine
self.interface = self.route
else:
self.log.warning("Unknown network routing destination specified, ignoring routing for this analysis: %s", self.route)
self.interface = None
Expand Down Expand Up @@ -583,6 +593,9 @@ def route_network(self):

elif self.route in ("none", "None", "drop"):
self.rooter_response = rooter("drop_enable", self.machine.ip, str(self.cfg.resultserver.port))
elif self.route[:3] == "tun" and is_network_interface(self.route):
self.log.info("Network interface {} is tunnel", self.interface)
self.rooter_response = rooter("interface_route_tun_enable", self.machine.ip, self.route, str(self.task.id))

self._rooter_response_check()

Expand Down Expand Up @@ -714,6 +727,9 @@ def unroute_network(self):

elif self.route in ("none", "None", "drop"):
self.rooter_response = rooter("drop_disable", self.machine.ip, str(self.cfg.resultserver.port))
elif self.route[:3] == "tun":
self.log.info("Disable tunnel interface {}", self.interface)
self.rooter_response = rooter("interface_route_tun_disable", self.machine.ip, self.route, str(self.task.id))

self._rooter_response_check()

Expand Down
7 changes: 5 additions & 2 deletions modules/processing/analysisinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,34 @@
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.

import os
import codecs
import logging
import os
import time
from contextlib import suppress
from datetime import datetime
from pathlib import Path

from lib.cuckoo.common.abstracts import Processing
from lib.cuckoo.common.constants import CUCKOO_VERSION, CUCKOO_ROOT
from lib.cuckoo.common.constants import CUCKOO_ROOT, CUCKOO_VERSION
from lib.cuckoo.common.exceptions import CuckooProcessingError
from lib.cuckoo.common.path_utils import path_exists
from lib.cuckoo.common.utils import get_options
from lib.cuckoo.core.database import Database

# https://stackoverflow.com/questions/14989858/get-the-current-git-hash-in-a-python-script/68215738#68215738

log = logging.getLogger(__name__)

db = Database()


def get_running_commit() -> str:
git_folder = Path(CUCKOO_ROOT, ".git")
head_name = Path(git_folder, "HEAD").read_text().split("\n")[0].split(" ")[-1]
return Path(git_folder, head_name).read_text().replace("\n", "")


CAPE_CURRENT_COMMIT_HASH = get_running_commit()


Expand Down
90 changes: 90 additions & 0 deletions utils/rooter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import argparse
import errno
import grp
import ipaddress
import json
import logging.handlers
import os
Expand Down Expand Up @@ -47,6 +48,49 @@ def run(*args):
return stdout, stderr


def get_tun_peer_address(interface_name):
"""Gets the peer address of a tun interface.
Args:
interface_name: The name of the tun interface (e.g., "tun0").
Returns:
The peer IP address as a string, or None if an error occurs. Returns None if the interface does not exist, or does not have a peer.
"""
try:
result = subprocess.run(["ip", "addr", "show", interface_name], capture_output=True, text=True, check=True)
output = result.stdout

for line in output.splitlines():
if "peer" in line:
parts = line.split()
if len(parts) > 1: # Check if there's a second element to avoid IndexError
peer_with_cidr = parts[1]
try:
# Handle CIDR notation using ipaddress library
peer_ip = ipaddress.ip_interface(peer_with_cidr).ip.exploded
return peer_ip
except ValueError: # Handle invalid CIDR notations
try:
peer_ip = peer_with_cidr.split("/")[0] # Try just splitting by /
return peer_ip
except IndexError:
return None # Invalid format - give up.
else:
return None # No peer address found on the line.
return None # "peer" not found in the output

except subprocess.CalledProcessError as e:
if e.returncode == 1: # Interface not found
return None
else:
print(f"Error executing ip command: {e}")
return None
except FileNotFoundError:
print("ip command not found. Is iproute2 installed?")
return None


def enable_ip_forwarding(sysctl="/usr/sbin/sysctl"):
log.debug("Enabling IPv4 forwarding")
run(sysctl, "-w" "net.ipv4.ip_forward=1")
Expand Down Expand Up @@ -641,6 +685,50 @@ def inetsim_disable(ipaddr, inetsim_ip, dns_port, resultserver_port, ports):
run_iptables("-D", "OUTPUT", "--source", ipaddr, "-j", "DROP")


def interface_route_tun_enable(ipaddr: str, out_interface: str, task_id: str):
"""Enable routing and NAT via tun output_interface."""
log.info(f"Enabling interface routing via: {out_interface} for task: {task_id}")

# mark packets from analysis VM
run_iptables("-t", "mangle", "-I", "PREROUTING", "--source", ipaddr, "-j", "MARK", "--set-mark", task_id)

run_iptables("-t", "nat", "-I", "POSTROUTING", "--source", ipaddr, "-o", out_interface, "-j", "MASQUERADE")
# ACCEPT forward
run_iptables("-t", "filter", "-I", "FORWARD", "--source", ipaddr, "-o", out_interface, "-j", "ACCEPT")

# in routing table add route table task_id
run(s.ip, "rule", "add", "fwmark", task_id, "lookup", task_id)

peer_ip = get_tun_peer_address(out_interface)
if peer_ip:
log.info(f"interface_route_enable {out_interface} has peer {peer_ip}")
run(s.ip, "route", "add", "default", "via", peer_ip, "table", task_id)
else:
log.error("interface_route_enable missing peer IP ")


def interface_route_tun_disable(ipaddr: str, out_interface: str, task_id: str):
"""Disable routing and NAT via tun output_interface."""
log.info(f"Disable interface routing via: {out_interface} for task: {task_id}")

# mark packets from analysis VM
run_iptables("-t", "mangle", "-D", "PREROUTING", "--source", ipaddr, "-j", "MARK", "--set-mark", task_id)

run_iptables("-t", "nat", "-D", "POSTROUTING", "--source", ipaddr, "-o", out_interface, "-j", "MASQUERADE")
# ACCEPT forward
run_iptables("-t", "filter", "-D", "FORWARD", "--source", ipaddr, "-o", out_interface, "-j", "ACCEPT")

# in routing table add route table task_id
run(s.ip, "rule", "del", "fwmark", task_id, "lookup", task_id)

peer_ip = get_tun_peer_address(out_interface)
if peer_ip:
log.info(f"interface_route_disable {out_interface} has peer {peer_ip}")
run(s.ip, "route", "del", "default", "via", peer_ip, "table", task_id)
else:
log.error("interface_route_disable missing peer IP ")


def socks5_enable(ipaddr, resultserver_port, dns_port, proxy_port):
"""Enable hijacking of all traffic and send it to socks5."""
log.info("Enabling socks route.")
Expand Down Expand Up @@ -750,6 +838,8 @@ def drop_disable(ipaddr, resultserver_port):
"srcroute_disable": srcroute_disable,
"inetsim_enable": inetsim_enable,
"inetsim_disable": inetsim_disable,
"interface_route_tun_enable": interface_route_tun_enable,
"interface_route_tun_disable": interface_route_tun_disable,
"socks5_enable": socks5_enable,
"socks5_disable": socks5_disable,
"drop_enable": drop_enable,
Expand Down

0 comments on commit 9d48bcd

Please sign in to comment.