|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2018-03-08 13:48 UTC] jaco at uls dot co dot za
Description: ------------ We've recently noticed that when some of our PHP code executes external scripts via the system() call, those scripts have access to file descriptors that it probably should not. Specifically we are able to tap into the PHP-FPM listening socket. Below a simple php + shell script combo to illustrate the problem. Note that in the below, fd 0 is the listening socket, so one could conceivable start executing accept() calls on this file descriptor to steal connections that should have gone to the PHP FPM master process. A simplistic fix is presumably to set FD_CLOEXEC on the listening sockets in the master process. Even the accept()ed connections. A more comprehensive fix is probably in the system() function to first close all descriptors before performing the actual system() call, and setting up stdin to come from /dev/null in the case of non-cli SAPIs. Or at the very least one could redirect fd 0 to utilize the php://stdin stream. This seemingly relates to bug #67383. Test script: --------------- <?php header("Content-Type: text/plain"); system("ls -la /proc/self/fd/; /sbin/ss -xtap"); ?> Expected result: ---------------- I would like to see only three open file descriptors being listed in the ls statement: 0 <= /dev/null (probably) 1 => pipe() 2 => /dev/null (as per current). (and 3 to /proc/32264/fd for the readdir cycle) Actual result: -------------- total 0 dr-x------ 2 nobody jkroon 0 Mar 8 15:25 . dr-xr-xr-x 8 nobody jkroon 0 Mar 8 15:25 .. lrwx------ 1 nobody jkroon 64 Mar 8 15:25 0 -> socket:[8376] l-wx------ 1 nobody jkroon 64 Mar 8 15:25 1 -> pipe:[572571] lrwx------ 1 nobody jkroon 64 Mar 8 15:25 2 -> /dev/null lr-x------ 1 nobody jkroon 64 Mar 8 15:25 3 -> /proc/32264/fd lrwx------ 1 nobody jkroon 64 Mar 8 15:25 4 -> socket:[572570] Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port ... u_str LISTEN 0 0 /var/run/php-fpm/www 8376 * 0 users:(("ss",pid=32265,fd=0),("list_fds.sh",pid=32263,fd=0),("sh",pid=32262,fd=0),("php-fpm",pid=32261,fd=0)) ... u_str ESTAB 0 0 /var/run/php-fpm/www 572570 * 0 users:(("ss",pid=32265,fd=4),("list_fds.sh",pid=32263,fd=4),("sh",pid=32262,fd=4),("php-fpm",pid=32261,fd=4)) PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Wed Oct 29 15:00:02 2025 UTC |
Workaround for run $command in exec: exec $command 3>&- 4>&- 5>&- 6>&- 7>&- 8>&- 9>&- 10>&- 11>&- 12>&- 13>&- 14>&- 15>&- 16>&- 17>&- 18>&- 19>&- 20>&- 21>&- 22>&- 23>&- 24>&- 25>&- 26>&- 27>&- 28>&- 29>&- 30>&- In code it is look like: $command = 'exec '.$command.' '.implode('>&- ', range(3, 30)).'>&-'; Now original command will use only fds 0, 1 and 2. With defined explicit on exec. Require bash which understand syntax 'fd>&-' for close fd.We host customer code ... so telling me a workaround is to do system("... &>/dev/null") or some variant thereof (killing off fd per fd) is just absolutely stupid insane. More to the point, fd=0 is original listening socket. So a remote hack can start doing accept() and intercepting posts, which could include login credentials. With some trickery it can forward this to legitimate processors conceivably, thus effectively installing a MITM right on the hosting machine. This is an escalation attack, so first need to exploit something else before this can be directly attacked.