Open-source project
ttpreport/ligolo-mp avatar
ttpreport/ligolo-mp

Ligolo-MP turns the Ligolo tunnelling proxy into a shared, multi-operator pivot

Multiplayer pivoting solution

538 stars57 forksGoGPL-3.0

At a glance

What is it?
Ligolo-MP is a Go rewrite of Ligolo-ng with a client-server architecture, so several operators can drive tunnels on one agent at once from a terminal interface, with the TUN devices and routes managed automatically. It ships a generated agent binary with mTLS and an obfuscation option, and it is a pivoting tool for authorised testing.
Who is it for?
Adopt Ligolo-MP if you run red team engagements where several operators need to pivot through one compromised host at the same time, because the client-server split lets more than one person open relays through the same agent and the dashboard shows them all.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 177 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What changed from Ligolo-ng, and what the feature list is really saying

Ligolo-MP is described as an advanced version of Ligolo-ng, with client-server architecture, enabling pentesters to play with multiple concurrent tunnels collaboratively. It manages all your TUNs automatically, while also providing a clean GUI to track everything.

That single sentence contains the whole thesis. Ligolo-ng is a single-operator tool: you run the agent on a machine you have already compromised and the proxy on your own machine, and each operator manages their own tunnels. Ligolo-MP splits that into a server, which is the operator side with the dashboard and the TUN interfaces, and an agent, which runs on the target and forwards traffic. Several operators can connect to the same agent, and each of their relays is tracked separately.

The feature list spells out the consequences. Multiplayer is the first entry. Automatic TUN management removes the part of Ligolo-ng where you set up interfaces by hand. Auto-restoring routing means the routes survive the agent reconnecting. Unlimited concurrent relays means the ceiling is not the number of tunnels but the machine. And listeners are independent redirectors, which is the key architectural property: each listener owns its own redirector, so two people tunnelling to the same host on different ports do not interfere with each other.

Two of the remaining entries are about deployment rather than use. Cross-platform agent compatible with Linux, FreeBSD, MacOS and Windows 7 or later, and dynamic mTLS-enabled agent binaries generation with an obfuscation option, so the binary that lands on the target is generated for that engagement rather than shipped as a fixed artefact.

Routing to the loopback of the target machine removes the need for port forwarding on the target, which is one of the classic problems with pivoting: a service listening only on 127.0.0.1 is unreachable from anywhere else, and the loopback feature makes it reachable through the tunnel.

The proxy support entry mentions SOCKS and HTTP proxy support including SSPI and SPNEGO authentication, which is what you need when the target's internal services authenticate against a Windows domain rather than accepting anonymous requests.

The primary language is Go, the licence is GPL-3.0, the default branch is main, and the last push was on 2026-04-29. The recent releases are v2.2.1 and v2.2.0, both from 2026-03-07, with v2.1.0 from 2025-07-13 before them. The project homepage is ttp.report and the README points to a GitHub wiki for up-to-date information.

The dependency list tells you how the tunnelling works

The go.mod is short and every dependency in it corresponds to a mechanism in the feature list.

github.com/hashicorp/yamux is the multiplexing layer. It runs many logical streams over one TCP connection, which is how a single connection to the agent carries every relay at once. That is also why the feature list can claim unlimited concurrent relays: the limit is the agent's capacity, not the number of TCP sessions, because yamux multiplexes them onto one.

gvisor.dev/gvisor provides the network stack in userspace, which is what lets the tool create and manage network interfaces without needing a particular kernel feature set on the target. The related package golang.org/x/net and golang.org/x/sys are the usual low-level dependencies of that kind of work.

github.com/vishvananda/netlink and golang.org/x/sys/netns are the Linux route and network-namespace plumbing. The auto-restoring routing feature depends on being able to write routing table entries programmatically and to put interfaces into the right namespaces, and netlink is how you talk to the kernel to do that.

The terminal interface is tview and tcell, which is the Go library pair for a full-screen text user interface. The friendly terminal-based GUI in the feature list is those two libraries, and the screenshots in the README's doc/ directory show what that looks like.

Then the service plumbing. google.golang.org/grpc and google.golang.org/protobuf carry the operator to agent protocol, which is the natural fit for a client-server design with strongly typed messages, and the protobuf/ directory at the repository root holds the definitions. github.com/google/uuid and github.com/rs/xid are the identifier generators, and modernc.org/sqlite is a pure-Go SQLite build, which suggests the server keeps its state, the relay list and the agent registry, in a local database rather than in memory.

