Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ftplib: unlimited readline() from connection #60242

Closed
tiran opened this issue Sep 25, 2012 · 52 comments
Closed

ftplib: unlimited readline() from connection #60242

tiran opened this issue Sep 25, 2012 · 52 comments
Assignees
Labels
performance Performance or resource usage release-blocker stdlib Python modules in the Lib dir

Comments

@tiran
Copy link
Member

tiran commented Sep 25, 2012

BPO 16038
Nosy @warsaw, @akuchling, @birkenfeld, @josiahcarlson, @pitrou, @larryhastings, @giampaolo, @tiran, @benjaminp, @serhiy-storchaka
Files
  • ftplib_maxline.patch
  • ftplib_maxline.patch
  • ftplib_maxline.patch
  • ftplib_maxline.patch
  • ftplib.patch
  • ftplib_maxline.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/tiran'
    closed_at = <Date 2014-09-30.12:49:29.449>
    created_at = <Date 2012-09-25.10:32:54.669>
    labels = ['release-blocker', 'library', 'performance']
    title = 'ftplib: unlimited readline() from connection'
    updated_at = <Date 2018-08-13.13:14:33.778>
    user = 'https://github.com/tiran'

    bugs.python.org fields:

    activity = <Date 2018-08-13.13:14:33.778>
    actor = 'Jeff Dafoe'
    assignee = 'christian.heimes'
    closed = True
    closed_date = <Date 2014-09-30.12:49:29.449>
    closer = 'georg.brandl'
    components = ['Library (Lib)']
    creation = <Date 2012-09-25.10:32:54.669>
    creator = 'christian.heimes'
    dependencies = []
    files = ['28970', '29019', '29090', '31589', '31777', '31862']
    hgrepos = []
    issue_num = 16038
    keywords = ['patch']
    message_count = 52.0
    messages = ['171241', '181479', '181546', '181549', '181553', '181559', '181571', '181580', '181635', '181638', '181644', '181745', '181771', '181772', '181773', '182195', '182235', '182236', '185060', '194170', '194551', '194921', '194935', '196861', '196927', '197791', '197817', '197827', '197921', '197957', '197959', '198298', '198323', '198325', '198327', '198344', '198356', '198357', '198368', '198371', '198380', '198388', '200348', '200386', '200387', '200588', '200592', '200602', '214908', '226309', '227889', '323483']
    nosy_count = 17.0
    nosy_names = ['barry', 'akuchling', 'georg.brandl', 'josiahcarlson', 'pitrou', 'larry', 'giampaolo.rodola', 'christian.heimes', 'benjamin.peterson', 'stutzbach', 'Arfrever', 'neologix', 'python-dev', 'serhiy.storchaka', 'raduv', 'inc0', 'Jeff Dafoe']
    pr_nums = []
    priority = 'release blocker'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'resource usage'
    url = 'https://bugs.python.org/issue16038'
    versions = ['Python 3.2']

    @tiran
    Copy link
    Member Author

    tiran commented Sep 25, 2012

    This bug is similar to bpo-16037.

    The ftplib module doesn't limit the amount of read data in its call to readline(). An erroneous or malicious FTP server can trick the ftplib module to consume large amounts of memory.

    Suggestion:
    The ftplib module should be modified to use limited readline() with _MAXLINE like the httplib module.

    @tiran tiran added stdlib Python modules in the Lib dir performance Performance or resource usage labels Sep 25, 2012
    @tiran tiran self-assigned this Jan 20, 2013
    @inc0
    Copy link
    Mannequin

    inc0 mannequin commented Feb 5, 2013

    Hello,

    I've made patch which address this issue.

    @tiran
    Copy link
    Member Author

    tiran commented Feb 6, 2013

    Thank you very much!

    Have you read and checked what the RFCs about the FTP protocol say about maximum line length?

    @inc0
    Copy link
    Mannequin

    inc0 mannequin commented Feb 6, 2013

    Well its my understanding, that there is no maximum length specified in RFC959. There is however option to set it up while telnet connection, and that would be other solution to this issue.

    @pitrou
    Copy link
    Member

    pitrou commented Feb 6, 2013

    Michał, thanks for the patch. Could you sign and e-mail a contributor's agreement? http://www.python.org/psf/contrib/

    As for the patch:

    • the test could be a separate test_ method
    • the offset variable isn't used in cmd_retrlarge, there is no need computing it

    @neologix
    Copy link
    Mannequin

    neologix mannequin commented Feb 6, 2013

    Have you read and checked what the RFCs about the
    FTP protocol say about maximum line length?

    vsftpd seems to use a 4096 limit (and the guy knows his stuff :-):
    ftp://vsftpd.beasts.org/users/cevans/untar/vsftpd-3.0.2/defs.h

    @inc0
    Copy link
    Mannequin

    inc0 mannequin commented Feb 6, 2013

    Well, that is not from RFC (or I hadn't find it):) however I'd lie if I'd call myself an expert, should I change limit to 4096 then?

    @neologix
    Copy link
    Mannequin

    neologix mannequin commented Feb 7, 2013

    Well, that is not from RFC (or I hadn't find it):) however I'd lie if I'd call myself an expert, should I change limit to 4096 then?

    It's probably not in the RFC: this just shows that the limit chosen
    should be enough.

    @giampaolo
    Copy link
    Contributor

    LineTooLong should be added to ftplib.all_errors.
    4096 looks enough to me.
    The longest lines I can think of occur when processing MLSD command which produces an output of like this:

    type=file;size=156;perm=r;modify=20071029155301;unique=801cd2; music.mp3
    type=dir;size=0;perm=el;modify=20071127230206;unique=801e33; ebooks
    type=file;size=211;perm=r;modify=20071103093626;unique=801e32; module.py

    Considering that the file names listed in there are forced to consist of base names (as opposed to *full* path names) I doubt we'll ever hit 4096.
    In pyftpdlib I used 2048 bytes.
    I can't recall any reference about this in any FTP-related RFC.

    @tiran
    Copy link
    Member Author

    tiran commented Feb 7, 2013

    I suggest that we use twice the size of the largest limit (8192) for the DoS fix and reduce it to 2048 for Python 3.4. 8192 is still a small number for modern computers.

    I also like to see comments next to the limit that explain on what grounds we have chosen the value. For example

    # vfstpd has a limit of 4096 (ftp://vsftpd.beasts.org/users/cevans/untar/vsftpd-3.0.2/defs.h)
    # pyftpdlib has a limit of 2048

    @pitrou
    Copy link
    Member

    pitrou commented Feb 7, 2013

    I suggest that we use twice the size of the largest limit (8192) for
    the DoS fix and reduce it to 2048 for Python 3.4. 8192 is still a
    small number for modern computers.

    Why do you want to reduce it? It doesn't bring any additional security.

    @inc0
    Copy link
    Mannequin

    inc0 mannequin commented Feb 9, 2013

    Hello,

    I've set up maxline limit to 8192. Also I've add some changes Antoine suggested earlier.

    @pitrou
    Copy link
    Member

    pitrou commented Feb 9, 2013

    Not sure how I nosied Larry by updating this issue, sorry for the mistake.

    @pitrou
    Copy link
    Member

    pitrou commented Feb 9, 2013

    Ah, but that's because I added 3.4 in the versions field and the issue is a release blocker :)

    @larryhastings
    Copy link
    Contributor

    My spies are everywhere! You cannot hide your black heart, Pitrou.

    @tiran
    Copy link
    Member Author

    tiran commented Feb 15, 2013

    CVE-2013-1752 Unbound readline() DoS vulnerabilities in Python stdlib

    @giampaolo
    Copy link
    Contributor

    Patch looks ok. Just add the new exception class to all_errors list.

    @inc0
    Copy link
    Mannequin

    inc0 mannequin commented Feb 16, 2013

    Thank you Giampaolo,

    I'm attaching patch changed according to your suggestion.

    @benjaminp
    Copy link
    Contributor

    Not blocking 2.7.4 as discussed on mailing list.

    @neologix
    Copy link
    Mannequin

    neologix mannequin commented Aug 2, 2013

    So, what now?

    @tiran
    Copy link
    Member Author

    tiran commented Aug 6, 2013

    The patches are languishing in the bug tracker for a while...

    Benjamin:
    I like to apply them to 3.3 and default before the next release of 3.3. Do you want to have the fixes in 2.7, too?

    @benjaminp
    Copy link
    Contributor

    I suppose this is fine for 2.7

    @serhiy-storchaka
    Copy link
    Member

    Error message "got more than %d bytes" is misleading because in most cases (except storlines()) we read not bytes but a text string.

    There are 4 changes in the ftplib module but only one of them covered by test.

    @warsaw
    Copy link
    Member

    warsaw commented Sep 3, 2013

    blocker for 2.6.9

    @giampaolo
    Copy link
    Contributor

    I'm attaching a slightly different patch including new tests and which uses a 'maxline' class attribute (as opposed to a global var).
    Christian if that's OK with you I will wait a while and then make a commit for all Python versions.

    @akuchling
    Copy link
    Member

    For 2.6 I'll make a revised version of Giampaolo's patch that doesn't add a new exception class.

    Rationale: Adding a new exception class changes the API of the module, which we'd like to avoid. If someone is writing 2.6 code that wants to catch this exception, they can't write "except ftplib.LineTooLong" because the name isn't present. Instead they'll have to catch the parent Error exception class and analyze either its type or the exception message. My conclusion is that adding the new class isn't actually useful.

    (bwarsaw and I are at a mini-sprint looking at the 2.6.9 blockers, so we're looking at all of these 'unlimited readline' issues and will continue to remove new exceptions introduced by patches.)

    @akuchling
    Copy link
    Member

    2.6 version of the patch. Changes from Giampaolo's version of the patch:

    • 2.6 didn't have FTP over TLS, so the patch changes slightly to adapt.

    • Removed the LineTooLong exception class and just raise Error instead. (This repeats the message text for "Line too long" in several place.)

    @warsaw
    Copy link
    Member

    warsaw commented Sep 15, 2013

    ======================================================================
    FAIL: test_retrlines_too_long (main.TestFTPClass)
    ----------------------------------------------------------------------

    Traceback (most recent call last):
      File "Lib/test/test_ftplib.py", line 374, in test_retrlines_too_long
        self.client.retrlines, 'retr', received.append)
    AssertionError: Error not raised

    @giampaolo
    Copy link
    Contributor

    Looks legitimate to me. I will come up with a separate patch for later Python versions.

    @warsaw
    Copy link
    Member

    warsaw commented Sep 17, 2013

    Yep, confirmed that ftplib.patch causes test_ftplib to fail, at least on Ubuntu 10.04 chroot.

    @warsaw
    Copy link
    Member

    warsaw commented Sep 17, 2013

    Succeeds on OS X 10.8 (although there are other failures)

    @warsaw
    Copy link
    Member

    warsaw commented Sep 22, 2013

    Okay, this one is quite odd. It's definitely a timing issue.

    If I put a import time; time.sleep(1) at the beginning of test_retrlines_too_line() -- i.e. first line of the method -- then the test reliably passes. If I put a print(len(line)) just before the maxline test in FTP.retrlines(), then the test will pass just as reliably.

    If I put that retrlines() print after the maxline test, then it passes sometimes and fails sometimes. When if fails, it's only ready 12 bytes from the fp.readline() call. When it passes, it's reading 8193 bytes (thus triggering the expected exception).

    I really hate to put a sleep in the test to make it pass. Obviously it would be better not to fudge this race condition, but I don't know the code well enough to know where the race is yet.

    @serhiy-storchaka
    Copy link
    Member

    What about time.sleep(0.1)?

    @warsaw
    Copy link
    Member

    warsaw commented Sep 23, 2013

    On Sep 23, 2013, at 03:36 PM, Serhiy Storchaka wrote:

    What about time.sleep(0.1)?

    I usually don't like introducing sleeps to fix race conditions, but if that's
    the only option for landing this patch, maybe we'll have to hold our noses and
    do it.

    @giampaolo
    Copy link
    Contributor

    Barry can you paste the traceback caused by the race condition? What's not clear to me is when (what line) it occurs.
    One solution might be to send a "NOOP" command (self.client.sendcmd('noop')) in order to synchronize client and server.

    @warsaw
    Copy link
    Member

    warsaw commented Sep 23, 2013

    On Sep 23, 2013, at 06:33 PM, Giampaolo Rodola' wrote:

    Barry can you paste the traceback caused by the race condition? What's not
    clear to me is when (what line) it occurs. One solution might be to send a
    "NOOP" command (self.client.sendcmd('noop')) in order to synchronize client
    and server.

    There's no traceback other than the test failure that I posted. It's a race
    condition because with a little sleep, the test passes. Without it, it fails.

    This is on various flavors of Ubuntu (only up to 10.04 which is the last
    version I can build a full 2.6 against) and Debian.

    @giampaolo
    Copy link
    Contributor

    I believe the problem is the set of next_retr_data attribute here:

        def test_retrlines_too_long(self):
            self.server.handler.next_retr_data = 'x' * self.client.maxline * 2

    ...because self.server.handler runs in a different thread (different than the main one, which is where the setattr() occurs).
    We should introduce a new command in the dummy FTP server which sets next_retr_data from within the server thread itself. Will try to work on a patch later this week (I'm sorry but I can't make it earlier).

    @warsaw
    Copy link
    Member

    warsaw commented Sep 24, 2013

    On Sep 24, 2013, at 01:12 PM, Giampaolo Rodola' wrote:

    Giampaolo Rodola' added the comment:

    I believe the problem is the set of next_retr_data attribute here:

    def test_retrlines_too_long(self):
    self.server.handler.next_retr_data = 'x' * self.client.maxline * 2

    ...because self.server.handler runs in a different thread (different than the
    main one, which is where the setattr() occurs). We should introduce a new
    command in the dummy FTP server which sets next_retr_data from within the
    server thread itself. Will try to work on a patch later this week (I'm sorry
    but I can't make it earlier).

    +1 - that explanation makes a lot of sense, thanks!

    Currently 2.6.9rc1 is planned for Monday 30-September. It would be nice to
    get this one in before then, but if not that's okay. I think it's fairly low
    risk.

    @serhiy-storchaka
    Copy link
    Member

    Here is a patch.

    @warsaw
    Copy link
    Member

    warsaw commented Sep 24, 2013

    On Sep 24, 2013, at 09:59 PM, Serhiy Storchaka wrote:

    Added file: http://bugs.python.org/file31862/ftplib_maxline.patch

    This looks great and fixes the test failure problem. Thanks! Serhiy, please
    feel free to apply this to the 2.6 branch, or let me know if you'd rather I
    apply it.

    @serhiy-storchaka
    Copy link
    Member

    Please apply it yourself.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Sep 25, 2013

    New changeset 8b19e7d0be45 by Barry Warsaw in branch '2.6':

    @larryhastings
    Copy link
    Contributor

    Ping. Please fix before "beta 1".

    @giampaolo
    Copy link
    Contributor

    I think this is already fixed. Barry can we close this out?

    @Arfrever
    Copy link
    Mannequin

    Arfrever mannequin commented Oct 19, 2013

    It is fixed in Python 2.6, but not 2.7, 3.1, 3.2, 3.3, 3.4.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Oct 20, 2013

    New changeset 44ac81e6d584 by Serhiy Storchaka in branch '2.7':
    Issue bpo-16038: CVE-2013-1752: ftplib: Limit amount of data read by
    http://hg.python.org/cpython/rev/44ac81e6d584

    New changeset 38db4d0726bd by Serhiy Storchaka in branch '3.3':
    Issue bpo-16038: CVE-2013-1752: ftplib: Limit amount of data read by
    http://hg.python.org/cpython/rev/38db4d0726bd

    New changeset 0c48fe975c54 by Serhiy Storchaka in branch 'default':
    Issue bpo-16038: CVE-2013-1752: ftplib: Limit amount of data read by
    http://hg.python.org/cpython/rev/0c48fe975c54

    @Arfrever
    Copy link
    Mannequin

    Arfrever mannequin commented Oct 20, 2013

    (3.1 branch is open to security fixes.)

    @giampaolo
    Copy link
    Contributor

    You are right. I will try to provide patches for other Python versions
    later next week.

    On Sun, Oct 20, 2013 at 5:08 PM, Arfrever Frehtes Taifersar Arahesis <
    report@bugs.python.org> wrote:

    Arfrever Frehtes Taifersar Arahesis added the comment:

    (3.1 branch is open to security fixes.)

    ----------
    versions: +Python 3.1


    Python tracker <report@bugs.python.org>
    <http://bugs.python.org/issue16038\>


    @akuchling
    Copy link
    Member

    Are we likely to actually apply this change to the 3.1 and 3.2 branches, given that even the later 3.3 branch is now in security-fix mode? If we're not going to change 3.1 or 3.2, this issue can just be closed.

    @raduv
    Copy link
    Mannequin

    raduv mannequin commented Sep 3, 2014

    I'm a little confused about this patch. Please correct me if I'm wrong, but fp.readline([size + 1]) should return a line of length at most size + 1. This means that the check len(line) > size will always be true when reading a line that has a length greater than self.maxline. Also, wouldn't it make more sense to have the line that logs stuff in debugging mode be before raising a LineTooLong exception ? This way you have the option of actually seeing the line.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Sep 30, 2014

    New changeset 783e7b4375ac by Georg Brandl in branch '3.2':
    Issue bpo-16038: CVE-2013-1752: ftplib: Limit amount of data read by
    https://hg.python.org/cpython/rev/783e7b4375ac

    @JeffDafoe
    Copy link
    Mannequin

    JeffDafoe mannequin commented Aug 13, 2018

    I have a question about this old patch, as it just came down in a CentOS 6 update. I think the patch is applied to the data channel in ASCII mode and not just the control channel. On the data channel in ASCII mode, there should be no assumption of maximum line length before EOL. I saw that your current value came from vsftpd's header file. I'm guessing if you look at the implementation, it's either only applied to the control channel or it's just used to set a single read size inside of a loop. Examples of ASCII mode files that can exceed nearly any MAXLINE value without EOL are XML files or EDI files.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    performance Performance or resource usage release-blocker stdlib Python modules in the Lib dir
    Projects
    None yet
    Development

    No branches or pull requests

    9 participants