metasploit-framework icon indicating copy to clipboard operation
metasploit-framework copied to clipboard

Reverse Port Forward Can't Listen on Localhost

Open Invoke-Mimikatz opened this issue 2 years ago • 3 comments

Steps to reproduce

  1. Get a Meterpreter shell (I used windows/x64/meterpreter/reverse_tcp but it should not matter)
  2. Start Python HTTP server on MSF host: python3 -m http.server 8080
  3. Add a reverse port forward portfwd add -R -l 8080 -L 127.0.0.1 -p 2222 -r 127.0.0.1
  4. Observe the inaccurate message saying the reverse listener is open on 127.0.0.1:2222
  5. Use netstat to confirm the listener is actually open on 0.0.0.0 😢
image

Environment: Meterpreter executed on Windows 10 Enterprise, 20H2 OS Build 19042.1110

Were you following a specific guide/tutorial or reading documentation?

Nope

Expected behavior

Meterpreter should listen on whatever address it's given (in this case, the loopback address).

Current behavior

Meterpreter listens on 0.0.0.0 instead.

Metasploit version

Framework: 6.2.28-dev-39da40e4b533bd55e2479f3d44cd9fe41b140b60 Console : 6.2.28-dev-39da40e4b533bd55e2479f3d44cd9fe41b140b60

Invoke-Mimikatz avatar Nov 19 '22 16:11 Invoke-Mimikatz

Few additional notes since troubleshooting this issue:

The first issue seems to be missing Ruby code to allow sending of a localHost value for the Windows machine to listen on:

# Add additional checks to make sure rhost is set ...

            channel = client.net.socket.create(
              Rex::Socket::Parameters.new(
                'LocalPort' => rport,
                'LocalHost' => rhost, # <-- include this line to pass localHost to the Meterpreter side
                'Proto'     => 'tcp',
                'Server'    => true
              )
            )

and possibly also here

            # Start the local TCP reverse relay in association with this stream
            relay = service.start_reverse_tcp_relay(channel,
              'LocalPort'         => channel.params.localport,
              'LocalHost'         => channel.params.localhost, # <-- Add this line to avoid defaulting to 0.0.0.0
              'PeerHost'          => lhost,
              'PeerPort'          => lport,
              'MeterpreterRelay'  => true)

https://github.com/rapid7/metasploit-framework/blob/fa125e19430d7907471edc8d46d2920b7e1d2ffd/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb#L454-L458

In the Metasploit-Payloads code, a logic bug exists that only allows the code which sets the localHost to the specified value (instead of defaulting to 0.0.0.0) if the system is XP, and falls back to V4 support only.

		if (v4Fallback)
		{
			struct sockaddr_in* v4Addr = (struct sockaddr_in*)&sockAddr;
			v4Addr->sin_addr.s_addr = localHost == NULL ? htons(INADDR_ANY) : inet_addr(localHost); // <-- localHost is set *HERE*
			v4Addr->sin_family = AF_INET;
			v4Addr->sin_port = htons(localPort);
			sockAddrSize = sizeof(struct sockaddr_in);
		}

https://github.com/rapid7/metasploit-payloads/blob/ffa80978321fe399b02b759479bba06ecc7241d1/c/meterpreter/source/extensions/stdapi/server/net/socket/tcp_server.c#L329-L336

Invoke-Mimikatz avatar Nov 19 '22 18:11 Invoke-Mimikatz

So there's two issues here.

  1. The portfwd command does not pass the LocalHost for binding to Rex API which then sends a default to the remote Meterpreter instance
  2. The remote Meterpreter instance ignores whatever LocalHost is sent to it by the lower-level Rex API. Additionally, different Meterpreters do different things when starting TCP server sockets.

We're going to fix number 2, it's going to require some changes to the Meterpreters which will take some time because each will need to be tested. I've got the code for the Python and Windows Meterpreters done that I'll submit in the next day or two. We're going to normalize the functoinality such that when creating a TCP server socket, the Meterpreters will all honor the bind address information. When no bind address is sent (either it's omitted entirely or it is an empty string) then the Meterpreter will bind to all available addresses, both IPv4 and IPv6. Right now Mettle and the Windows Meterpreters are binding to all IPv4 and IPv6 addresses while Python is binding to all IPv4 unless an explicit address was set.

Unfortunately, we're not going to fix issue number 1. We're concerned that it will break instances where users are passing a specific address but relying on the existing (incorrect) behavior of most Meterpreters ignoring it and binding to everything. I've updated my PR to reflect this, we're going to throw a warning in portfwd when used with -r so users know it's ignored, thus retaining the original functionality but at least printing a warning so users know whats happening.

Once the Meterpreter updates are in place, I'll send you a work around so you can start a portforward that's bound to an explicit address, bypassing the aforementioned UI-imposed restriction.

smcintyre-r7 avatar Dec 07 '22 19:12 smcintyre-r7

Hi!

This issue has been left open with no activity for a while now.

We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 30 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request.

github-actions[bot] avatar Jan 09 '23 15:01 github-actions[bot]

Now that #685 is landed, all of the Meterpreters behave consistently and will bind to the specified address.

smcintyre-r7 avatar Nov 22 '23 16:11 smcintyre-r7