The module path is github.com/ttpreport/ligolo-mp/v2 with a replace directive pointing at itself, which is the standard pattern for a repository that keeps its internal packages importable under the module path while developing.

How the binaries are built, and why garble is in the Makefile

The Makefile shows the build pipeline, and it is unusual in a way worth understanding.

makefile
GARBLE_VER := 0.14.2
makefile
go:
	# Build go
	cd artifacts && curl -L --output go/$(TARGET_OS)/$(TARGET_ARCH)/go.tar.gz https://dl.google.com/go/go$(GO_VER).$(TARGET_OS)-$(TARGET_ARCH).tar.gz
	cd artifacts/go/$(TARGET_OS)/$(TARGET_ARCH) && tar xvf go.tar.gz

The build downloads a Go toolchain rather than using the one on the machine. GO_VER is pinned to 1.24.0, TARGET_OS defaults to linux and TARGET_ARCH is derived from uname -m with an explicit mapping for aarch64, armv5, armv6, armv7, x86_64, x86, i686 and i386, and you can override it with TARGET_ARCH.

The reason for downloading the toolchain is that the agent binary has to be built for the target platform, and it is simpler to fetch a known toolchain per target than to require a cross-compiler on every builder. The artifacts/ directory at the repository root is where the toolchains and outputs land.

Then garble, which is a Go obfuscator:

makefile
	# Build garble
	cd artifacts/go/$(TARGET_OS)/$(TARGET_ARCH)/go/bin && curl -L --output garble https://github.com/ttpreport/garble/releases/download/v$(GARBLE_VER)/garble_$(TARGET_OS)_$(TARGET_ARCH) && chmod +x garble

Notice the fork. It is not the upstream garble release but one published under the ttpreport organisation, which is the project's own build of the obfuscator. The obfuscation option in the feature list refers to this: the binary that ends up on the target can have its symbols and package paths obscured, so a defender looking at the process list or at the file does not immediately see what it is.

The rest of the Makefile strips the toolchain before bundling, removing the documentation, the tour and the test2json tool from the Go distribution, and the BLOAT_FILES list defines what is excluded from the bundle: the licence and readme files, the doc, test, api and misc directories. The effect is a smaller agent artifact.

The version is injected at link time with LDFLAGS pointing at the internal/version package and APP_VERSION defaulting to v2.2.1, so the binary reports the release it was built from.

Where the deployment scripts sit and what the wiki carries

The repository layout shows the split between the operator side and the agent side, plus the infrastructure around them.

There are cmd/ for the command entry points, internal/ for the server internals and pkg/ for the reusable pieces, protobuf/ for the wire definitions, and artifacts/ for the build outputs. config/ and launch/ are the ROS-like configuration, doc/ holds the images, and the vendor/ directory means the dependencies are checked in rather than fetched at build time.

Three install scripts sit at the root and they map onto the three things you install: install_deps.sh, install_server.sh and install_service.sh. The naming suggests the server, the dependencies and the service unit are separate steps, which is the arrangement you want when the server runs as a service on an operator machine and the agent is generated separately.

There is also a .goreleaser.yaml, so the release binaries are produced by GoReleaser, and the release badge in the README points at a release workflow. That is how the project publishes multi-platform artefacts for the server and the client.

What is not in the repository is the documentation itself. The README says to visit the Wiki for up-to-date information, and the wiki is where the operational detail lives, since this README is a feature list with a link. That is a normal choice for a project with an active user base, and it does mean the repository is not self-contained for someone who wants to understand the protocol or the certificate flow.

The dashboard screenshots referenced as doc/logo.png and doc/dashboard-1.png are the visual summary of what the client-server split looks like in practice: a list of the agents, the relays they carry, and presumably the traffic.

Where Ligolo-MP is the wrong tool

Three limits, and the first is about what this tool assumes you have already done.

The first is that the whole design presumes a foothold. Ligolo-MP is not an exploitation tool and it does not get you the first shell. It is a way to build tunnels through a machine you already control, which means the value of it depends entirely on the step before it, and if you do not have that step, this is the wrong repository and you need a different one. The topic tags say pentesting, pivoting, post-exploitation and redteam, and post-exploitation is exactly the phase this belongs to.

