PowerShell / Win32-OpenSSH

Win32 port of OpenSSH
7.36k stars 757 forks source link

Windows <-> Linux SSH tunnels result in connection errors #2218

Closed Octoberfest7 closed 6 months ago

Octoberfest7 commented 6 months ago

Prerequisites

Steps to reproduce

Traffic sent via SSH tunnels opened between Linux and Windows machines seems to be malformed. I have tried two different syntaxes:

  1. From Windows, run ssh -vvv -R 9050 user@Linux to connect to an ssh server on the Linux machine and open port 9050 on the Linux machine that dynamically forwards traffic through the Windows machine.
  2. From Linux, run ssh -vvv -D 9050 user@Windows to connect to an ssh server on the Windows machine and open port 9050 on the Linux machine that dynamically forwards traffic through the Windows machine.

I have tested this with the latest Windows OpenSSH version available via winget, OpenSSH_for_Windows_9.5p1, LibreSSL 3.8.2, and 'OpenSSH_9.6p1 Debian-3, OpenSSL 3.1.4 24` on the Linux side.

My specific use scenario was creating a dynamic port forward between a Windows and Linux machine, and then running tooling like Nmap, ssh, and smbclient through the port opened on the Linux machine using Proxychains.

I first became aware of this issue when Nmap returned all scanned ports as open. Other tools like ssh and smbclient worked as expected, so I initially thought this was an issue with Nmap. However upon further examination, the same behavior is occuring with ALL of the mentioned tools, but only manifesting in a tool-breaking error in Nmap.

The issue can be seen in the debug output from the ssh -vvv -R 9050 user@Linux command when a tool is ran via proxy chains (this particular screenshot was Nmap scanning port 445): image

Contrast with the output when a Linux <-> Linux tunnel is created using the same commands and the same Nmap scan is ran: image

The first specific error is:

debug3: socketio_getsockopt - ERROR:10022
debug1: getsockopt TCP_NODELAY: Invalid argument
debug1: connect_next: connect host 192.168.1.160 ([192.168.1.160]:445) in progress, fd=4

I further confirmed this was NOT an issue with Proxychains by removing proxychains from the equation and doing a direct port forward, e.g. from Windows ssh -vvv -R 9050:forwardip:22 and then on Linux using it via ssh user@127.0.0.1 -p 9050. The same error was observed.

I then replaced the SSH tunnel with Chisel, which was successfully able to run Nmap using proxychains and output an accurate result.

This post discusses a similar (but different) issue where the same error seen above is encountered, but this time because the tunnel tries to route traffic via IPV6. In the answer, the author discusses that the next line (connect_next...) is actually the pertinent one, as it appears that the TCP_NODELAY argument is flagged as an invalid argument as a result of the attempted connection failing. I did a few Wireshark captures and was able to notice a difference.

In the Windows <-> Linux capture, the initial SYN sent by the Windows device from the tunnel to port 445 on the target machine receives a RST-ACK: image

The full wireshark info for the first SYN packet is:

Frame 157: 66 bytes on wire (528 bits), 66 bytes captured (528 bits) on interface \Device\NPF_{63A1277B-EB63-4E10-A7F0-96ED2A1F8F24}, id 0
    Interface id: 0 (\Device\NPF_{63A1277B-EB63-4E10-A7F0-96ED2A1F8F24})
        Interface name: \Device\NPF_{63A1277B-EB63-4E10-A7F0-96ED2A1F8F24}
        Interface description: Ethernet 2
    Encapsulation type: Ethernet (1)
    Arrival Time: Mar 21, 2024 20:46:05.180350000 Eastern Daylight Time
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1711068365.180350000 seconds
    [Time delta from previous captured frame: 0.002666000 seconds]
    [Time delta from previous displayed frame: 0.002666000 seconds]
    [Time since reference or first frame: 21.807352000 seconds]
    Frame Number: 157
    Frame Length: 66 bytes (528 bits)
    Capture Length: 66 bytes (528 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: eth:ethertype:ip:tcp]
    [Coloring Rule Name: TCP SYN/FIN]
    [Coloring Rule String: tcp.flags & 0x02 || tcp.flags.fin == 1]
Ethernet II, Src: PcsCompu_f8:b3:a6 (08:00:27:f8:b3:a6), Dst: PcsCompu_0e:34:8d (08:00:27:0e:34:8d)
    Destination: PcsCompu_0e:34:8d (08:00:27:0e:34:8d)
        Address: PcsCompu_0e:34:8d (08:00:27:0e:34:8d)
        .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Source: PcsCompu_f8:b3:a6 (08:00:27:f8:b3:a6)
        Address: PcsCompu_f8:b3:a6 (08:00:27:f8:b3:a6)
        .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Type: IPv4 (0x0800)
