3389
Windows 上的遠端桌面監聽者——服務起不來時它已經被占走,而用戶端只是一直轉圈時,封包正被安靜地丟掉。
照你的系統挑一行貼上去執行。它會直接印出占著這個連接埠的行程,以及它綁在哪個位址上——通常看到這兩件事答案就出來了。
netstat -ano | findstr ":3389"這就是微軟自己的 RDP 疑難排解文件用的那一串。`-a` 顯示所有連線和監聽中的連接埠,`-n` 讓位址和連接埠維持數字,`-o` 補上所屬行程 ID,也就是最後一欄。找那一行 Local Address 以 `:3389` 結尾、State 是 LISTENING 的——`0.0.0.0:3389` 是每一張介面,`127.0.0.1:3389` 就只有回送位址,永遠不可能接受遠端 session。然後用 `tasklist /svc | findstr "1234"` 去查那個 PID,不要用單純的 `tasklist`:監聽者住在某個 `svchost.exe` 裡面,只有 `/svc` 那一欄才會揭露它背後的服務是 `TermService`。如果那個 PID 屬於別的東西,就是那個程式先搶走了連接埠——去搬它,不要搬 RDP。
qwinsta在你開始抓小偷之前先跑這個,因為它回答的是另一個問題:到底有沒有監聽者存在?出現一行名稱是 `rdp-tcp`、State 是 `Listen`,代表 RDP 監聽者是正常的,問題在更外面——防火牆、路由或帳密。沒有那一行,代表監聽者根本沒起來,而 `netstat` 在 3389 上什麼都查不到的原因和別的行程完全無關。這種情況請確認遠端桌面服務(`TermService`)和遠端桌面服務使用者模式連接埠重新導向器(`UmRdpService`)都在跑,而且 `fDenyTSConnections` 是 `0`。
sudo ss -tlnp 'sport = :3389'一台在 3389 上監聽的 Linux 機器跑的是 xrdp 或類似的伺服器,不是任何微軟的東西。`-t` 顯示 TCP socket,`-l` 只留監聽中的,`-n` 跳過服務名稱解析,`-p` 顯示所屬行程;`sport = :3389` 依來源埠過濾。Local Address:Port 那一欄就是綁定結果——`0.0.0.0:3389` 網路上碰得到,`127.0.0.1:3389` 只有回送位址,那通常是 xrdp 打算讓人走 SSH 通道進來時的設定。持有者會印成 `users:(("xrdp",pid=987,fd=11))`。沒有 `ss` 的話,`sudo netstat -tlnp | grep :3389` 給你同樣的答案,行程那欄改成斜線分隔的 pid/program;兩者都需要超級使用者權限才點得出不屬於你的行程。
sudo lsof -nP -iTCP:3389 -sTCP:LISTENmacOS 內建的是 RDP 用戶端而不是伺服器,所以在這裡聽的東西不是容器、就是某台 VM 轉發出來的連接埠,再不然就是你自己裝的第三方伺服器。`-n` 和 `-P` 讓位址和連接埠維持數字,`-iTCP:3389` 選連接埠,`-sTCP:LISTEN` 收斂到監聽者。COMMAND 和 PID 點出持有者,NAME 是綁定位址:`127.0.0.1:3389` 只有回送位址,`*:3389` 是每一張介面。COMMAND 是 `com.docker.backend` 代表這是容器發布出來的連接埠,要停的是容器不是行程。沒有輸出——lsof 以 1 結束——代表沒有人在聽,連接埠是真的空的。
`nmap -Pn -p 3389 host.example.com` 告訴你,從用戶端實際所在的位置看,這個連接埠長什麼樣,而 `-Pn` 會跳過主機探索,所以不回 ping 的主機照樣掃得到。nmap 對三種答案的定義很精確:`open` 代表目標上有應用程式在聽;`closed` 代表主機回應了但那裡沒有人在聽;`filtered` 代表防火牆、過濾器或其他網路障礙擋住了探測,所以 nmap 分不出是前兩者中的哪一個。對 RDP 來說,這個分野就是整個診斷——`closed` 把你導向伺服器(服務停了、`fDenyTSConnections` 被設成 1、`PortNumber` 被搬走),`filtered` 把你導向 Windows 防火牆規則或雲端安全群組。加 `--reason` 看是回了 reset 還是根本沒回,加 `-sV` 讓 nmap 讀交握。只掃你自己負責的主機。
| 你看到的訊息 | 代表什麼 |
|---|---|
The client spins, then shows the generic dialog listing three reasons: remote access is not enabled, the computer is turned off, or it is not available on the network | 那個對話框是包山包海的萬用訊息,三個原因一個都沒指名。改成照順序把鏈子走一遍:在目標上跑 `qwinsta` 看有沒有 `rdp-tcp` 那行處於 `Listen`;用 `netstat -ano | findstr ":3389"` 找 LISTENING 那一行;確認 `fDenyTSConnections` 是 `0`;最後才看防火牆。每一步都消掉那個對話框懶得替你選的其中一個原因。 |
The connection is refused immediately — the handshake gets an instant reset | 主機連得到,但沒有人在聽。`TermService` 停了、遠端桌面被停用(`fDenyTSConnections` 是 `1`,而且很可能是群組原則壓著、蓋過「設定」裡的勾選框),或者 `WinStations\RDP-Tcp` 底下的 `PortNumber` 被改過,監聽者根本在別的地方。最後這種情況 `qwinsta` 一行就分得出來:監聽者存在,只是不在你撥的那個連接埠上。 |
The client waits many seconds and then reports a timeout | 什麼都沒回應,這是被丟掉而不是被拒絕。可能是 Windows 防火牆的遠端桌面入站規則、雲端的安全群組或網路安全性群組,也可能是中間某段網路 ACL。被拒絕是瞬間的、被丟掉是慢的,所以這段延遲本身就是證據;`nmap --reason` 會報 `filtered` 而不是 `closed`,直接替你確認。 |
Only one usage of each socket address (protocol/network address/port) is normally permitted | Windows 錯誤 10048,也就是 `EADDRINUSE` 在本地的說法:在遠端桌面服務啟動之前,有別的東西先搶走了 3389。照微軟的順序走——先 `netstat -ano | findstr ":3389"` 拿 PID,再 `tasklist /svc | findstr "<pid>"` 查它背後的服務。如果持有者不是 `TermService`,官方建議的做法是搬走那個程式,而不是搬 RDP。 |
netstat shows nothing on 3389 and the listener still cannot bind | 連接埠是被保留而不是被占用,這是 Windows 版的權限失敗形狀:根本沒有行程可以找。`netsh interface ipv4 show excludedportrange protocol=tcp` 會列出 Hyper-V、WSL2、Docker Desktop 和 Windows NAT 服務在開機時搶下的動態連接埠區段。如果 3389 落在其中一段裡,就去重啟搶走它的那個服務——砍行程沒有用,因為根本沒有那個行程。 |
The identity of the remote computer cannot be verified. Do you want to connect anyway? | 在原廠設定的機器上這是預期行為,不是網路故障:對方拿出來的是服務自己替自己產生的 RDP 自簽憑證,沒有任何用戶端驗證得了。這代表 session 有加密但沒有身分驗證,所以它不能證明另一端是哪一台主機。如果這件事重要,就替監聽者簽發一張真的憑證——而如果這個警告出現在一台原本有受信任憑證的主機上,請把它當成一個發現去查,不要當成煩人的提示。 |
The session connects but is far laggier than the same link used to be | TCP 3389 是開的,UDP 3389 不是。微軟的 UDP 傳輸擴充用同一個連接埠號跑圖形和多媒體,當它起不來,session 就安靜地退回只用 TCP。請確認防火牆規則兩種協定都涵蓋,不是只有 TCP。 |
IANA 把 3389 登記為 `ms-wbt-server`(微軟對 Windows-Based Terminal 的縮寫),TCP 和 UDP 都有,而 Windows 也真的兩邊都用。TCP 承載整個 session;微軟的 UDP Transport Extension 把圖形和多媒體走 UDP,而它的規格書寫明終端機伺服器接收 UDP 連線請求的預設連接埠同樣是 3389。這就是為什麼一條只開 TCP 的防火牆規則,會讓你連得上、但在丟包的線路上明顯變差。在 Windows 上,這個監聽者不是一個普通程式。它屬於遠端桌面服務,也就是 `TermService`,而它被裝在某個 `svchost.exe` 裡面,所以用單純的 `tasklist` 去查 PID 只會得到 `svchost.exe`,什麼有用的都沒說;要用 `tasklist /svc` 才會報出服務名稱。剩下的由三個 Registry 機碼決定。`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server` 底下的 `fDenyTSConnections` 在遠端桌面啟用時是 `0`、停用時是 `1`,而群組原則可以不管「設定」裡的勾選框長什麼樣,把它壓在 `1`。`...\Terminal Server\WinStations\RDP-Tcp` 底下的 `PortNumber` 才是真正決定連接埠的東西。至於 `rdp-tcp` 監聽者到底有沒有進到 `Listen` 狀態,`qwinsta` 會告訴你,而那跟「服務有沒有在跑」是兩個不同的問題。在 Windows 以外,xrdp 這類第三方伺服器也用同一個號碼,所以一台在 3389 上回應的 Linux 主機跑的是那些東西,不是微軟的元件。
不要把 3389 放到公開網際網路上。這裡的歷史是具體的,不是理論:CVE-2019-0708,也就是「BlueKeep」,是 Windows 7、Server 2008 和 Server 2008 R2 上遠端桌面服務的一個認證前遠端執行程式碼漏洞——具備蠕蟲能力,因為它不需要帳密、不需要使用者操作、也不需要社交工程,只需要碰得到一個有漏洞的監聽者。微軟連已經停止支援的版本都發了修補,並且指出網路層級驗證(NLA)算是部分緩解,理由正是它會在有漏洞的那段程式碼跑起來之前先強制驗證身分。用 `PortNumber` 把監聽者搬走不是防禦:微軟自己的疑難排解文件明確不建議把 RDP 跑在別的連接埠上,而會辨識服務指紋的掃描器照樣找得到它。你順手按掉的那個 TLS 警告也不是保護——那張憑證通常是機器自己的 RDP 自簽憑證,由服務產生並放在電腦憑證存放區的 Remote Desktop 底下,所以接受它並不能驗證任何事,而你接下來要在上面輸入的是網域密碼。請把 RDP 放到 VPN、RD Gateway 或跳板機後面,依來源位址限制,要求使用網路層級驗證;如果身分驗證真的重要,就把自簽憑證換成一張用戶端會真的去驗的簽發憑證。
這些全都在你的瀏覽器裡跑,不會上傳任何東西。
查 3389 的時候,通常也會順手看一下這幾個。
幾乎都是權限問題。lsof 和 ss 會列出 socket,但別人的行程它不會顯示所有者,所以由系統帳號、容器執行環境或 launchd 啟動的服務,在你補上 sudo 之前就只是一個沒有主人的監聽項。Windows 則是反過來:netstat 什麼都沒有,綁定卻還是失敗,那代表這個號碼落在 Hyper-V、WSL2 或 Docker Desktop 開機時預留的範圍裡。
先看清楚它是什麼。沒收乾淨的開發伺服器砍掉沒事;正在寫入的資料庫不行,其他東西依賴的系統服務也不行。如果持有者是容器,請停容器,不要砍主機看到的那個行程——砍了執行環境只會把它拉起來。遇到真的不能動的,把自己的服務換一個連接埠反而更快。
因為它綁在 127.0.0.1 而不是 0.0.0.0。每一頁的指令之所以會把綁定位址跟行程名一起印出來,就是為了這件事:綁在回送位址上的服務,不管防火牆怎麼設,都只有這台機器自己連得到。現在不少框架是刻意這樣預設的,所以通常是一個參數的問題,不是 bug。如果已經是 0.0.0.0 還是不通,下一個要懷疑的就是主機防火牆或雲端的安全群組。
幾乎沒有幫助。掃全網的機器持續在掃整段範圍,換號碼買到的是幾個小時,不是安全,代價卻是每個同事都得記住它。它確實能讓針對預設埠的無差別掃描少污染一點日誌,這算是實際但有限的好處。真正會改變結果的是認證、限制來源位址的防火牆,以及一開始就不要讓它對外。
連接埠 3389 還是卡住嗎?看完整的連接埠對照表,或是回到上面,從你實際看到的那行錯誤往回追。