The second is that the multi-operator design has a cost that the single-operator version does not. The agent accepts connections from several operators, which means the agent's authentication is a shared secret distributed among a team, and the mTLS in the feature list is what makes that manageable. It also means the server holds state about other people's relays, and the SQLite in the dependency list implies a database on the operator side holding that picture of who is doing what. If you are working alone, the original Ligolo-ng is simpler, has a smaller attack surface, and does not need a server component running somewhere.

The third is the range of targets. The agent is compatible with Linux, FreeBSD, MacOS and Windows 7 or later, but the routing features, auto-restoring routes and the TUN management are Linux-shaped, because netlink is a Linux interface and the tool is written in Go around that. On a Windows target you have a working agent binary but a different set of behaviours around interfaces and routes, and the README does not describe the differences.

The alternative approach for many engagements is a plain SSH tunnel or SOCKS over SSH, or a simpler static tunnel tool. The difference in approach is that those need nothing on the target beyond an SSH server, whereas Ligolo's agent gives you arbitrary protocol relay and the ability to reach services bound to the target's loopback, which SSH port forwarding cannot do. If your pivot requirement is HTTP and SSH, use SSH. If you need to carry a service that only listens on 127.0.0.1 on the far side, or you need several operators working simultaneously through the same box, that is what this tool is for.

One operational note that matters for anyone deploying it: the obfuscation option reduces what a defender can learn from the artefact, but the traffic still originates from a process on a host you have compromised, and the agent binary is distributed to that host. The obfuscation is for reducing the signal of the tool, not for making the compromise invisible, and anyone using it needs the authorisation to do the pivoting in the first place.

Editorial conclusion

Adopt Ligolo-MP if you run red team engagements where several operators need to pivot through one compromised host at the same time, because the client-server split lets more than one person open relays through the same agent and the dashboard shows them all. Do not adopt it for a single-operator engagement, because the original Ligolo-ng covers that case without a server component to deploy, and do not treat it as a general VPN, since the traffic is tunnelled through an agent you have already compromised. Verify first that the target agent can run the generated binary, since the agent is built per platform with a specific Go version and the architecture mapping in the Makefile, that your operator machine can create the TUN interfaces the tool needs, since netlink is used to manage routes, and that you understand the agent is mutually authenticated with mTLS, so the certificate credentials have to be distributed to the people allowed to use it.

Frequently asked questions

What is Ligolo-MP and how does it differ from Ligolo-ng?

It is an advanced version of Ligolo-ng with a client-server architecture, enabling pentesters to work with multiple concurrent tunnels collaboratively. It manages the TUNs automatically, provides a GUI to track everything, and supports unlimited concurrent relays.

What platforms does the Ligolo-MP agent support?

The cross-platform agent is compatible with Linux, FreeBSD, MacOS and Windows 7 or later. The agent binaries are generated dynamically per platform with mTLS enabled, with an obfuscation option.

How does Ligolo-MP handle conflicts between multiple operators' tunnels?

Listeners are independent redirectors, so each listener owns its own redirector and two operators tunnelling to the same host on different ports do not interfere. Routing is also auto-restoring, so routes come back when the agent reconnects.

What proxy protocols does Ligolo-MP support?

SOCKS and HTTP proxy support, including SSPI and SPNEGO authentication, which is what internal Windows domain services need. It also routes to the loopback of the target machine, so a service bound to 127.0.0.1 on the far side is reachable without port forwarding.

How are the Ligolo-MP binaries built?

The Makefile downloads a pinned Go toolchain per target OS and architecture rather than using the local one, strips documentation and other files from it, and bundles it. A garble fork published by the project is used to obfuscate the agent, and the version is injected at link time, with APP_VERSION defaulting to v2.2.1.

Where is the Ligolo-MP documentation?

The README points to the GitHub wiki for up-to-date information, and the repository itself contains the code, the install scripts for dependencies, the server and the service, the protobuf definitions and the doc directory with the logo and dashboard images.

Official sources

  1. License: GPL-3.0
  2. Project website
  3. README
  4. Releases
  5. ttpreport/ligolo-mp on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ttpreport-ligolo-mp.svg)](https://hysenlabs.com/projects/ttpreport-ligolo-mp)

Was this useful?