diff --git a/.gitignore b/.gitignore index c5becab..2f4794c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ *.Forgefile-* *.o/ coverage.info +**/binds/py/*.so +**/binds/py/macgonuts.c +**/binds/py/build diff --git a/doc/BINDS.md b/doc/BINDS.md new file mode 100644 index 0000000..25277e5 --- /dev/null +++ b/doc/BINDS.md @@ -0,0 +1,86 @@ +# Macgonuts binds + +**Abstract**: This document is intended to explain how to use the available `macgonuts` binds. +Details about how to build is not discussed here, take a look at `doc/BUILD.md`. + +## Topics + +- [What is available until now](#what-is-available-until-now) +- [Using `macgonuts_pybind`](#using-macgonuts_pybind) + +## What is available until now + +Currently it is only available two function that acts as basic building blocks for managing +spoofing attacks. + +Those two function are: + +- `macgonuts_spoof()` +- `macgonuts_undo_spoof()` + +By using those two functions through the binds, you will be able to easily implement the +spoof stuff at your own program natively without depeding on `macgonuts` command line tool. + +Until now `macgonuts` features binds for `Python`. + +[``Back``](#topics) + +## Using `macgonuts_pybind` + +Once it build and well-installed, it is fairly simple to use `macgonuts_pyind` module. +The functions present in this module are: + +- `macgonuts_spoof()` +- `macgonuts_undo_spoof()` + +The `macgonuts_spoof()` function can receive five arguments: + +- `lo_iface` is the name of the interface you are accessing the network. +- `target_addr` is the network address of the target, it can be a `IPv4` or `IPv6` address. +- `addr2spoof` is the address that will be spoofed at target, it can be a `IPv4` or `IPv6` address. +- `fake_pkts_amount` is the total of spoofed packets sent to target, it defaults to one. +- `timeout` is the timeout in `mss` between a spoofed packet and the next, it defauts to no timeout. + +The `macgonuts_undo_spoof()` undoes a previous promoted spoof attack against a specific target. +This function expects three arguments: + +- `lo_iface` is the name of the interface you are accessing the network. +- `target_addr` is the network address of the target, it can be a `IPv4` or `IPv6` address. +- `addr2spoof` is the address that was spoofed at target, it can be a `IPv4` or `IPv6` address. + +Follows the general idea when using `macgonuts` spoofing primitives from `Python`: + +```python +import macgonuts_pybind + +(...) + +# INFO(Rafael): Send one fake ARP packet to 192.168.5.142. +if macgonuts_pybind.macgonuts_spoof('eth0', '192.168.5.142', '192.168.5.1') != 0: + print('error when trying to spoof.\n'); + (...) + +(...) + +# INFO(Rafael): Send 200 fake NDP packets to dead::beef:1 at each 500 mss. +if macgonuts_pybind.macgonuts_spoof('eth1', + 'dead::beef::8e', + 'dead::beef:1', 200, 500) != 0: + print('error when trying to spoof.\n'); + (...) + +(...) + +# INFO(Rafael): Now undoing all promoted spoofing attacks. +if macgonuts_undo_spoof('eth0', '192.168.5.142', '192.168.5.1') != 0: + print('unable to undo spoof attack done from eth0') + (...) + +if macgonuts_undo_spoof('eth1', 'dead::beef:8e', 'dead::beef:1') != 0: + print('unable to undo spoof attack done from eth1') + (...) + +(...) +``` + +[``Back``](#topics) diff --git a/doc/BUILD.md b/doc/BUILD.md index 366573d..9de7943 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -16,6 +16,8 @@ fresh ``macgonuts`` binary to get your stuff done, you can give ``the low-cost b - [List of all build options](#list-of-all-build-options) - [Installing the command line tool](#installing-the-command-line-tool) - [Building the debian package](#building-the-debian-package) + - [Building the binds](#building-the-binds) + - [pybind](#pybind) ## Getting newest macgonuts source code revision @@ -243,3 +245,37 @@ you@somewhere-over-the-rainbow:~/macgonuts/src# make deb ``` [``Back``](#topics) + +## Building the binds + +In this part you can find instructions about how to build the available ``macgonuts`` binds. + +[``Back``](#topics) + +### pybind + +The ``Macgonuts`` `Python`` bind depends on ``cython``, so in order to install it you can use: + +``` +you@somewhere-over-the-rainbow:~/macgonuts/src# pip install cython +``` + +Done! Now is time to actually build ``macgonuts_pybind``. + +By using the ``developer's`` build and being into ``src`` sub-directory, you need to invoke ``Hefesto`` passing +the option ``--with-pybind``: + +``` +you@somewhere-over-the-rainbow:~/macgonuts/src# hefesto --with-pybind +``` + +When using the ``low-cost`` build you also need to be into ``src`` sub-directory and call ``make`` defining the +build parameter ``with-pybind``: + +``` +you@somewhere-over-the-rainbow:~/macgonuts/src# make with-pybind +``` + +The ``python`` bind artifacts will be built into ``src/binds/py``. + +[``Back``](#topics) diff --git a/doc/CodeOrganization.md b/doc/CodeOrganization.md index 9c1d832..cf6fd15 100644 --- a/doc/CodeOrganization.md +++ b/doc/CodeOrganization.md @@ -38,7 +38,9 @@ This is the current repo layout: | +-- ... +-- src/ | +-- Forgefile.hsl -| +-- ... +| | +-- ... +| +-- binds/ +| | +-- ... | +-- build/ | +-- toolsets.hsl | +-- cmd/ @@ -66,24 +68,25 @@ This is the current repo layout: In order to know more details about each directory level take a look at **Table 1**. **Table 1**: Directory levels overview. -| **Directory level** | **Here goes** | -|:----------------------:|:------------------------------------------------:| -| toplevel | Main configuration and information files | -| ``.github`` | Files related to github configuration | -| ``.github/workspaces`` | Configuration of ``CI`` stuff | -| ``doc`` | More specific documentation | -| ``doc/man1`` | ``Macgonuts`` tool man page | -| ``etc`` | Miscellaneous stuff | -| ``src`` | Main source (library) | -| ``src/build`` | Build conveniences stuff | -| ``src/cmd`` | Command line tool source code | -| ``src/cmd/hooks`` | Source code for task hooks of ``CLI`` tool | -| ``src/cmd/test`` | Tests for the ``CLI`` tool | -| ``src/freebsd`` | ``FreeBSD`` native code of main source (library) | -| ``src/libs`` | All dependencies used by ``Macgonuts`` code | -| ``src/linux`` | ``Linux`` native code of main source (library) | -| ``src/test`` | Tests for the library | -| ``src/unix`` | Common ``UNIX-like`` codes | +| **Directory level** | **Here goes** | +|:----------------------:|:---------------------------------------------------:| +| toplevel | Main configuration and information files | +| ``.github`` | Files related to github configuration | +| ``.github/workspaces`` | Configuration of ``CI`` stuff | +| ``doc`` | More specific documentation | +| ``doc/man1`` | ``Macgonuts`` tool man page | +| ``etc`` | Miscellaneous stuff | +| ``src`` | Main source (library) | +| ``src/binds`` | ``Macgonuts`` binds for other programming languages | +| ``src/build`` | Build conveniences stuff | +| ``src/cmd`` | Command line tool source code | +| ``src/cmd/hooks`` | Source code for task hooks of ``CLI`` tool | +| ``src/cmd/test`` | Tests for the ``CLI`` tool | +| ``src/freebsd`` | ``FreeBSD`` native code of main source (library) | +| ``src/libs`` | All dependencies used by ``Macgonuts`` code | +| ``src/linux`` | ``Linux`` native code of main source (library) | +| ``src/test`` | Tests for the library | +| ``src/unix`` | Common ``UNIX-like`` codes | [``Back``](#topics) diff --git a/doc/README.md b/doc/README.md index 3d5c1d2..ed43e05 100644 --- a/doc/README.md +++ b/doc/README.md @@ -2,6 +2,7 @@ This directory is aimed to more specific documentation. Get your destiny below: -- Do you are wanting to build ``Macgonuts``? Read [BUILD.md](BUILD.md). -- Do you are wanting to learn how to use ``Macgonuts``? Read [MANUAL.md](MANUAL.md). -- Do you are wanting to start coding some stuff? Read [CodeOrganization.md](CodeOrganization.md) and [Codingstyle.md](Codingstyle.md). +- Are you wanting to build ``Macgonuts``? Read [BUILD.md](BUILD.md). +- Are you wanting to learn how to use ``Macgonuts``? Read [MANUAL.md](MANUAL.md). +- Are you wanting to start coding some stuff? Read [CodeOrganization.md](CodeOrganization.md) and [Codingstyle.md](Codingstyle.md). +- Are you wanting to use ``Macgonuts`` spoofing building blocks into your own stuff? Read [BINDS.md](BINDS.md). diff --git a/src/Forgefile.hsl b/src/Forgefile.hsl index 3a4a3b9..c06664f 100644 --- a/src/Forgefile.hsl +++ b/src/Forgefile.hsl @@ -33,6 +33,12 @@ macgonuts-static-lib.prologue() { $includes = hefesto.sys.get_option("includes"); $cflags = hefesto.sys.get_option("cflags"); + var option type list; + $option = hefesto.sys.get_option("with-pybind"); + if ($option.count() > 0) { + # INFO(Rafael): It is necessary to build macgonuts_pybind .so stuff. + $cflags.add_item("-fPIC"); + } $libraries = hefesto.sys.get_option("libraries"); $ldflags = hefesto.sys.get_option("ldflags"); @@ -71,8 +77,12 @@ macgonuts-static-lib.epilogue() { var subprojects type list; $subprojects.add_item("test"); $subprojects.add_item("cmd"); + var option type list; + $option = hefesto.sys.get_option("with-pybind"); + if ($option.count() > 0) { + $subprojects.add_item("pybind"); + } if (build_projects($subprojects) == 0) { - var option type list; $option = hefesto.sys.get_option("coverage"); if ($option.count() > 0) { var report_path type string; diff --git a/src/GNUmakefile b/src/GNUmakefile index d9f8633..21a0593 100644 --- a/src/GNUmakefile +++ b/src/GNUmakefile @@ -15,6 +15,13 @@ NATIVE_SRC = $(shell uname -s | tr '[:upper:]' '[:lower:]') LDFLAGS=-lpthread -laccacia CFLAGS=-Ilibs/accacia/src -I. +LIBACCACIA_CFLAGS= + +ifdef with-pybind + CFLAGS += -fPIC + LIBACCACIA_CFLAGS += -fPIC +endif + SOURCES := $(wildcard *.c) SOURCES += $(wildcard unix/*.c) SOURCES += $(wildcard $(NATIVE_SRC)/*.c) @@ -24,12 +31,16 @@ MACGONUTS_SHARE_DIR = /usr/local/share/macgonuts all: setup build_accacia $(BINARY) cd cmd && $(MAKE) +ifdef with-pybind + ar -r $(LIBDIR)/libmacgonutssock.a + cd binds/py && rm -rf build && rm -f macgonuts.c && python setup.py build_ext --inplace +endif setup: @mkdir -p .o build_accacia: - cd libs/accacia/src && $(CC) -I. -c accacia.c && mkdir -p ../lib &&\ + cd libs/accacia/src && $(CC) -I. -c accacia.c $(LIBACCACIA_CFLAGS) && mkdir -p ../lib &&\ $(AR) -r ../lib/libaccacia.a accacia.o && rm accacia.o && cd ../../.. $(BINARY): $(OBJDIR)/$(OBJECTS) diff --git a/src/binds/py/macgonuts.pyx b/src/binds/py/macgonuts.pyx new file mode 100644 index 0000000..335c546 --- /dev/null +++ b/src/binds/py/macgonuts.pyx @@ -0,0 +1,53 @@ +# +# Copyright (c) 2023, Rafael Santiago +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. +# + +""" Macgonuts general spoofing utilities binds for Python """ + +cdef extern from "macgonuts_pybind.h": + int macgonuts_pybind_spoof(char *lo_iface, char *target_addr, char *addr2spoof, + int fake_pkts_amount, int timeout); + +cdef extern from "macgonuts_pybind.h": + int macgonuts_pybind_undo_spoof(char *lo_iface, char *target_addr, char *addr2spoof); + +def macgonuts_spoof(lo_iface, target_addr, addr2spoof, fake_pkts_amount = 1, timeout = 0): + """The python wrapper for macgonuts_spoof() C function + + By using this function you can easily promote a spoofing attack based on IPv4 or IPv6. + + This function receives: + + - the local interface label (lo_iface) + - the target IPv4/IPv6 address (target_addr) + - the IPv4/IPv6 address which will be spoofed at the target host (addr2spoof) + - the amount of fake address resolution packets to be sent (fake_pkts_amount, its default is one packet only) + - the timeout amount between the current packet and the next (timeout, its default is no timeout) + + It returns zero on success and non-zero value on failure, besides writing some error description to stderr. + """ + return macgonuts_pybind_spoof(bytes(lo_iface, 'ascii'), + bytes(target_addr, 'ascii'), + bytes(addr2spoof, 'ascii'), + fake_pkts_amount, timeout) + +def macgonuts_undo_spoof(lo_iface, target_addr, addr2spoof): + """The python wrapper for macgonuts_undo_spoof() C function + + By using this function you can easily undo a previous promoted spoofing attack based on IPv4 or IPV6. + + This function receives: + + - the local interface used during the spoofing attack (lo_iface) + - the target IPv4/IPv6 address of the spoofing attack (target_addr) + - the IPv4/IPv6 address which was spoofed at the target host (addr2spoof) + + It returns zero on success and non-zero value on failure, besides writing some error description to stderr. + """ + return macgonuts_pybind_undo_spoof(bytes(lo_iface, 'ascii'), + bytes(target_addr, 'ascii'), + bytes(addr2spoof, 'ascii')) diff --git a/src/binds/py/macgonuts_pybind.c b/src/binds/py/macgonuts_pybind.c new file mode 100644 index 0000000..f521c7b --- /dev/null +++ b/src/binds/py/macgonuts_pybind.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023, Rafael Santiago + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +#include +#include +#include +#include + +int macgonuts_pybind_spoof(char *lo_iface, char *target_addr, char *addr2spoof, + int fake_pkts_amount, int timeout) { + macgonuts_socket_t rsk = -1; + struct macgonuts_spoof_layers_ctx layers; + int err = EXIT_FAILURE; + int f; + int timeout_mss; + + if (lo_iface == NULL + || target_addr == NULL + || addr2spoof == NULL || fake_pkts_amount <= 0) { + macgonuts_si_error("invalid argument(s) passed to macgonuts_pybind_spoof().\n"); + return EXIT_FAILURE; + } + + rsk = macgonuts_create_socket(lo_iface, 1); + + if (rsk == -1) { + macgonuts_si_error("unable to create socket.\n"); + return EXIT_FAILURE; + } + + memset(&layers, 0, sizeof(layers)); + + err = macgonuts_get_spoof_layers_info(rsk, &layers, + target_addr, strlen(target_addr), + addr2spoof, strlen(addr2spoof), lo_iface); + if (err != EXIT_SUCCESS) { + macgonuts_si_error("unable to fill up spoofing layers context.\n"); + goto macgonuts_pybind_spoof_epilogue; + } + + timeout_mss = timeout * 1000; + + for (f = 0; f < fake_pkts_amount && err == EXIT_SUCCESS; f++) { + err = macgonuts_spoof(rsk, &layers); + if (err != EXIT_SUCCESS) { + macgonuts_si_error("when trying to spoof.\n"); + continue; + } + if (timeout_mss > 0) { + usleep(timeout_mss); + } + } + + macgonuts_release_spoof_layers_ctx(&layers); + +macgonuts_pybind_spoof_epilogue: + + macgonuts_release_socket(rsk); + + return err; +} + +int macgonuts_pybind_undo_spoof(char *lo_iface, char *target_addr, char *addr2spoof) { + int err = EXIT_FAILURE; + macgonuts_socket_t rsk = -1; + struct macgonuts_spoof_layers_ctx layers; + + if (lo_iface == NULL + || target_addr == NULL + || addr2spoof == NULL) { + macgonuts_si_error("invalid argument(s) passed to macgonuts_pybind_undo_spoof().\n"); + return EXIT_FAILURE; + } + + rsk = macgonuts_create_socket(lo_iface, 1); + + if (rsk == -1) { + macgonuts_si_error("unable to create socket.\n"); + return EXIT_FAILURE; + } + + memset(&layers, 0, sizeof(layers)); + + err = macgonuts_get_spoof_layers_info(rsk, &layers, + target_addr, strlen(target_addr), + addr2spoof, strlen(addr2spoof), lo_iface); + if (err != EXIT_SUCCESS) { + macgonuts_si_error("unable to fill up spoofing layers context.\n"); + goto macgonuts_pybind_undo_spoof_epilogue; + } + + err = macgonuts_undo_spoof(rsk, &layers); + + macgonuts_release_spoof_layers_ctx(&layers); + +macgonuts_pybind_undo_spoof_epilogue: + + macgonuts_release_socket(rsk); + + return err; +} diff --git a/src/binds/py/macgonuts_pybind.h b/src/binds/py/macgonuts_pybind.h new file mode 100644 index 0000000..36bde27 --- /dev/null +++ b/src/binds/py/macgonuts_pybind.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023, Rafael Santiago + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +#ifndef MACGONTUS_BINDS_PY_MACGONUTS_PYBIND_H +#define MACGONUTS_BINDS_PY_MACGONUTS_PYBIND_H 1 + +int macgonuts_pybind_spoof(char *lo_iface, char *target_addr, char *addr2spoof, + int fake_pkts_amount, int timeout); + +int macgonuts_pybind_undo_spoof(char *lo_iface, char *target_addr, char *addr2spoof); + +#endif // MACGONUTS_BINDS_PY_MACGONUTS_PYBIND_H diff --git a/src/binds/py/setup.py b/src/binds/py/setup.py new file mode 100644 index 0000000..cbff57b --- /dev/null +++ b/src/binds/py/setup.py @@ -0,0 +1,20 @@ +# +# Copyright (c) 2023, Rafael Santiago +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. +# +from distutils.core import setup +from distutils.extension import Extension +from Cython.Build import cythonize + +setup( + name = 'macgonuts_pybind', + ext_modules=cythonize([ + Extension("macgonuts_pybind", ["macgonuts.pyx", "macgonuts_pybind.c"], + include_dirs=['../..'], + library_dirs=['../../../lib','../../libs/accacia/lib'], + libraries=['macgonuts', 'macgonutssock','accacia']), + ]), +) diff --git a/src/build/toolsets.hsl b/src/build/toolsets.hsl index 7a9ffab..315f291 100644 --- a/src/build/toolsets.hsl +++ b/src/build/toolsets.hsl @@ -490,3 +490,29 @@ local function build_debian_pkg() : result type int { mktree(hefesto.sys.make_path(get_rootdir(), "../deb")); result hefesto.sys.run("dpkg-buildpackage -uc -us --pre-clean -j1 && mv ../macgonuts*.* ../deb"); } + +local function build_pybind() : result type int { + var oldcwd type string; + $oldcwd = hefesto.sys.pwd(); + + if (hefesto.sys.cd("binds/py") != 1) { + hefesto.sys.echo("ERROR: Unable to find pybind implementation.\n"); + result 1; + } + + hefesto.sys.echo("*** Now building macgonuts_pybind...\n"); + + var err type int; + $err = hefesto.sys.run("rm -rf build && rm -f macgonuts.c && " + + "python setup.py build_ext --inplace"); + + hefesto.sys.cd($oldcwd); + + if ($err == 0) { + hefesto.sys.echo("*** success.\n"); + } else { + hefesto.sys.echo("*** failure.\n"); + } + + result $err; +}