From 1616e5492d3dd290d12cc4b5546899a3ae9b66e8 Mon Sep 17 00:00:00 2001 From: rafael-santiago Date: Mon, 4 Sep 2023 22:06:22 -0300 Subject: [PATCH] Implement golang bind This commit implements the macgonuts golang bind. Besides build tasks (--with-gobind, with-gobind=yes) related to it and some documentation. Building and usage. A little sample, too. --- doc/BINDS.md | 79 ++++++++++++++++++++++++++++ doc/BUILD.md | 21 +++++++- src/Forgefile.hsl | 4 ++ src/GNUmakefile | 5 ++ src/binds/go/sample/main.go | 60 ++++++++++++++++++++++ src/binds/go/v1/go.mod | 3 ++ src/binds/go/v1/macgonuts.go | 82 +++++++++++++++++++++++++++++ src/binds/macgonuts_binds.c | 91 +++++++++++++++++++++++++++++++++ src/binds/macgonuts_binds.h | 16 ++++++ src/binds/py/macgonuts_pybind.c | 79 ++-------------------------- src/binds/py/setup.py | 2 +- src/build/toolsets.hsl | 25 +++++++++ 12 files changed, 390 insertions(+), 77 deletions(-) create mode 100644 src/binds/go/sample/main.go create mode 100644 src/binds/go/v1/go.mod create mode 100644 src/binds/go/v1/macgonuts.go create mode 100644 src/binds/macgonuts_binds.c create mode 100644 src/binds/macgonuts_binds.h diff --git a/doc/BINDS.md b/doc/BINDS.md index 25277e5..1e39f50 100644 --- a/doc/BINDS.md +++ b/doc/BINDS.md @@ -7,6 +7,7 @@ Details about how to build is not discussed here, take a look at `doc/BUILD.md`. - [What is available until now](#what-is-available-until-now) - [Using `macgonuts_pybind`](#using-macgonuts_pybind) +- [Using `macgonuts` from `Golang`](#using-macgonuts-from-golang) ## What is available until now @@ -84,3 +85,81 @@ if macgonuts_undo_spoof('eth1', 'dead::beef:8e', 'dead::beef:1') != 0: ``` [``Back``](#topics) + +## Using `macgonuts` from `Golang` + +Similar to `Python`'s bind you have the two basic building blocks to promote a spoofing attack: + +- `Spoof()` function +- `UndoSpoof()` function + +The `Spoof()` function expects the following arguments: + +- `loIface` is the name of the interface you are accessing the network. +- `targetAddr` 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. +- `fakePktsAmount` 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. + +Since `Golang` does not have default arguments, when you pass `fakePktsAmount` as zero it will infer +that you want one single fake packet. + +The `UndoSpoof()` function expects the following arguments: + +- `loIface` is the name of the interface you are accessing the network. +- `targetAddr` 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. + +When those function succeed they return `nil` when not, an error is returned and more details are +provided on `stderr`, too. + +The bind also implements a `Version()` function that returns a `string` related to the version of the bind. +In general it follows the main version of the project. + +This is the main idea on using `Macgonuts` from a `Go` code: + +```go +package main + +import ( + "fmt" + "os" + // INFO(Rafael): Personally I find this way of importing things in Golang kind of naive + // and bit stupid. Because you are linking your stuff to on-line stuff from other + // people that at some point in the future can remove this, cut access, whatever. + // In other words, you have zero control about it. If you lost your local go + // installation, maybe in a future attempt of recompiling the stuff you can + // be surprised of how external world does not give a sh_t to your dependencies + // and you.... If the package is provided by a person, instead of a company, it is + // even worse. But even companies can easily vanish away as whole or at least with + // some technologies as the years go by. Thus, if you are intending to do good engineering + // by writing code that can be built do not mattering where and when... You must consider + // my unpopular, "profanus", maybe heretic point.... + "github.com/rafael-santiago/macgonuts/binds/go/v1" + // Instead, the better would be cloning the Macgonuts sources and embed it into your + // own code, by importing as follows (supposing you are into `binds/go/my-new-blau`: + "../v1" + // Cool off, the stuff is BSD-Licensed! ;) +) + +func main() { + fmt.Printf("Spoofing...\n") + err := macgonuts.Spoof("eth0", "192.168.5.142", "192.168.5.1", 100, 100) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Printf("Done!\nNow Undoing the spoof...\n") + err = macgonuts.UndoSpoof("eth0", "192.168.5.142", "192.168.5.1") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Printf("Done!\n") + os.Exit(0) +} +``` + +You also can check a more complete sample at `src/binds/go/sample`. + +[``Back``](#topics) diff --git a/doc/BUILD.md b/doc/BUILD.md index 4b70a2f..90b0a55 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -18,6 +18,7 @@ fresh ``macgonuts`` binary to get your stuff done, you can give ``the low-cost b - [Building the debian package](#building-the-debian-package) - [Building the binds](#building-the-binds) - [pybind](#pybind) + - [gobind](#gobind) ## Getting newest macgonuts source code revision @@ -208,6 +209,8 @@ Take a look at **Table 1** to know all build options supported by the ``Hefesto` |``--genhtml-outpath``| value | Specifies a file path for the ``LCOV`` coverage report | | ``--toolset`` | value | Specifies the name of wanted compiler, can being ``gcc`` or ``clang`` | | ``--debian-pkg`` | flag | Runs the debian packaging build task | +| ``--with-pybind`` | flag | Includes ``Python``'s bind compilation in the main build task | +| ``--with-gobind`` | flag | Includes ``Golang``'s bind compilation in the main build task | [``Back``](#topics) @@ -273,9 +276,25 @@ When using the ``low-cost`` build you also need to be into ``src`` sub-directory build parameter ``with-pybind``: ``` -you@somewhere-over-the-rainbow:~/macgonuts/src# make with-pybind +you@somewhere-over-the-rainbow:~/macgonuts/src# make with-pybind=yes ``` The ``python`` bind artifacts will be built into ``src/binds/py``. [``Back``](#topics) + +### gobind + +In order to build ``Macgonuts`` ``Golang`` bind by using the ``developer's`` build you need to invoke ``Hefesto`` passing +the option ``--with-gobind`` (supposing you are into ``src`` sub-directory): + +``` +you@somewhere-over-the-rainbow:~/macgonuts/src# hefesto --with-gobind +``` + +If you want to use ``low-cost`` build instead, also being into ``src`` sub-directory, call ``make`` defining the build +parameter ``with-gobind``: + +``` +you@somewhere-over-the-rainbow:~/macgonuts/src# make with-gobind=yes +``` diff --git a/src/Forgefile.hsl b/src/Forgefile.hsl index c06664f..7e79d38 100644 --- a/src/Forgefile.hsl +++ b/src/Forgefile.hsl @@ -82,6 +82,10 @@ macgonuts-static-lib.epilogue() { if ($option.count() > 0) { $subprojects.add_item("pybind"); } + $option = hefesto.sys.get_option("with-gobind"); + if ($option.count() > 0) { + $subprojects.add_item("gobind"); + } if (build_projects($subprojects) == 0) { $option = hefesto.sys.get_option("coverage"); if ($option.count() > 0) { diff --git a/src/GNUmakefile b/src/GNUmakefile index 21a0593..d3a372b 100644 --- a/src/GNUmakefile +++ b/src/GNUmakefile @@ -22,6 +22,8 @@ ifdef with-pybind LIBACCACIA_CFLAGS += -fPIC endif +CURR_GOBIND_VERSION = v1 + SOURCES := $(wildcard *.c) SOURCES += $(wildcard unix/*.c) SOURCES += $(wildcard $(NATIVE_SRC)/*.c) @@ -35,6 +37,9 @@ 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 +ifdef with-gobind + cd binds/go/$(CURR_GOBIND_VERSION) && go build +endif setup: @mkdir -p .o diff --git a/src/binds/go/sample/main.go b/src/binds/go/sample/main.go new file mode 100644 index 0000000..30752bc --- /dev/null +++ b/src/binds/go/sample/main.go @@ -0,0 +1,60 @@ +// +// 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. +// +// INFO(Rafael): If you want to build this sample just run `go build` +package main + +import ( + "../v1" + "fmt" + "os" + "strconv" +) + +func main() { + if len(os.Args) < 4 { + fmt.Fprintf(os.Stderr, "use: %s [ ]", + os.Args[0]) + os.Exit(1) + } + var packetsTotal int = 0 + var err error = nil + if len(os.Args) > 4 { + packetsTotal, err = strconv.Atoi(os.Args[4]) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + if packetsTotal < 0 { + fmt.Fprintf(os.Stderr, "error: packets-total must be a positive integer.\n") + os.Exit(1) + } + } + var timeoutInMss int = 0 + if len(os.Args) > 5 { + timeoutInMss, err = strconv.Atoi(os.Args[5]) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + if timeoutInMss < 0 { + fmt.Fprintf(os.Stderr, "error: timeout must be a positive integer.\n") + os.Exit(1) + } + } + err = macgonuts.Spoof(os.Args[1], os.Args[2], os.Args[3], packetsTotal, timeoutInMss) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + err = macgonuts.UndoSpoof(os.Args[1], os.Args[2], os.Args[3]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + os.Exit(0) +} diff --git a/src/binds/go/v1/go.mod b/src/binds/go/v1/go.mod new file mode 100644 index 0000000..8ed3cbf --- /dev/null +++ b/src/binds/go/v1/go.mod @@ -0,0 +1,3 @@ +module github.com/rafael-santiago/macgonuts/src/binds/go/v1 + +go 1.15 diff --git a/src/binds/go/v1/macgonuts.go b/src/binds/go/v1/macgonuts.go new file mode 100644 index 0000000..199726f --- /dev/null +++ b/src/binds/go/v1/macgonuts.go @@ -0,0 +1,82 @@ +// +// 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. +// +package macgonuts + +/* +#cgo CFLAGS: -I../../.. +#cgo LDFLAGS: -L../../../../lib -L../../../libs/accacia/lib -lmacgonuts -lmacgonutssock -laccacia +#include +#include +*/ +import "C" +import "unsafe" + +import ( + "fmt" +) + +// Indicates the current version of the bind stuff. Always it is directly +// linked to Macgonuts main release version. +const kMacgonutsGoBindVersion string = "v1" + +// The Golang bind for macgonuts_spoof() function from libmacgonuts. +// By using this function you can easily promote a spoofing attack based on IPv4 or IPv6. It receives: +// - the local interface label (loIface) +// - the target IPv4/IPv6 address (targetAddr) +// - the IPv4/IPv6 address which will be spoofed at the target host (addr2Spoof) +// - the amount of fake address resolution packets to be sent (fakePktsAmount, when zero it defaults to one) +// - the timeout amount between the current packet and the next (timeout, its default is no timeout) +// It returns nil on success and an error on failure, besides writing some error description to stderr. +func Spoof(loIface, targetAddr, addr2Spoof string, fakePktsAmount, timeout int) error { + if len(loIface) == 0 || len(targetAddr) == 0 || len(addr2Spoof) == 0 { + return fmt.Errorf("invalid argument(s) passed to MacgonutsSpoof().") + } + lo_iface := C.CString(loIface) + defer C.free(unsafe.Pointer(lo_iface)) + target_addr := C.CString(targetAddr) + defer C.free(unsafe.Pointer(target_addr)) + addr2spoof := C.CString(addr2Spoof) + defer C.free(unsafe.Pointer(addr2spoof)) + var fake_pkts_amount C.int = 1 + if fakePktsAmount > 0 { + fake_pkts_amount = C.int(fakePktsAmount) + } + if C.macgonuts_binds_spoof(lo_iface, + target_addr, + addr2spoof, fake_pkts_amount, C.int(timeout)) != 0 { + return fmt.Errorf("error when spoofing.") + } + return nil +} + +// The Golang bind for macgonuts_undo_spoof() function from libmacgonuts. +// By using this function you can easily undo a previous promoted spoofing attack based on IPv4 or IPV6. It receives: +// - the local interface used during the spoofing attack (loIface) +// - the target IPv4/IPv6 address of the spoofing attack (targetAddr) +// - 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. +func UndoSpoof(loIface, targetAddr, addr2Spoof string) error { + if len(loIface) == 0 || len(targetAddr) == 0 || len(addr2Spoof) == 0 { + return fmt.Errorf("invalid argument(s) passed to MacgonutsSpoof().") + } + lo_iface := C.CString(loIface) + defer C.free(unsafe.Pointer(lo_iface)) + target_addr := C.CString(targetAddr) + defer C.free(unsafe.Pointer(target_addr)) + addr2spoof := C.CString(addr2Spoof) + defer C.free(unsafe.Pointer(addr2spoof)) + if C.macgonuts_binds_undo_spoof(lo_iface, target_addr, addr2spoof) != 0 { + return fmt.Errorf("error when undoing spoof.") + } + return nil +} + +// Returns the version of the bind stuff. +func Version() string { + return kMacgonutsGoBindVersion +} diff --git a/src/binds/macgonuts_binds.c b/src/binds/macgonuts_binds.c new file mode 100644 index 0000000..aadaabd --- /dev/null +++ b/src/binds/macgonuts_binds.c @@ -0,0 +1,91 @@ +/* + * 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_binds_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; + + 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_binds_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_binds_spoof_epilogue: + + macgonuts_release_socket(rsk); + + return err; +} + +int macgonuts_binds_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; + + 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_binds_undo_spoof_epilogue; + } + + err = macgonuts_undo_spoof(rsk, &layers); + + macgonuts_release_spoof_layers_ctx(&layers); + +macgonuts_binds_undo_spoof_epilogue: + + macgonuts_release_socket(rsk); + + return err; +} diff --git a/src/binds/macgonuts_binds.h b/src/binds/macgonuts_binds.h new file mode 100644 index 0000000..2d18d2c --- /dev/null +++ b/src/binds/macgonuts_binds.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 MACGONUTS_BINDS_MACGONUTS_BINDS_H +#define MACGONUTS_BINDS_MACGONUTS_BINDS_H 1 + +int macgonuts_binds_spoof(char *lo_iface, char *target_addr, char *addr2spoof, + int fake_pkts_amount, int timeout); + +int macgonuts_binds_undo_spoof(char *lo_iface, char *target_addr, char *addr2spoof); + +#endif // MACGONUTS_BINDS_MACGONUTS_BINDS_H diff --git a/src/binds/py/macgonuts_pybind.c b/src/binds/py/macgonuts_pybind.c index f521c7b..e8bd8ef 100644 --- a/src/binds/py/macgonuts_pybind.c +++ b/src/binds/py/macgonuts_pybind.c @@ -6,18 +6,12 @@ * LICENSE file in the root directory of this source tree. */ #include -#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) { @@ -25,50 +19,10 @@ int macgonuts_pybind_spoof(char *lo_iface, char *target_addr, char *addr2spoof, 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; + return macgonuts_binds_spoof(lo_iface, target_addr, addr2spoof, fake_pkts_amount, timeout); } 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) { @@ -76,30 +30,5 @@ int macgonuts_pybind_undo_spoof(char *lo_iface, char *target_addr, char *addr2sp 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; + return macgonuts_binds_undo_spoof(lo_iface, target_addr, addr2spoof); } diff --git a/src/binds/py/setup.py b/src/binds/py/setup.py index cbff57b..e5bc09f 100644 --- a/src/binds/py/setup.py +++ b/src/binds/py/setup.py @@ -12,7 +12,7 @@ setup( name = 'macgonuts_pybind', ext_modules=cythonize([ - Extension("macgonuts_pybind", ["macgonuts.pyx", "macgonuts_pybind.c"], + Extension("macgonuts_pybind", ["macgonuts.pyx", "macgonuts_pybind.c", "../macgonuts_binds.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 315f291..c5c7be3 100644 --- a/src/build/toolsets.hsl +++ b/src/build/toolsets.hsl @@ -516,3 +516,28 @@ local function build_pybind() : result type int { result $err; } + +local function build_gobind() : result type int { + var oldcwd type string; + $oldcwd = hefesto.sys.pwd(); + + if (hefesto.sys.cd("binds/go/v1") != 1) { + hefesto.sys.echo("ERROR: Unable to find gobind implementation.\n"); + result 1; + } + + hefesto.sys.echo("*** Now building macgonuts golang bind...\n"); + + var err type int; + $err = hefesto.sys.run("go build\n"); + + hefesto.sys.cd($oldcwd); + + if ($err == 0) { + hefesto.sys.echo("*** success.\n"); + } else { + hefesto.sys.echo("*** failure.\n"); + } + + result $err; +}