Esegui eseguibile da php senza generare una shell

Devo call un eseguibile da un context imposto di uno script PHP. Sia per quanto riguarda le performance che per la sicurezza, è meglio non call mai una shell tra process ed eseguibile del server web.

Certo che ho cercato sul web, senza successo (in un context PHP). Molte altre lingue lo permettono e lo documentano chiaramente.

Purtroppo, backtick, exec() , shell_exec() , passthru() , system() , proc_open() , popen() chiamano una shell. E pcntl_fork() sembra non disponibile.

Come verificare se una function chiama una shell o less.

Questo è stato testato su un Debian 6 a 64 bit con PHP 5.3.3-7 + squeeze15. Codice di prova su http://pastebin.com/y4C7MeJz

Per get un test significativo ho usato un trucco che è quello di chiedere di eseguire un command shell non disponibile come eseguibile. Un buon esempio è umask . Qualsiasi function che restituisce qualcosa come 0022 ha sicuramente definito una shell. exec() , shell_exec() , passthru() , system() , proc_open() tutto fatto. Vedi i risultati dettagliati su http://pastebin.com/RBcBz02F .

pcntl_fork fallisce

Ora, torna l'objective: come eseguire un programma arbitrario senza avviare una shell?

L'exec di Php prende come previsto una serie di argomenti di string invece di una string univoca. Ma pcntl_fork ferma solo le richieste senza nemless un log.

Modifica: l'errore pcntl_fork è dovuto al fatto che il server utilizza il mod_php di Apache, vedere http://www.php.net/manual/en/function.pcntl-fork.php#49949 .

Modifica: aggiunto popen() ai test, seguendo il suggerimento @hakre.

Ogni suggerimento è apprezzato.

Per rispondere alla tua frase:

Sia per quanto riguarda le performance che per la sicurezza, è meglio non call mai una shell tra process ed eseguibile del server web.

Per quanto riguarda le performance, beh, sì, le forchette interne PHP, e anche la shell stessa si biforca, quindi è un po 'pesante. Ma hai davvero bisogno di eseguire molti processi per considerare questi problemi di performance.

A proposito di sicurezza, non vedo alcun problema qui. PHP ha la function escapeshellarg per disinfettare gli argomenti.

L'unico vero problema che ho incontrato con exec senza pcntl non è una risorsa né un problema di sicurezza: è davvero difficile creare veri deamons (senza alcun attaccamento al suo genitore, in particolare Apache ). Ho risolto questo problema usando, dopo aver ejar il mio command:

 $arg1 = escapeshellarg($arg1); $arg2 = escapeshellarg($arg2); $command = escapeshellarg("/some/bin $arg1 $arg2 > /dev/null 2>&1 &"); exec("$command | at now -M"); 

Per tornare alla tua domanda, l'unico modo che conosco per eseguire programmi in un modo standard (fork + exec) è usare l'estensione PCNTL (come già menzionato). Comunque, buona fortuna!


Per completare la mia risposta, puoi creare tu stesso una function exec che faccia la stessa cosa di pcntl_fork + pcntl_exec .

Ho creato un'estensione my_exec che fa un classico exec + fork, ma in realtà, non credo che risolverà i tuoi problemi se stai utilizzando questa function in Apache , perché verrà applicato lo stesso comportmento di pcntl_fork (apache2 sarà biforcato e lì possono essere comportmenti imprevisti con cattura del segnale e così via quando execv non riesce).

config.m4 il file di configuration di phpize

 PHP_ARG_ENABLE(my_exec_extension, whether to enable my extension, [ --enable-my-extension Enable my extension]) if test "$PHP_MY_EXEC_EXTENSION" = "yes"; then AC_DEFINE(HAVE_MY_EXEC_EXTENSION, 1, [Whether you have my extension]) PHP_NEW_EXTENSION(my_exec_extension, my_exec_extension.c, $ext_shared) fi 

