Download | Plain Text | No Line Numbers


  1. This patch is based on http://kyberdigi.cz/projects/execdir/ but instead trying
  2. to sanitize the exec command string inside PHP this job is handled by bash and
  3. a special restricted PHP-mode which strips off all path informations.
  4.  
  5. This patch depends on:
  6. - bash restricted php mode patch
  7. https://manuel.mausz.at/coding/patches/php/bash-4.2-restricted-php.patch
  8.  
  9. No warranty!
  10. diff -Naur php-7.0.15.orig/ext/standard/exec.c php-7.0.15/ext/standard/exec.c
  11. --- php-7.0.15.orig/ext/standard/exec.c 2017-01-17 12:52:57.000000000 +0100
  12. +++ php-7.0.15/ext/standard/exec.c 2017-02-16 18:04:19.948005831 +0100
  13. @@ -96,21 +96,30 @@
  14. char *buf;
  15. size_t l = 0;
  16. int pclose_return;
  17. - char *b, *d=NULL;
  18. + char *cmd_p, *b, *d=NULL;
  19. php_stream *stream;
  20. size_t buflen, bufl = 0;
  21. #if PHP_SIGCHILD
  22. void (*sig_handler)() = NULL;
  23. #endif
  24.  
  25. + if (PG(exec_dir) && strlen(PG(exec_dir))) {
  26. + zend_string *cmd_esc = php_escape_shell_arg(cmd);
  27. + spprintf(&d, 0, "PATH=%s /bin/bash --php -rc %s", PG(exec_dir), ZSTR_VAL(cmd_esc));
  28. + zend_string_release(cmd_esc);
  29. + cmd_p = d;
  30. + } else {
  31. + cmd_p = cmd;
  32. + }
  33. +
  34. #if PHP_SIGCHILD
  35. sig_handler = signal (SIGCHLD, SIG_DFL);
  36. #endif
  37.  
  38. #ifdef PHP_WIN32
  39. - fp = VCWD_POPEN(cmd, "rb");
  40. + fp = VCWD_POPEN(cmd_p, "rb");
  41. #else
  42. - fp = VCWD_POPEN(cmd, "r");
  43. + fp = VCWD_POPEN(cmd_p, "r");
  44. #endif
  45. if (!fp) {
  46. php_error_docref(NULL, E_WARNING, "Unable to fork [%s]", cmd);
  47. @@ -515,7 +524,7 @@
  48. PHP_FUNCTION(shell_exec)
  49. {
  50. FILE *in;
  51. - char *command;
  52. + char *command, *command_p;
  53. size_t command_len;
  54. zend_string *ret;
  55. php_stream *stream;
  56. @@ -524,14 +533,24 @@
  57. return;
  58. }
  59.  
  60. + if (PG(exec_dir) && strlen(PG(exec_dir))) {
  61. + zend_string *cmd_esc = php_escape_shell_arg(command);
  62. + spprintf(&command_p, 0, "PATH=%s /bin/bash --php -rc %s", PG(exec_dir), ZSTR_VAL(cmd_esc));
  63. + zend_string_release(cmd_esc);
  64. + } else {
  65. + command_p = estrdup(command);
  66. + }
  67. +
  68. #ifdef PHP_WIN32
  69. - if ((in=VCWD_POPEN(command, "rt"))==NULL) {
  70. + if ((in=VCWD_POPEN(command_p, "rt"))==NULL) {
  71. #else
  72. - if ((in=VCWD_POPEN(command, "r"))==NULL) {
  73. + if ((in=VCWD_POPEN(command_p, "r"))==NULL) {
  74. #endif
  75. php_error_docref(NULL, E_WARNING, "Unable to execute '%s'", command);
  76. + efree(command_p);
  77. RETURN_FALSE;
  78. }
  79. + efree(command_p);
  80.  
  81. stream = php_stream_fopen_from_pipe(in, "rb");
  82. ret = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0);
  83. diff -Naur php-7.0.15.orig/ext/standard/file.c php-7.0.15/ext/standard/file.c
  84. --- php-7.0.15.orig/ext/standard/file.c 2017-01-17 12:52:56.000000000 +0100
  85. +++ php-7.0.15/ext/standard/file.c 2017-02-16 18:04:19.951005797 +0100
  86. @@ -927,7 +927,16 @@
  87. }
  88. #endif
  89.  
  90. - fp = VCWD_POPEN(command, posix_mode);
  91. + if (PG(exec_dir) && strlen(PG(exec_dir))) {
  92. + zend_string *cmd_esc = php_escape_shell_arg(command);
  93. + char *tmp;
  94. + spprintf(&tmp, 0, "PATH=%s /bin/bash --php -rc %s", PG(exec_dir), ZSTR_VAL(cmd_esc));
  95. + fp = VCWD_POPEN(tmp, posix_mode);
  96. + efree(tmp);
  97. + zend_string_release(cmd_esc);
  98. + } else {
  99. + fp = VCWD_POPEN(command, posix_mode);
  100. + }
  101. if (!fp) {
  102. php_error_docref2(NULL, command, posix_mode, E_WARNING, "%s", strerror(errno));
  103. efree(posix_mode);
  104. diff -Naur php-7.0.15.orig/ext/standard/filestat.c php-7.0.15/ext/standard/filestat.c
  105. --- php-7.0.15.orig/ext/standard/filestat.c 2017-01-17 12:52:57.000000000 +0100
  106. +++ php-7.0.15/ext/standard/filestat.c 2017-02-16 18:04:34.854835939 +0100
  107. @@ -846,13 +846,14 @@
  108. "size", "atime", "mtime", "ctime", "blksize", "blocks"
  109. };
  110. const char *local;
  111. + char local_safe[MAXPATHLEN];
  112. php_stream_wrapper *wrapper;
  113.  
  114. if (!filename_length) {
  115. RETURN_FALSE;
  116. }
  117.  
  118. - if ((wrapper = php_stream_locate_url_wrapper(filename, &local, 0)) == &php_plain_files_wrapper && php_check_open_basedir(local)) {
  119. + if ((wrapper = php_stream_locate_url_wrapper(filename, &local, 0)) == &php_plain_files_wrapper && type != FS_IS_X && php_check_open_basedir(local)) {
  120. RETURN_FALSE;
  121. }
  122.  
  123. @@ -877,6 +878,19 @@
  124. #endif
  125. #ifdef X_OK
  126. case FS_IS_X:
  127. + if (PG(exec_dir) && strlen(PG(exec_dir))) {
  128. + if (strstr(local, "..")) {
  129. + RETURN_FALSE;
  130. + } else {
  131. + char *b = strrchr(local, PHP_DIR_SEPARATOR);
  132. + if (b) {
  133. + snprintf(local_safe, MAXPATHLEN, "%s%s", PG(exec_dir), b);
  134. + } else {
  135. + snprintf(local_safe, MAXPATHLEN, "%s%c%s", PG(exec_dir), PHP_DIR_SEPARATOR, local);
  136. + }
  137. + local = (char *)&local_safe;
  138. + }
  139. + }
  140. RETURN_BOOL(VCWD_ACCESS(local, X_OK) == 0);
  141. break;
  142. #endif
  143. diff -Naur php-7.0.15.orig/ext/standard/proc_open.c php-7.0.15/ext/standard/proc_open.c
  144. --- php-7.0.15.orig/ext/standard/proc_open.c 2017-01-17 12:52:57.000000000 +0100
  145. +++ php-7.0.15/ext/standard/proc_open.c 2017-02-16 18:04:19.952005786 +0100
  146. @@ -492,6 +492,15 @@
  147.  
  148. command_len = strlen(command);
  149.  
  150. + zval envpath;
  151. + if (PG(exec_dir) && strlen(PG(exec_dir))) {
  152. + if (!environment) {
  153. + array_init_size(&envpath, 1);
  154. + environment = &envpath;
  155. + }
  156. + add_assoc_string(environment, "PATH", PG(exec_dir));
  157. + }
  158. +
  159. if (environment) {
  160. env = _php_array_to_envp(environment, is_persistent);
  161. } else {
  162. @@ -846,10 +855,14 @@
  163. php_ignore_value(chdir(cwd));
  164. }
  165.  
  166. - if (env.envarray) {
  167. - execle("/bin/sh", "sh", "-c", command, NULL, env.envarray);
  168. + if (PG(exec_dir) && strlen(PG(exec_dir))) {
  169. + execle("/bin/bash", "bash", "--php", "-rc", command, NULL, env.envarray);
  170. } else {
  171. - execl("/bin/sh", "sh", "-c", command, NULL);
  172. + if (env.envarray) {
  173. + execle("/bin/sh", "sh", "-c", command, NULL, env.envarray);
  174. + } else {
  175. + execl("/bin/sh", "sh", "-c", command, NULL);
  176. + }
  177. }
  178. _exit(127);
  179.  
  180. diff -Naur php-7.0.15.orig/main/main.c php-7.0.15/main/main.c
  181. --- php-7.0.15.orig/main/main.c 2017-01-17 12:53:05.000000000 +0100
  182. +++ php-7.0.15/main/main.c 2017-02-16 18:04:19.953005774 +0100
  183. @@ -576,6 +576,7 @@
  184. #ifdef PHP_WIN32
  185. STD_PHP_INI_BOOLEAN("windows.show_crt_warning", "0", PHP_INI_ALL, OnUpdateBool, windows_show_crt_warning, php_core_globals, core_globals)
  186. #endif
  187. + STD_PHP_INI_ENTRY("exec_dir", NULL, PHP_INI_SYSTEM, OnUpdateString, exec_dir, php_core_globals, core_globals)
  188. PHP_INI_END()
  189. /* }}} */
  190.  
  191. diff -Naur php-7.0.15.orig/main/php_globals.h php-7.0.15/main/php_globals.h
  192. --- php-7.0.15.orig/main/php_globals.h 2017-01-17 12:53:05.000000000 +0100
  193. +++ php-7.0.15/main/php_globals.h 2017-02-16 18:04:19.953005774 +0100
  194. @@ -168,6 +168,7 @@
  195. #ifdef PHP_WIN32
  196. zend_bool windows_show_crt_warning;
  197. #endif
  198. + char *exec_dir;
  199. };
  200.  
  201.  
  202. diff -Naur php-7.0.15.orig/php.ini-development php-7.0.15/php.ini-development
  203. --- php-7.0.15.orig/php.ini-development 2017-01-17 12:52:04.000000000 +0100
  204. +++ php-7.0.15/php.ini-development 2017-02-16 18:04:19.954005763 +0100
  205. @@ -293,6 +293,10 @@
  206. ; http://php.net/open-basedir
  207. ;open_basedir =
  208.  
  209. +; Only executables located in the exec_dir will be allowed to be executed
  210. +; via the exec family of functions.
  211. +exec_dir =
  212. +
  213. ; This directive allows you to disable certain functions for security reasons.
  214. ; It receives a comma-delimited list of function names.
  215. ; http://php.net/disable-functions
  216. diff -Naur php-7.0.15.orig/php.ini-production php-7.0.15/php.ini-production
  217. --- php-7.0.15.orig/php.ini-production 2017-01-17 12:52:04.000000000 +0100
  218. +++ php-7.0.15/php.ini-production 2017-02-16 18:04:19.954005763 +0100
  219. @@ -293,6 +293,10 @@
  220. ; http://php.net/open-basedir
  221. ;open_basedir =
  222.  
  223. +; Only executables located in the exec_dir will be allowed to be executed
  224. +; via the exec family of functions.
  225. +exec_dir =
  226. +
  227. ; This directive allows you to disable certain functions for security reasons.
  228. ; It receives a comma-delimited list of function names.
  229. ; http://php.net/disable-functions
  230.