Internet Protocol Version 4, Src: 192.168.1.193 (192.168.1.193), Dst: 192.168.1.160 (192.168.1.160)
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
        0000 00.. = Differentiated Services Codepoint: Default (0)
        .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    Total Length: 52
    Identification: 0x451e (17694)
    Flags: 0x40, Don't fragment
        0... .... = Reserved bit: Not set
        .1.. .... = Don't fragment: Set
        ..0. .... = More fragments: Not set
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 128
    Protocol: TCP (6)
    Header Checksum: 0x0000 [validation disabled]
    [Header checksum status: Unverified]
    Source Address: 192.168.1.193 (192.168.1.193)
    Destination Address: 192.168.1.160 (192.168.1.160)
Transmission Control Protocol, Src Port: 50076 (50076), Dst Port: microsoft-ds (445), Seq: 0, Len: 0
    Source Port: 50076 (50076)
    Destination Port: microsoft-ds (445)
    [Stream index: 9]
    [Conversation completeness: Incomplete (37)]
    [TCP Segment Len: 0]
    Sequence Number: 0    (relative sequence number)
    Sequence Number (raw): 262150869
    [Next Sequence Number: 1    (relative sequence number)]
    Acknowledgment Number: 0
    Acknowledgment number (raw): 0
    1000 .... = Header Length: 32 bytes (8)
    Flags: 0x002 (SYN)
        000. .... .... = Reserved: Not set
        ...0 .... .... = Nonce: Not set
        .... 0... .... = Congestion Window Reduced (CWR): Not set
        .... .0.. .... = ECN-Echo: Not set
        .... ..0. .... = Urgent: Not set
        .... ...0 .... = Acknowledgment: Not set
        .... .... 0... = Push: Not set
        .... .... .0.. = Reset: Not set
        .... .... ..1. = Syn: Set
            [Expert Info (Chat/Sequence): Connection establish request (SYN): server port 445]
                [Connection establish request (SYN): server port 445]
                [Severity level: Chat]
                [Group: Sequence]
        .... .... ...0 = Fin: Not set
        [TCP Flags: ··········S·]
    Window: 64240
    [Calculated window size: 64240]
    Checksum: 0x84d8 [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    Options: (12 bytes), Maximum segment size, No-Operation (NOP), Window scale, No-Operation (NOP), No-Operation (NOP), SACK permitted
        TCP Option - Maximum segment size: 1460 bytes
            Kind: Maximum Segment Size (2)
            Length: 4
            MSS Value: 1460
        TCP Option - No-Operation (NOP)
            Kind: No-Operation (1)
        TCP Option - Window scale: 8 (multiply by 256)
            Kind: Window Scale (3)
            Length: 3
            Shift count: 8
            [Multiplier: 256]
        TCP Option - No-Operation (NOP)
            Kind: No-Operation (1)
        TCP Option - No-Operation (NOP)
            Kind: No-Operation (1)
        TCP Option - SACK permitted
            Kind: SACK Permitted (4)
            Length: 2
    [Timestamps]
        [Time since first frame in this TCP stream: 0.000000000 seconds]
        [Time since previous frame in this TCP stream: 0.000000000 seconds]

In a Linux <-> Linux capture using Tcpdump, it appears that four SYNs were sent by the proxy for port 445 that went unanswered (which Nmap correctly interprets as the port being closed): image

From my cursory, uneducated glance, it appears different TCP options are being used in the initial SYNs sent in each scenario.

Let me know if I can provide more info that would be helpful.

Expected behavior

Windows <-> Linux tunnels do not result in malformed packets

Actual behavior

Windows <-> Linux tunnels do result in malformed packets

Error details

No response

Environment data

Name                           Value
----                           -----
PSVersion                      5.1.22000.2538
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.22000.2538
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Version

OpenSSH_for_Windows_9.5p1, LibreSSL 3.8.2

Visuals

No response

Octoberfest7 commented 6 months ago

My apologies; somehow in all of the testing I did, I failed to test creating the tunnel using a different Windows device. For reasons unknown, the particular Win11 VM I was using (latest developer image from MSFT) encountered those issues, while 3 other win10/win11 devices did not. No idea why, but I no longer think this is an issue in OpenSSH.

TTTPOB commented 2 months ago

last time i came across similar problem, it was that my local program running on windows only listening on 0.0.0.0 not on any v6 address, and ssh remote port forward will proxy remote machine requests from ::1, so it reports port closed since I am not listening on it. maybe you can check if there should be similar problems.