my_exec_extension.c l'estensione

 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define PHP_MY_EXEC_EXTENSION_VERSION "1.0" #define PHP_MY_EXEC_EXTENSION_EXTNAME "my_exec_extension" extern zend_module_entry my_exec_extension_module_entry; #define phpext_my_exec_extension_ptr &my_exec_extension_module_entry // declaration of a custom my_exec() PHP_FUNCTION(my_exec); // list of custom PHP functions provided by this extension // set {NULL, NULL, NULL} as the last record to mark the end of list static function_entry my_functions[] = { PHP_FE(my_exec, NULL) {NULL, NULL, NULL} }; // the following code creates an entry for the module and registers it with Zend. zend_module_entry my_exec_extension_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif PHP_MY_EXEC_EXTENSION_EXTNAME, my_functions, NULL, // name of the MINIT function or NULL if not applicable NULL, // name of the MSHUTDOWN function or NULL if not applicable NULL, // name of the RINIT function or NULL if not applicable NULL, // name of the RSHUTDOWN function or NULL if not applicable NULL, // name of the MINFO function or NULL if not applicable #if ZEND_MODULE_API_NO >= 20010901 PHP_MY_EXEC_EXTENSION_VERSION, #endif STANDARD_MODULE_PROPERTIES }; ZEND_GET_MODULE(my_exec_extension) char *concat(char *old, char *buf, int buf_len) { int str_size = strlen(old) + buf_len; char *str = malloc((str_size + 1) * sizeof(char)); snprintf(str, str_size, "%s%s", old, buf); str[str_size] = '\0'; free(old); return str; } char *exec_and_return(char *command, char **argv) { int link[2], readlen; pid_t pid; char buffer[4096]; char *output; output = strdup(""); if (pipe(link) < 0) { return strdup("Could not pipe!"); } if ((pid = fork()) < 0) { return strdup("Could not fork!"); } if (pid == 0) { dup2(link[1], STDOUT_FILENO); close(link[0]); if (execv(command, argv) < 0) { printf("Command not found or access denied: %s\n", command); exit(1); } } else { close(link[1]); while ((readlen = read(link[0], buffer, sizeof(buffer))) > 0) { output = concat(output, buffer, readlen); } wait(NULL); } return output; } PHP_FUNCTION(my_exec) { char *command; int command_len, argc, i; zval *arguments, **data; HashTable *arr_hash; HashPosition pointer; char **argv; // recovers a string (s) and an arrays (a) from arguments if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &command, &command_len, &arguments) == FAILURE) { RETURN_NULL(); } arr_hash = Z_ARRVAL_P(arguments); // creating argc and argv from our argument arrays argc = zend_hash_num_elements(arr_hash); argv = malloc((argc + 1) * sizeof(char *)); argv[argc] = NULL; for ( i = 0, zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer) ) { if (Z_TYPE_PP(data) == IS_STRING) { argv[i] = malloc((Z_STRLEN_PP(data) + 1) * sizeof(char)); argv[i][Z_STRLEN_PP(data)] = '\0'; strncpy(argv[i], Z_STRVAL_PP(data), Z_STRLEN_PP(data)); i++; } } char *output = exec_and_return(command, argv); // freeing allocated memory for (i = 0; (i < argc); i++) { free(argv[i]); } free(argv); // WARNING! I guess there is a memory leak here. // Second arguemnt to 1 means to PHP: do not free memory // But if I put 0, I get a segmentation fault // So I think I do not malloc correctly for a PHP extension. RETURN_STRING(output, 1); } 

test.php un esempio di utilizzo

 <?php dl("my_exec.so"); $output = my_exec("/bin/ls", arrays("-l", "/")); var_dump($output); 

lo script di shell esegue questi comandi, ovviamente usa la propria directory dei moduli

 phpize ./configure make sudo cp modules/my_exec_extension.so /opt/local/lib/php/extensions/no-debug-non-zts-20090626/my_exec.so 

Risultato

 KolyMac:my_fork ninsuo$ php test.php string(329) ".DS_Store .Spotlight-V100 .Trashes .file .fseventsd .hidden .hotfiles.btree .vol AppleScript Applications Developer Installer Log File Library Microsoft Excel Documents Microsoft Word Documents Network System Users Volumes bin cores dev etc home lost+found mach_kernel net opt private sbin tmp usr var vc_command.txt vidotask.txt" 

Non sono un C dev, quindi penso che ci siano modi più puliti per raggiungere questo objective. Ma tu hai l'idea.

Id considera di provare pcntl_exec ()

Non è ansible avviare alcun programma arbitrario senza shell: la shell è l'involucro attorno al programma in esecuzione che fornisce le impostazioni dell'ambiente, come PWD e PATH.

Se si desidera la possibilità di eseguire comandi di sistema senza avviare una shell, riscrivere i comandi di sistema come moduli PHP e installarli nel server PHP ed eseguirli come funzioni.