Bug #3779 | non close-on-exec file descriptor leads to security risk | ||
---|---|---|---|
Submitted: | 16 May 2004 0:00 | Modified: | 6 Sep 2004 19:43 |
Reporter: | Lukasz Wojtow | Email Updates: | |
Status: | Won't fix | Impact on me: | |
Category: | MySQL Server | Severity: | S2 (Serious) |
Version: | 4.0.18 | OS: | Linux (Linux) |
Assigned to: | Sergei Golubchik | CPU Architecture: | Any |
[16 May 2004 0:00]
Lukasz Wojtow
[29 May 2004 16:30]
MySQL Verification Team
Hi! Thank you so much for writting to us. Can you please clarify few things. First of all, scanning all file descriptors in the given process will can only descriptors in that process, not in entire OS. Not even in the parent process. So, by scanning fd's, what is caught is actually a file descriptor of this program. You have connected to MySQL server in the same program and it is normal that this file descriptor is found. Try to skip connecting to MySQL server in the same program and see for the result. Close on exec would also make impossible permanent connections.
[29 May 2004 21:11]
Lukasz Wojtow
Hi, Thanks for replying. Of course i know that the process can only find its own file descriptors. And partly that's my point - a connection's descriptor goes to the process being executed. It's a bit dificult to show that issue without 'fake' connection. That's because connecting to a database not only creates a socket, but also initializes other members of MYSQL structure. I didn't have time to do that, but look on the example: root@darkstar:/www/docs/host1.com/php# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 2 to server version: 4.0.18-log Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> show databases; +----------+ | Database | +----------+ | host1 | | host2 | | mysql | +----------+ 3 rows in set (0.14 sec) mysql> quit Bye root@darkstar:/www/docs/host1.com/php# mysql -u host1 -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3 to server version: 4.0.18-log Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> show databases; +----------+ | Database | +----------+ | host1 | +----------+ 1 row in set (0.00 sec) mysql> quit Bye As you see, user host1 have permission to use only host1 database, but there are some more (shown in root's connection) Now, look on apache's processes: root@darkstar:~# ps aux | grep apache root 120 0.0 0.2 4108 1948 ? S 19:45 0:00 /usr/local/apache_1.3.29/bin/httpd nobody 122 0.0 0.2 4124 1952 ? S 19:45 0:00 /usr/local/apache_1.3.29/bin/httpd root 170 0.0 0.0 1468 452 pts/0 R 19:46 0:00 grep apache It's state just after restarting apache. For this text's purpose just one child is created (makes things easier). When script root@darkstar:/www/docs/host1.com# cat test.php <?php system("./sockdump >> /tmp/sockdump"); ?> is called now, then there will be only one connection found: root@darkstar:/www/docs/host1.com# lynx --dump host1.com/test.php > /dev/null root@darkstar:/www/docs/host1.com# cat /tmp/sockdump Found socket on descriptor 3 Dumping database host1 Dumping table table1 kajsd kajasdsd kajaasdsd asd It's the connection created by that program. Now imagine that some domain on the web server uses script similar to this: root@darkstar:/www/docs/host2.com# cat m.php <?php $c = mysql_pconnect('localhost','root','dupajasia'); // whatever here ?> After a request to that script things look like this: root@darkstar:~#ps aux | grep apache root 120 0.0 0.2 4108 1948 ? S 19:45 0:00 /usr/local/apache_1.3.29/bin/httpd nobody 122 0.0 0.2 4176 2280 ? S 19:45 0:00 /usr/local/apache_1.3.29/bin/httpd root@darkstar:~# ls -l /proc/122/fd total 0 lr-x------ 1 root root 64 May 29 19:55 0 -> /dev/null l-wx------ 1 root root 64 May 29 19:55 1 -> /dev/null l-wx------ 1 root root 64 May 29 19:55 15 -> /usr/local/apache_1.3.29/logs/error_log lrwx------ 1 root root 64 May 29 19:55 16 -> socket:[107] l-wx------ 1 root root 64 May 29 19:55 17 -> /usr/local/apache_1.3.29/logs/access_log l-wx------ 1 root root 64 May 29 19:55 2 -> /usr/local/apache_1.3.29/logs/error_log lrwx------ 1 root root 64 May 29 19:55 4 -> socket:[854] Additional socket has been created (connection to the database). Now let's try to call host1.com/test.php again (and indirectly sockdump program): root@darkstar:~# rm /tmp/sockdump root@darkstar:~# lynx --dump host1.com/test.php > /dev/nul root@darkstar:~# cat /tmp/sockdump Found socket on descriptor 3 Dumping database host1 Dumping table table1 kajsd kajasdsd kajaasdsd asd Found socket on descriptor 4 Dumping database host1 Dumping table table1 kajsd kajasdsd kajaasdsd asd Dumping database host2 Dumping table table2 asd aasdsd akjoiu Dumping database mysql Dumping table columns_priv Dumping table db % test Y Y Y Y Y Y N Y Y Y Y Y % test\_% Y Y Y Y Y Y N Y Y Y Y Y % host2 host2 Y Y Y Y Y N Y Y Y Y Y Y % host1 host1 Y Y Y Y Y N Y Y Y Y Y Y Dumping table func Dumping table host Dumping table tables_priv Dumping table user localhost root 4b1b27c800bde9da Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y 0 0 0 darkstar root 4b1b27c800bde9da Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y 0 0 0 % host1 310367357a4bd718 N N N N N N N N N N N N N N N N N N N N N 0 0 0 % host2 310369227a4bdd05 N N N N N N N N N N N N N N N N N N N N N 0 0 0 Two connections are found: 1st made from sockdump (file descriptor 3 - so only 1 database found), and the 2nd on file descriptor 4 - as you see it's inherited by sockdump program from apache's child (root login). Got the idea now? I'm not quite convinced that marking socket as close-on-exec would break mysql_pconnect. I don't know much about it, but i thought that apache doesn't call execve during php scripts execution (even on two diffrent domains)? Thanks for reading. Lukasz Wojtow
[14 Jun 2004 7:52]
Hartmut Holzgraefe
No, close_on_exec won't break PHPs pconnect as this only works with integrated server modules anyway (like PHP as Apache module) where PHP serves several or even all (in the case of pure multithreaded servers) PHP requests from within a single process context. But even if close_on_exec were set on PHP->MySQL connections there would still be the risk of connection hijacking with PHP pconnect as any other piece of code executed within the web server context has access to the persistant connection. This especially includes, but is not limited to, other PHP scripts. So as soon someone has enough access to the server to add a PHP or modify PHP scripts he will also have access to your persistant connections. Even if he is not allowed to use PHPs exec family of functions. I can agree that adding close_on_write would add a little security in general. But OTOH there is the possibility of breaking existing code by changing this behavior. Someone may have used exactly the current behavior to actually gain security by passing already opened connections to binaries not trusted to see the actual login parameters. I'm not aware of anybody using this, and i'm not even sure if this approach really makes sense, but *if* someone had ever made use of this he would have a very hard time to track down what had changed if we were to break his code by adding close_on_exec.
[14 Jun 2004 13:13]
Lukasz Wojtow
>So as soon someone has enough access to the server to add a PHP >or modify PHP scripts he will also have access to your persistant >connections. So which php's function (or what code) connects do database on given file descriptor? php's connections are not integer numbers, and trying to treat an integer as a connection will result with a failure. I though that only way to connect to mysql in php is to use mysql_connect or mysql_pconnect. I agree that as descriptor is still opened, then in case of any bug allowing to execute arbitrary code it will be possible to take over the connection, but this behavior would be result of a bug only, and is not intended, am i right? >Even if he is not allowed to use PHPs exec family of functions. You're right, i.e. suexec doesn't close this descriptor, so a connection can be inherited by cgi script. >security in general. But OTOH there is the possibility of breaking >existing code by changing this behavior. Someone may have used >exactly the current behavior to actually gain security by passing >already opened connections to binaries not trusted to see the >actual login parameters. As mysql doesn't have any lib function which makes connection to given file descriptor (unlike pgsql) that means that there is no 'green light' to use this behavior and in order to do that developers have to use dirty tricks with MYSQL stucture. I wouldn't be worried about such software. Anyway, if you think that's not a bug, that's fine. But it would be nice if you told guys from php to warn people about insecurity of mysql_pconnect and made them to ship php with persistent connections disabled by default. Thanks for your answer
[14 Jun 2004 14:48]
Hartmut Holzgraefe
Addition: The new mysqli extension in PHP 5 has dropped the support for persistent connections alltogether. The speed gain by using pconnect hasn't been that big anyway as mysql client connects are rather fast anyway (compared to other databases) and the effort needed to reset a connection per PHP request using it so that it does not get influenced by previous requests settings would have consumend most of the remaining gain. So it was decided to completely drop pconnects for ext/mysqli as there is no way to implement it both performant and secure at the same time.