pyftpdlib
pyftpdlib copied to clipboard
CVE-2011-1575 and pyftpdlib
Although the CVE is specifically talking about pureftpd, I run an FTP server with pyftpdlib and got a hit on this service for CVE-2011-1575.
Can you comment on susceptibility to this vulnerability?
Here is a link to more details on CVE-2011-1575. Seems to be limited to pureftpd and should not apply to pyftpdlib. Can someone else from the community chip in on this?
The link doesn't give many details but it seems the issue is related to the "I/O buffering" when proftpd initiates the SSL session, which seems implemented into proftpd code itself. The "I/O buffering" in pyftpdlib in terms of SSL initialization (whatever that means) is handled internally by PyOpenSSL library. In terms of application (pyftpdlib) code there's another buffering which is done in pure python, occurring when we append the incoming data to a list (which also gets emptied if len(chars) exceeds 2048 bytes). The same routine is used both for clear-text and SSL sessions therefore I would say pyftpdlib cannot be affected by this specific bug which seems closely related to proftpd implementation.
I've successfully demonstrated the vulnerability using this recipe: http://www.securityfocus.com/archive/1/516901
Instead of adding a RSET to SMTP, add a NOOP to FTP's STARTTLS command. You see the response to both commands, when the server should either error out or ignore the second command.
I have a running instance of OpenSSL that exposes this flaw on my side, and my github repo of OpenSSL contains the change needed to expose the problem. https://github.com/mdineen/openssl
I can run the demo very quickly against a server of yours if you want to see it working.
The vulnerability as it affects Pyftpdlib is that if the ~~STARTTLS~~ AUTH TLS
command has a plaintext command after it, the plaintext command is accepted in the clear, and returns data over the encrypted channel. The RFC says that a ~~STARTTLS~~ AUTH TLS
should be the last command in a batch. The standard fix for other servers is to strip anything after the ~~STARTTLS~~ AUTH TLS
and ignore it.
Wait a sec: pyftpdlib does not support any "STARTTLS" command. Can you provide a script (client side) which demonstrates the bug?
Corrected. If you pull the OpenSSL source code from my git repo and build it, there's a simple command line test.
./config
make
make install
apps/openssl s_client -quiet -starttls ftp -connect <server ip>:<server port>
The result is:
220 pyftpdlib 1.3.0 ready.
200 I successfully done nothin'.
The second return line is the confirmation that it's vulnerable.
I've added a one-line fix that fixes this for me. At the end of TLS_FTPHandler.ftp_AUTH
I added:
self.ac_in_buffer = ''
I've put it in my derived class. Supported sync clients work as before, and the detection routine no longer trips the CVE.
Any further questions I can answer or follow up I can provide on this?
So, I tried to compile latest OpenSSL from sources on Ubuntu but failed so I used the one installed via apt (1.0.1f). This is the client:
$ openssl s_client -quiet -starttls ftp -connect 127.0.0.1:2121
depth=0 C = US, ST = Delaware, L = Wilmington, O = Python Software Foundation, OU = SSL, CN = somemachine.python.org
verify error:num=18:self signed certificate
verify return:1
depth=0 C = US, ST = Delaware, L = Wilmington, O = Python Software Foundation, OU = SSL, CN = somemachine.python.org
verify error:num=10:certificate has expired
notAfter=Feb 16 16:54:50 2013 GMT
verify return:1
depth=0 C = US, ST = Delaware, L = Wilmington, O = Python Software Foundation, OU = SSL, CN = somemachine.python.org
notAfter=Feb 16 16:54:50 2013 GMT
verify return:1
220 pyftpdlib 1.4.1 ready.
...and this is the server:
[I 14-12-15 18:13:39] >>> starting FTP+SSL server on 0.0.0.0:2121, pid=27601 <<<
[I 14-12-15 18:13:39] poller: <class 'pyftpdlib.ioloop.Epoll'>
[I 14-12-15 18:13:39] masquerade (NAT) address: None
[I 14-12-15 18:13:39] passive ports: None
[I 14-12-15 18:13:39] use sendfile(2): True
[I 14-12-15 18:13:43] 127.0.0.1:33672-[] FTP session opened (connect)
[D 14-12-15 18:13:43] 127.0.0.1:33672-[] -> 220 pyftpdlib 1.4.1 ready.
[D 14-12-15 18:13:43] 127.0.0.1:33672-[] <- AUTH TLS
[D 14-12-15 18:13:43] 127.0.0.1:33672-[] -> 234 AUTH TLS successful.
[I 14-12-15 18:14:48] 127.0.0.1:33672-[] FTP session closed (disconnect).
AFIU by using this version I cannot see any problem. As for your previous message: you say that "200 I successfully done nothin'." indicates there's a problem. To me that simply means the client sent a "NOOP" command, and "200 I successfully done nothin'." is the server response. Maybe it's my as I probably didn't understand the issue but I still fail to see what you think should be fixed here exactly.
Is this issue demonstrable by writing a Python unittest via ftplib+ssl or something?
You can't do it from the download package, you have to change the source for it to hit the exploit. Please see my change set at https://github.com/mdineen/openssl/commit/18ad39bdf223c139c43dd8f82e9e29687095bb70
The issue is that the command was sent in the clear, and is served encrypted. It violates the RFC to allow commands to trail after the AUTH TLS command. You're supposed to either throw an error or discard the buffer. I chose to discard the buffer, so I do not get a "200 I successfully done nothin'.".
I'm sure you can demonstrate using anything where you can append a command to the AUTH TLS command. Send a FEAT\n along, and if you get a response then the server is susceptible to plaintext command injection.
Let me know if I can be more help.
On Mon, Dec 15, 2014 at 12:23 PM, giampaolo [email protected] wrote:
Is this issue demonstrable by writing a Python unittest via ftplib+ssl or something?
— Reply to this email directly or view it on GitHub https://github.com/giampaolo/pyftpdlib/issues/315#issuecomment-67030432.
Our OpenVAS vulnerability monitoring pointed out this same issue for our pyftpdlib-based servers in the MiGrid project (www.migrid.org) and a web search lead me here.
I compiled and tried the modified openssl client from @mdineen and can verify that it does indeed still allow the illegal NOOP command to pass both in 1.5.1 and the latest git version of pyftpdlib.
As I understand the CVE this behavior potentially allows a malicious person a short window to inject any FTP commands into an FTPS session of another user (which is what the modified openssl client successfully emulates with NOOP). It may be difficult for someone else to hit that short time frame, but the consequences of a successful attack are pretty nasty, so fixing it seems quite relevant, IMO.
Wouldn't it make sense to at least apply the suggested workaround? I applied it in the git checkout and verified that it drops the illegal NOOP command and thus appears to solve the issue - without influencing basic FTP(S) use.
Cheers, Jonas