A method to prevent shell injection in Linux C
By building the exec function family package library, using structure arrays, pipelines, redirection and other features, the protection of shell injection in Linux C is achieved, which solves the problem of lack of general methods in the existing technology, simplifies code logic and prevents injection attack.
Patent Information
- Application Number
- CN202410791653.X
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- Filing Date
- 2024-06-19
- Publication Date
- 2025-01-03
- Estimated Expiration
- 2044-06-19
AI Technical Summary
There is a lack of a universal and complete method in the prior art to prevent shell injection attacks in Linux C. Developers need to target the filtering and escape the parameters entered by users, and bypassing shell commands is cumbersome and not very popular.
By building a package library that implements the exec function family to run commands, it provides secure_exec, secure_system, secure_popen and secure_pclose functions, and uses structural arrays and pipelines, redirections, logical operators and other features to completely bypass the shell to execute commands to prevent injection attacks.
Implements a common method of shell injection protection in Linux C, simplifying code logic, supporting pipelines, redirection and logic operators, preventing injection attacks, and reducing code complexity.
Smart Images

Figure CN118797638B_ABST
Abstract
Description
Technical Field
[0001] The present invention relates to the technical field of information security, and in particular to a shell injection prevention method in Linux C. Background Art
[0002] Applications sometimes need to call some system command functions, such as system() and popen() in Linux C. When users can control the parameters in these functions, malicious system commands can be spliced into normal commands, thereby causing command injection attacks on device systems. These command injections can inherit the permissions of applications to execute system commands and read and write executable files, causing the system to be maliciously attacked or leak system user information.
[0003] Currently, there is no universal and perfect method to deal with shell injection attacks in Linux C. In most cases, developers need to filter and escape the parameters input by users in a targeted manner to prevent shell injection. The content that needs to be filtered and escaped will also change according to the context of command execution, the type of input parameters, etc. This not only relies on the experience of developers, but also introduces additional code processing logic outside the normal code logic, increasing the complexity of the code.
[0004] Another method to prevent shell injection is to run commands through the exec family of functions, completely bypassing the shell. This method does not have the risk of shell injection, but running a command requires at least dozens of lines of code support, and because it lacks available encapsulation, if you need to use common shell features such as pipes and redirection, you need to implement them separately. It is cumbersome to use and is not widely used. Summary of the invention
[0005] The purpose of the present invention is to overcome the shortcomings of the above-mentioned prior art and provide a method for shell injection prevention in Linux C, which can enable commands to be executed completely without the shell mode and provide support for common features such as pipelines, redirection, logical operators, background operation, etc., which is easy to use.
[0006] The present invention is achieved through the following technical solutions:
[0007] A method for preventing shell injection in Linux C, comprising the following steps:
[0008] S1. Build an encapsulation library that implements the exec function family to run commands. The encapsulation library mainly provides the following interfaces: secure_exec function, secure_system function, secure_popen function, and secure_pclose function.
[0009] secure_exec: This function is the core function of this encapsulation library. It implements all functions of this encapsulation library except background execution and logical operators. Other functions are secondary encapsulation based on it. This function mainly runs a group of commands by defining a structure array SECURE_EXEC_PROCESS_T. This group of commands forms a connected pipeline. Each command in it forks a process separately and runs through the execvp function in the child process. secure_exec uses the standard output of the previous process as the standard input of the next process according to the order of the structure array, or specifies the file descriptor to connect through the pipe+dup2 function to achieve functions similar to shell pipes and redirection. The secure_exec function will eventually return the pid of the last process in the pipeline and the optional outgoing file descriptor bound to the standard input of the first process in the pipeline.
[0010] secure_system: This function is similar to the system function in Linux C. It supports pipes, redirection, logical operators, and background execution. It also supports passing parameter lists in the form of formatted strings + variable parameters, such as secure_system("echo-n%s:%d|md5sum>%s", username, id, filename). The secure_system function converts the command and parameter list represented by a string into an array of SECURE_EXEC_PROCESS_T structures, then calls the secure_exec function to run the command, and waits for the last process in the pipeline to complete execution before returning its process exit status.
[0011] secure_popen: This function is similar to the popen function in Linux C. Like secure_system, it also supports pipes, redirection, and logical operators. It also supports passing in parameter lists in the form of formatted strings + variable parameters. This function converts the command and parameter list represented by a string into an array of SECURE_EXEC_PROCESS_T structures, then calls the secure_exec function to run the command and returns a structure that contains a readable file pointer and the pid of the child process that executes the command. The file pointer is used to read the data output by the command. The return value of the popen function in Linux C is a file pointer, while the return value of secure_popen is a structure that contains information such as the file pointer and the pid of the child process that executes the command.
[0012] secure_pclose: This function is similar to the pclose function in Linux C. It is used to release the resources created by the secure_popen function, wait for the command executed by secure_popen to end, and return the process exit status. The pclose function in Linux C is used to release the resources created by popen.
[0013] S2. When a command is passed in, secure_system and secure_popen pass in the command in the form of a formatted string (the command parameter of the function), and pass in the parameters required for the formatted characters through a variable parameter list. The command parameter describes which are the commands themselves and which are the parameters required by the command. secure_system divides the command into multiple groups based on logical operators. Each group is a pipeline with multiple executable commands on the pipeline. Each command is disassembled into different parts (command executable program, parameter list, redirection target), and each part is filled with formatted characters through vsnprintf. Then, the parsed results are used to create a SECURE_EXEC_PROCESS_T structure. This process ensures that the dynamically passed in parameters will not become a new command execution. Therefore, the parameters in the variable parameter list of secure_system are ultimately regarded as ordinary parameters by the command and will not retain any special meaning. Characters that need to retain special meanings are written in the command parameter, such as pipes, redirections, logical operators, spaces, etc. secure_system and secure_popen parse the incoming command into an array of SECURE_EXEC_PROCESS_T structures, and then execute the command through the secure_exec function. After the command is executed, secure_system waits for the last process in the pipeline to complete execution and then returns its termination status, and secure_pclose waits for the process created by secure_popen to end and returns its termination status.
[0014] The system and popen functions in Linux C both run the passed-in commands through sh, and the commands need to be spliced before the function is called, which is one of the reasons for shell injection. However, the secure_system and secure_popen in the present invention both convert the command and parameter list represented by a string into an array of SECURE_EXEC_PROCESS_T structures and then pass them into secure_exec to run the command. The execvp encapsulated inside secure_exec does not pass through sh at all during the command running process, and the commands are assembled in the form of formatted strings inside the function, which makes it possible for secure_system and secure_popen to determine which are the commands themselves and which are the passed-in parameters, thereby preventing the passed-in parameters from becoming a new command to be executed, thereby playing a role in anti-injection.
[0015] Furthermore, the structure array (SECURE_EXEC_PROCESS_T[]) includes at least one structure, and each structure includes: a required command name, an optional command parameter list, a standard output, an error output redirection method, and an output direction.
[0016] Furthermore, the redirection method of the error output includes redirection to a file descriptor, a file or appending to a file, and the output direction includes outputting to a file or a file descriptor.
[0017] Furthermore, the secure_exec is run as follows:
[0018] S11. The secure_exec function traverses the passed SECURE_EXEC_PROCESS_T structure array internally, creates two pipes for each array member, one for connecting the standard output pipe and the other for connecting the standard input pipe of the child process, and the other for connecting the standard input and standard output of the child process, and forks a child process at the same time.
[0019] S12. If the child process is the first process in the SECURE_EXEC_PROCESS_T structure array (equivalent to the first process in the shell pipeline), and there is a wfd parameter when calling secure_exec, the read end of the standard input pipeline of the process is redirected to the read end of the standard input of the child process created by the parent process through dup2, and the write end of the pipeline is passed out through wfd in the parent process, and then step S15 is executed. The role of wfd is that if the first process in the SECURE_EXEC_PROCESS_T structure array needs to receive data from the standard input, it can be written through wfd. For example, when base64 is used as the first process, it needs to receive data from the standard input before it can continue to execute. At this time, the data can be passed to the standard input of base64 through wfd.
[0020] S13. If the child process is the first process in the SECURE_EXEC_PROCESS_T structure array, but the wfd parameter does not exist when secure_exec is called, then directly execute step S15.
[0021] S14, if the child process is not the first process in the SECURE_EXEC_PROCESS_T structure array, redirect the read end of the standard output pipe connected to the previous child process to the standard input of the current child process through dup2, and then execute step S15, thereby realizing the function of the shell pipe.
[0022] S15. Redirect the standard output and standard error of the process through dup2 according to the standard output and standard error redirection rules defined in SECURE_EXEC_PROCESS_T. If the current child process does not specify a redirection rule, the standard output of the child process is redirected to the write end of the standard output pipe created by the parent process for connecting to the child process by default, and the standard error output is not processed.
[0023] S16. In each child process, the command and parameter list to be executed are passed to the execvp function, and the command is executed according to the command and params specified by SECURE_EXEC_PROCESS_T;
[0024] S17. After traversing the SECURE_EXEC_PROCESS_T structure array, if the wfd parameter still exists, the write end of the standard input pipe of the first child process in the pipeline is passed out, and then the function is exited, and the pid of the last child process in the pipeline is returned; if the wfd parameter does not exist after traversing all SECURE_EXEC_PROCESS_T structure arrays, the function is exited directly, and the pid of the last child process in the pipeline is returned.
[0025] Furthermore, the secure_system is operated as follows:
[0026] S21. The secure_system function first determines whether the command is executed in the background. If it is executed in the background, a child process is forked first, and all subsequent processing is performed in the child process; if it is not executed in the background, subsequent processing is completed directly in the current process.
[0027] S22. Decompose the command into multiple command groups according to the logical operators in the command, each command group contains a group of commands that need to be run currently and the prerequisites for the next group of commands to be run. For example: the command cmd1|cmd2&&cmd3||cmd4 will be decomposed into three groups. The prerequisite for the next group of commands to be run in the first group of commands cmd1|cmd2 is true, indicating that the next group of commands will be run when the final execution result of this group of commands is successful. The prerequisite for the next group of commands to be run in the second group of commands cmd3 is false, indicating that the next group of commands will be run when the final execution result of this group of commands is a failure. The third group of commands cmd4 is the last group of commands, so there is no prerequisite for the next group of commands to be run.
[0028] S23, traversing the command group in the pipeline execution order, and splitting them respectively with the pipeline descriptor "|" to obtain at least one command, and then traversing each part of the command by space to split the executable program name, parameter list and redirection operator in the command; respectively detecting whether the executable program name, parameter list and redirection operator contain the formatting symbol "%", if so, filling the formatting character through the variable parameter list valist received by vsnprintf and secure_system, and converting these parameters into a SECURE_EXEC_PROCESS_T structure;
[0029] S24. After converting all commands in the command group into a SECURE_EXEC_PROCESS_T structure according to the method in step S23, a SECURE_EXEC_PROCESS_T structure array is obtained, and the structure array is passed as a parameter to the secure_exec function to run the command. The secure_exec function returns the pid of the last command process in this group of commands, waits for the process to end through waitpid, and then obtains the exit status of the process through WEXITSTATUS;
[0030] S25. Compare the process exit status after executing step S24 with the prerequisites for executing the next set of commands. If they match, run the next set of commands according to the method of steps S22-S24. If they do not match, skip the next set of commands, obtain the execution prerequisites of the next set of commands and compare them, and repeat this process until all commands are executed.
[0031] Furthermore, the secure_popen operation method is:
[0032] S31, secure_popen first creates a pipe connecting the child process through the pipe function, and then forks a child process;
[0033] S32, the main process converts the read-end file descriptor of the pipe into a file pointer through fdopen, and constructs the file pointer and the pid of the child process into a SECURE_POPEN_FP_T structure and returns it to the function caller; the child process redirects the standard output of the child process itself to the write end of the pipe, so as to pass the content output of subsequent command execution to the parent process;
[0034] S33, decomposing the command command into multiple command groups according to the logical operators in the command, each command group including a group of commands that need to be currently executed and a prerequisite for the execution of the next group of commands;
[0035] S34, traversing the command group in the pipeline execution order, and splitting them respectively with the pipeline descriptor "|" to obtain at least one command, and then traversing each part of the command by space to split the executable program name, parameter list and redirection operator in the command; respectively detecting whether the executable program name, parameter list and redirection operator contain the formatting symbol "%", if so, filling the formatting character through the variable parameter list valist received by vsnprintf and secure_system, and converting these parameters into a SECURE_EXEC_PROCESS_T structure;
[0036] S35. After converting all commands in the command group into a SECURE_EXEC_PROCESS_T structure according to the method in step S34, a SECURE_EXEC_PROCESS_T structure array is obtained, and the structure array is passed as a parameter to the secure_exec function to run the command. The secure_exec function returns the pid of the last command process in this group of commands, waits for the process to end through waitpid, and then obtains the exit status of the process through WEXITSTATUS;
[0037] S36. Compare the process exit status after executing step S35 with the prerequisites for executing the next set of commands. If they match, run the next set of commands according to the method of steps S33-S35. If they do not match, skip the next set of commands, obtain the execution prerequisites of the next set of commands and compare them, and repeat this process until all commands are executed.
[0038] Furthermore, the secure_pclose operation method is as follows: the secure_pclose function first obtains the pid of the associated child process from efp, waits for the child process to exit through waitpid, then obtains the process exit status through WEXITSTATUS, closes the associated file pointer in efp, exits the function and returns the process exit status code.
[0039] The present invention constructs an encapsulation library for realizing the command running of the exec function family. The encapsulation library mainly provides four interfaces, namely, secure_exec function, secure_system function, secure_popen function and secure_pclose function. Through these functions, commands can be executed in a manner completely bypassing the shell, and support for common features such as pipelines, redirection, logical operators and background operation is provided, so that additional injection commands can be prevented from being generated, and an anti-injection function is played, so as to make up for the lack of a general method for dealing with shell injection attacks in Linux C. BRIEF DESCRIPTION OF THE DRAWINGS
[0040] Figure 1 It is a workflow diagram of secure_exec of the present invention.
[0041] Figure 2 This is a flowchart of the secure_system of the present invention.
[0042] Figure 3 It is the workflow diagram of secure_popen of the present invention.
[0043] Figure 4 It is a workflow diagram of secure_pclose of the present invention. DETAILED DESCRIPTION
[0044] A method for preventing shell injection in Linux C, comprising the following steps:
[0045] S1. Build an encapsulation library that implements the exec function family (execl, execv, execle, execve, execlp and execvp) to run commands. The encapsulation library mainly provides the following interfaces to the outside: secure_exec function, secure_system function, secure_popen function, and secure_pclose function.
[0046] secure_exec: This function is the core function of this encapsulation library. It implements all functions of this encapsulation library except background execution and logical operators. Other functions are secondary encapsulation based on it. This function mainly runs a group of commands by defining a structure array SECURE_EXEC_PROCESS_T. This group of commands forms a connected pipeline. Each command in the command group forks a process separately and runs in the child process through the execvp function. secure_exec uses the standard output of the previous process as the standard input of the next process according to the order of the structure array, or specifies the file descriptor to connect through the pipe+dup2 function to achieve functions similar to shell pipes and redirection. Pipe is used to create a pipe, which is a special buffer that can transmit data unidirectionally between one process (the write end of the pipe) and another process (the read end of the pipe). Pipe creates an anonymous pipe and returns two file descriptors, one for reading (read end) and one for writing (write end). The dup2 function is used to duplicate file descriptors. Use dup2 to copy the write end of the pipe to the file descriptor of the standard output, and to copy the read end of the pipe to the file descriptor of the standard input, so as to facilitate the execution of commands or programs that need to be redirected. The secure_exec function will eventually return the pid of the last process in the pipeline and the optional outgoing file descriptor bound to the standard input of the first process in the pipeline.
[0047] The structure array (SECURE_EXEC_PROCESS_T[]) includes at least one structure, as shown in Table 1, each structure includes: a required command name, an optional command parameter list, standard output, an error output redirection method and an output direction. Among them, the error output redirection method includes redirection to a file descriptor, redirection to a file or appending to a file, and the output direction includes outputting to a file or a file descriptor.
[0048] secure_system: This function is similar to the system function in Linux C. It supports pipes, redirection, logical operators, and background execution. It also supports passing parameter lists in the form of formatted strings + variable parameters, such as secure_system("echo-n%s:%d|md5sum>%s", username, id, filename). The secure_system function converts the command and parameter list represented by a string into an array of SECURE_EXEC_PROCESS_T structures, then calls the secure_exec function to run the command, and waits for the last process in the pipeline to complete execution before returning its process exit status.
[0049] secure_popen: This function is similar to the popen function in Linux C. Like secure_system, it also supports pipes, redirection, and logical operators, and supports passing parameter lists in the form of formatted strings + variable parameters. This function converts the command and parameter list represented by a string into an array of SECURE_EXEC_PROCESS_T structures, then calls the secure_exec function to run the command, and returns a structure that contains a readable file pointer and the pid of the child process that executes the command. The file pointer is used to read the data output by the command. The return value of the popen function in Linux C is a file pointer, while the return value of secure_popen is a structure that contains information such as the file pointer and the pid of the child process that executes the command.
[0050] secure_pclose: This function is similar to the pclose function in Linux C. It is used to release the resources created by the secure_popen function, wait for the command executed by secure_popen to end, and return the process exit status. The pclose function in Linux C is used to release the resources created by popen.
[0051] S2. When a command is passed in, secure_system and secure_popen pass in the command in the form of a formatted string (the command parameter of the function), and pass in the parameters required for the formatted characters through a variable parameter list. The command parameter describes which are the commands themselves and which are the parameters required by the command. secure_system divides the command into multiple groups based on logical operators. Each group is a pipeline with multiple executable commands on the pipeline. Each command is disassembled into different parts (command executable program, parameter list, redirection target), and each part is filled with formatted characters through vsnprintf. Then, the parsed results are used to create a SECURE_EXEC_PROCESS_T structure. This process ensures that the dynamically passed in parameters will not become a new command execution. Therefore, the parameters in the variable parameter list of secure_system are ultimately regarded as ordinary parameters by the command and will not retain any special meaning. Characters that need to retain special meanings are written in the command parameter, such as pipes, redirections, logical operators, spaces, etc. secure_system and secure_popen parse the incoming command into SECURE_EXEC_PROCESS_T structure arrays respectively, and then pass the structure arrays to secure_exec. The secure_exec function executes the command. After the command is executed, secure_system waits for the last process in the pipeline to complete execution and then returns its termination status. secure_pclose waits for the process created by secure_popen to end and returns its termination status.
[0052] The system and popen functions in Linux C both run the passed-in commands through sh, and the commands need to be spliced before the function is called, which is one of the reasons for shell injection. The present invention, on the other hand, converts the command and parameter list represented by a string into an array of SECURE_EXEC_PROCESS_T structures through secure_system and secure_popen, and then passes it into secure_exec to run the command. The execvp encapsulated inside secure_exec does not pass through sh at all during the command running process, and the commands are assembled in the form of formatted strings inside the function, which enables secure_system and secure_popen to have the opportunity to determine which are the commands themselves and which are the passed-in parameters, thereby preventing the passed-in parameters from becoming a new command to be executed, thereby playing a role in anti-injection.
[0053] The following is a detailed discussion of the four main interfaces provided by the encapsulation library that implements the exec function family to run commands.
[0054] secure_exec is the core function of this wrapper library. It implements all functions of this wrapper library except background execution and logical operators. The function will eventually return the pid of the last process in the pipeline and the optional file descriptor bound to the standard input of the first process in the pipeline. The following example illustrates this.
[0055] Example 1: The following code is equivalent to executing echo -n abcdefg | openssl base64 -e | md5sum through popen and getting the return result
[0056]
[0057]
[0058] Example 2: The following code is equivalent to executing curl http: / / localhost-o-2> / dev / null|md5sum> / tmp / md5 through system
[0059]
[0060]
[0061] Example 3: The following code writes data to the standard input of the first process in the pipeline through the wfd parameter
[0062]
[0063] printf("write date error\n");
[0064] }
[0065] close(wfd);
[0066] status=read(pipefd[0],buf,sizeof(buf));
[0067] close(pipefd[0]);
[0068] assert(strcmp("b284de94d059bc628ce77f0c34b94299-\n",buf)==0);
[0069] return 0;
[0070] }
[0071] like Figure 1 As shown, the secure_exec operation method is:
[0072] S11. The secure_exec function traverses the passed SECURE_EXEC_PROCESS_T structure array internally, that is, Figure 1 In the processes array, two pipes are created for each array member, one for connecting the standard output pipe and the other for connecting the standard input and standard output of the child process, and a child process is forked at the same time.
[0073] S12. If the child process is the first process in the processes array (equivalent to the first process in the shell pipeline), and there is a wfd parameter when secure_exec is called, the read end of the standard input pipeline of the process is redirected to the read end of the standard input of the child process created by the parent process through dup2, and the write end of the pipeline is transmitted through wfd in the parent process, and then step S15 is executed. The role of wfd is that if the first process in the processes array needs to receive data from the standard input, it can be written through wfd. For example, when base64 is used as the first process, it needs to receive data from the standard input before it can continue to execute. At this time, the data can be passed to the standard input of base64 through wfd.
[0074] S13. If the child process is the first process in the processes array, but the wfd parameter does not exist when secure_exec is called, then directly execute step S15.
[0075] S14, if the child process is not the first process in the processes array, redirect the read end of the standard output pipe connected to the previous child process to the standard input of the current child process through dup2, and then execute step S15. Thus, the function of the shell pipe is realized.
[0076] S15. Redirect the standard output and standard error of the process through dup2 according to the standard output and standard error redirection rules defined in SECURE_EXEC_PROCESS_T. If the current child process does not specify a redirection rule, the standard output of the child process is redirected to the write end of the standard output pipe created by the parent process for connecting to the child process by default, and the standard error output is not processed.
[0077] S16. In each child process, the command and parameter list to be executed are passed to the execvp function, and the command is executed according to the command and params specified by SECURE_EXEC_PROCESS_T;
[0078] S17. After traversing the processes array, if the wfd parameter still exists, the write end of the standard input pipe of the first child process in the pipeline is passed out, and then the function is exited, and the pid of the last child process in the pipeline is returned; if the wfd parameter does not exist after traversing all the processes arrays, the function is exited directly and the pid of the last child process in the pipeline is returned.
[0079] The function of secure_system is similar to that of system function, but it needs to convert the command and parameter list represented by a string into an array of SECURE_EXEC_PROCESS_T structures. In this process, the command itself and the passed-in parameters are distinguished, and the passed-in parameters become a new command to be executed, thereby playing a role in preventing injection.
[0080] The following is a typical example of secure_system:
[0081] Its execution result is equivalent to executing echo -n "$username:$id" | md5sum > $filename in the shell.
[0082] static int test_secure_system()
[0083] {
[0084] char*username="root";
[0085] char*filename=" / tmp / test.txt";
[0086] int id = 1;
[0087] int status=secure_system("echo-n%s:%d|md5sum>%s",username,id,filename);
[0088] assert(status == 0);
[0089] return 0;
[0090] }
[0091] like Figure 2 As shown, the operation method of the secure_system is:
[0092] S21. The secure_system function first determines whether the command is executed in the background. If it is executed in the background, a child process is forked first, and all subsequent processing is performed in the child process; if it is not executed in the background, subsequent processing is completed directly in the current process.
[0093] S22. Decompose the command into multiple command groups according to the logical operators in the command, each command group contains a group of commands that need to be run currently and the prerequisites for the next group of commands to be run. For example: the command cmd1|cmd2&&cmd3||cmd4 will be decomposed into three groups. The prerequisite for the next group of commands to be run in the first group of commands cmd1|cmd2 is true, indicating that the next group of commands will be run when the final execution result of this group of commands is successful. The prerequisite for the next group of commands to be run in the second group of commands cmd3 is false, indicating that the next group of commands will be run when the final execution result of this group of commands is a failure. The third group of commands cmd4 is the last group of commands, so there is no prerequisite for the next group of commands to be run.
[0094] S23, traverse the command group (a group of commands currently to be run) in the pipeline execution order, and separate them respectively with the pipeline descriptor "|" to obtain at least one command, and then traverse each part of the command according to the space to separate the executable program name, parameter list and redirection operator in the command; respectively detect whether the executable program name, parameter list and redirection operator contain the formatting symbol "%", if so, fill the formatting character through the variable parameter list valist received by vsnprintf and secure_system, and convert these parameters into a SECURE_EXEC_PROCESS_T structure.
[0095] S24. After converting all commands in the command group (the group of commands currently to be run) into a SECURE_EXEC_PROCESS_T structure according to the method in step S23, a SECURE_EXEC_PROCESS_T structure array is obtained, and the structure array is passed as a parameter to the secure_exec function to run the command. The secure_exec function returns the pid of the last command process in this group of commands, waits for the process to end through waitpid, and then obtains the exit status of the process through WEXITSTATUS. In this way, the current command group is executed.
[0096] S25. Compare the process exit status after executing the current set of commands in step S24 with the prerequisites for the execution of the next set of commands. If they match (i.e., the prerequisites for the execution of the next set of commands are met), run the next set of commands according to the method of steps S22-S24. If they do not match, skip the next set of commands, obtain the execution prerequisites of the next set of commands (the next next set of commands) and compare them, and repeat this process until all commands are executed.
[0097] The process of converting command to structure array is as follows:
[0098] 1)secure_system("echo-n%s:%d|md5sum>%s","root",1,
[0099] " / tmp / md5");
[0100] 2)[{"command":"echo",params:["-n",
[0101] "%s:%d"]},{"command":"root","stdout_direction":SECURE_EXEC_STREAM_DI RECTION_NEW_FILE,"stdout_target":{"file":"%s"}}]
[0102] 3)[{"command":"echo",params:["-n",
[0103] "root:1"]},{"command":"root","stdout_direction":SECURE_EXEC_STREAM_DIRECTION_NEW_FILE,"stdout_target":{"file":" / tmp / md5"}}]
[0104] Taking the command "curl http: / / localhost-o-2> / dev / null|md5sum> / tmp / md5" as an example, the format of the converted SECURE_EXEC_PROCESS_T structure array is shown in Table 1.
[0105] Table 1 represents the command as an array of SECURE_EXEC_PROCESS_T structures
[0106]
[0107] The function of secure_popen is similar to that of popen. Both secure_popen and secure_system need to assemble commands in the form of formatted strings inside the function. This gives secure_popen the opportunity to determine which are the commands themselves and which are the passed-in parameters, thereby preventing the passed-in parameters from becoming a new command to be executed.
[0108] The following is a typical example of secure_popen:
[0109] static int test_secure_popen()
[0110] {
[0111] char*data="root";
[0112] SECURE_POPEN_FP_T*sfp=secure_popen("echo%s|openssl base64-e",data);
[0113] char buf
[1024] = {0};
[0114] assert(sfp!=NULL);
[0115] int len=fread(buf,1,sizeof(buf),sfp->fp);
[0116] secure_pclose(sfp);
[0117] assert(len>0);
[0118] assert(strcmp("cm9vdAo=\n",buf)==0);
[0119] return 0;
[0120] }
[0121] like Figure 3 As shown, the secure_popen operation method is:
[0122] S31, secure_popen first creates a pipe connecting the child process through the pipe function, and then forks a child process;
[0123] S32, the main process converts the read-end file descriptor of the pipe into a file pointer through fdopen, and constructs the file pointer and the pid of the child process into a SECURE_POPEN_FP_T structure and returns it to the function caller; the child process redirects the standard output of the child process itself to the write end of the pipe, so as to pass the content output of subsequent command execution to the parent process;
[0124] S33, decomposing the command command into multiple command groups according to the logical operators in the command, each command group including a group of commands that need to be currently executed and a prerequisite for the execution of the next group of commands;
[0125] S34, traversing the command group (a group of commands currently to be run) in the pipeline execution order, and splitting them respectively with the pipeline descriptor "|" to obtain at least one command, and then traversing each part of the command by space to split the executable program name, parameter list and redirection operator in the command; respectively detecting whether the executable program name, parameter list and redirection operator contain the formatting symbol "%", if so, filling the formatting character through the variable parameter list valist received by vsnprintf and secure_system, and converting these parameters into a SECURE_EXEC_PROCESS_T structure;
[0126] S35. After converting all commands in the command group (the group of commands currently to be run) into a SECURE_EXEC_PROCESS_T structure according to the method in step S34, a SECURE_EXEC_PROCESS_T structure array is obtained, and the structure array is passed as a parameter to the secure_exec function to run the command. The secure_exec function returns the pid of the last command process in this group of commands, waits for the process to end through waitpid, and then obtains the exit status of the process through WEXITSTATUS;
[0127] S36. Compare the process exit status after executing the current set of commands in step S35 with the prerequisites for the execution of the next set of commands. If they match (i.e., the prerequisites for the execution of the next set of commands are met), run the next set of commands according to the method of steps S33-S35. If they do not match, skip the next set of commands, obtain the execution prerequisites of the next set of commands and compare them, and repeat this process until all commands are executed.
[0128] It can be seen that secure_popen is the same as secure_system in the conversion between command and SECURE_EXEC_PROCESS_T structure array and command execution method.
[0129] The secure_pclose function is similar to the pclose function and is used to release the resources created by secure_popen. Figure 4 As shown, the secure_pclose operation method is: the secure_pclose function first obtains the pid of the associated child process from efp, waits for the child process to exit through waitpid, then obtains the process exit status through WEXITSTATUS, closes the file pointer associated in efp, exits the function and returns the process exit status code.
[0130] The above detailed description is a specific description of a feasible embodiment of the present invention. The embodiment is not intended to limit the patent scope of the present invention. Any equivalent implementation or modification that does not deviate from the present invention should be included in the patent scope of this case.
Claims
1. A method for preventing shell injection in Linux C, characterized in that: The steps include: S1. Build a package library that implements the exec function family to run commands. The interfaces provided by the package library include: secure_exec: This function runs a group of commands by defining a structure array SECURE_EXEC_PROCESS_T. This group of commands forms a connected pipeline. Each command in the command group forks a process and runs it in the child process through the execvp function. secure_exec uses the standard output of the previous process as the standard input of the next process according to the order of the structure array, or connects the specified file descriptor through the pipe+dup2 function to achieve functions similar to shell pipes and redirection; the secure_exec function will eventually return the pid of the last process in the pipeline and the optional outgoing file descriptor bound to the standard input of the first process in the pipeline; secure_system: This function supports pipes, redirection, logical operators, and background execution. It also supports passing parameter lists in the form of formatted strings + variable parameters. This function converts the command and parameter list represented by strings into an array of SECURE_EXEC_PROCESS_T structures, then calls the secure_exec function to run the command, and waits for the last process in the pipeline to complete execution and then returns its process exit status. secure_popen: This function supports pipes, redirection, logical operators, and supports passing parameter lists in the form of formatted strings + variable parameters. This function converts the command and parameter list represented by a string into an array of SECURE_EXEC_PROCESS_T structures, then calls the secure_exec function to run the command and returns a structure containing a readable file pointer and the pid of the child process that executes the command. The file pointer is used to read the data output by the command. secure_pclose: This function is used to release the resources created by the secure_popen function, wait for the command executed by secure_popen to end and return the process exit status; S2. When a command is passed in, secure_system and secure_popen pass in the command in the form of a formatted string, and pass in the parameters required for the formatted characters through a variable parameter list. The command parameter describes which are the command itself and which are the parameters required by the command. The parameters in the parameter list are ultimately regarded as ordinary parameters by the command and will not retain any special meaning. The characters that need to retain special meanings are written in the command command parameter to ensure that the dynamically passed in parameters will not become a new command to be executed; secure_system and secure_popen parse the passed in command into SECURE_EXEC_PROCESS_T structure arrays, and then execute the command through the secure_exec function; after the command is executed, secure_system waits for the last process in the pipeline to complete execution and then returns its termination status, and secure_pclose waits for the process created by secure_popen to end and returns its termination status.
2. The method for preventing shell injection in Linux C according to claim 1, characterized in that: The structure array includes at least one structure, and each structure includes: a required command name, an optional command parameter list, a standard output, a redirection method of an error output, and an output direction.
3. A method for preventing shell injection in Linux C according to claim 2, characterized in that: The redirection mode of the error output includes redirecting to a file descriptor, a file or appending to a file, and the output direction includes outputting to a file or a file descriptor.
4. The method for preventing shell injection in Linux C according to claim 1, characterized in that: The secure_exec operation method is: S11. The secure_exec function traverses the passed SECURE_EXEC_PROCESS_T structure array internally, creates two pipes for each array member, one for connecting the standard output pipe and the other for connecting the standard input pipe of the child process, and the other for forking a child process. S12. If the child process is the first process in the SECURE_EXEC_PROCESS_T structure array, and the wfd parameter exists when secure_exec is called, the read end of the standard input pipe of the process is redirected to the read end of the standard input of the child process created by the parent process through dup2, and the write end of the pipe is transmitted through wfd in the parent process, and then step S15 is executed; the role of wfd is that if the first process in the SECURE_EXEC_PROCESS_T structure array needs to receive data from the standard input, it can be written through wfd; S13. If the child process is the first process in the SECURE_EXEC_PROCESS_T structure array, but the wfd parameter does not exist when secure_exec is called, then directly execute step S15; S14, if the child process is not the first process in the SECURE_EXEC_PROCESS_T structure array, redirect the read end of the standard output pipe connected to the previous child process to the standard input of the current child process through dup2, and then execute step S15; S15. Redirect the standard output and standard error of the process through dup2 according to the standard output and standard error redirection rules defined in SECURE_EXEC_PROCESS_T. If the current child process does not specify a redirection rule, the standard output of the child process is redirected to the write end of the standard output pipe created by the parent process for connecting to the child process by default, and the standard error output is not processed; S16. In each child process, the command and parameter list to be executed are passed to the execvp function, and the command is executed according to the command and params specified by SECURE_EXEC_PROCESS_T; S17. After traversing the SECURE_EXEC_PROCESS_T structure array, if the wfd parameter still exists, the write end of the standard input pipe of the first child process in the pipeline is passed out, and then the function is exited, and the pid of the last child process in the pipeline is returned; if the wfd parameter does not exist after traversing all SECURE_EXEC_PROCESS_T structure arrays, the function is exited directly, and the pid of the last child process in the pipeline is returned.
5. The method for preventing shell injection in Linux C according to claim 1, characterized in that: The operation method of the secure_system is: S21, first determine whether the command is executed in the background. If it is executed in the background, fork a child process first, and all subsequent processing is performed in the child process; if it is not executed in the background, the subsequent processing is completed directly in the current process; S22, decomposing the command command into multiple command groups according to the logical operators in the command, each command group including a group of commands that need to be run currently and a prerequisite for the next group of commands to be run; S23, traversing the command group in the pipeline execution order, and splitting them respectively with the pipeline descriptor "|", obtaining at least one command, and then traversing each part of the command by space, splitting them to obtain the executable program name, parameter list and redirection operator in the command; respectively detecting whether the executable program name, parameter list and redirection operator contain the formatting symbol "%", if so, filling the formatting characters through the variable parameter list valist received by vsnprintf and secure_system, and converting these parameters into a SECURE_EXEC_PROCESS_T structure; S24. After converting all commands in the command group into a SECURE_EXEC_PROCESS_T structure according to the method in step S23, a SECURE_EXEC_PROCESS_T structure array is obtained, and the structure array is passed as a parameter to the secure_exec function to run the command. The secure_exec function returns the pid of the last command process in this group of commands, waits for the process to end through waitpid, and then obtains the exit status of the process through WEXITSTATUS; S25. Compare the process exit status after executing step S24 with the prerequisites for executing the next set of commands. If they match, run the next set of commands according to the method of steps S22-S24. If they do not match, skip the next set of commands, obtain the execution prerequisites of the next set of commands and compare them, and repeat this process until all commands are executed.
6. The method for preventing shell injection in Linux C according to claim 1, characterized in that: The secure_popen operation method is: S31. First, create a pipe to connect the child process through the pipe function, and then fork a child process; S32, the main process converts the read-end file descriptor of the pipe into a file pointer through fdopen, and constructs the file pointer and the pid of the child process into a SECURE_POPEN_FP_T structure and returns it to the function caller; the child process redirects the standard output of the child process itself to the write end of the pipe, so as to pass the content output of subsequent command execution to the parent process; S33, decomposing the command command into multiple command groups according to the logical operators in the command, each command group including a group of commands that need to be currently executed and a prerequisite for the execution of the next group of commands; S34, traversing the command group in the pipeline execution order, and splitting them respectively with the pipeline descriptor "|", obtaining at least one command, and then traversing each part of the command by space, splitting them to obtain the executable program name, parameter list and redirection operator in the command; respectively detecting whether the executable program name, parameter list and redirection operator contain the formatting symbol "%", if so, filling the formatting characters through the variable parameter list valist received by vsnprintf and secure_system, and converting these parameters into a SECURE_EXEC_PROCESS_T structure; S35. After converting all commands in the command group into a SECURE_EXEC_PROCESS_T structure according to the method in step S34, a SECURE_EXEC_PROCESS_T structure array is obtained, and the structure array is passed as a parameter to the secure_exec function to run the command. The secure_exec function returns the pid of the last command process in this group of commands, waits for the process to end through waitpid, and then obtains the exit status of the process through WEXITSTATUS; S36. Compare the process exit status after executing step S35 with the prerequisites for executing the next set of commands. If they match, run the next set of commands according to the method of steps S33-S35. If they do not match, skip the next set of commands, obtain the execution prerequisites of the next set of commands and compare them, and repeat this process until all commands are executed.
7. The method for preventing shell injection in Linux C according to claim 1, characterized in that: The secure_pclose operation method is as follows: the secure_pclose function first obtains the pid of the associated child process from efp, waits for the child process to exit through waitpid, then obtains the process exit status through WEXITSTATUS, closes the file pointer associated in efp, exits the function and returns the process exit status code.