CVE-2009-2288: Nagios CGI Arbitrary Command Execution

This vulnerability affects Nagios prior to 3.1.1. Specifically, statuswml CGI script does not perform sufficient checks in the user controlled input. Here is the vulnerable code as seen in 3.1.0 release of Nagios.

/* displays ping results */
void display_ping(void){
	char input_buffer[MAX_INPUT_BUFFER];
	char buffer[MAX_INPUT_BUFFER];
	char *temp_ptr;
	FILE *fp;
	int odd=0;
	int in_macro=FALSE;
       ...
			for(temp_ptr=my_strtok(input_buffer,"$");temp_ptr!=NULL;temp_ptr=my_strtok(NULL,"$")){
       ...
					if(strlen(buffer)+strlen(temp_ptr) < sizeof(buffer)-1){

						if(!strcmp(temp_ptr,"HOSTADDRESS"))
							strncat(buffer,ping_address,sizeof(buffer)-strlen(buffer)-1);
					        }
       ...
			/* run the ping command */
			fp=popen(buffer,"r");
       ...
	return;
        }

This code can be found at cgi/statuswml.c file. As you can see, there is no check on the host address passed to the PING option of that CGI script. Because of this missing check, a user is able to use command seperation characters such as ‘;’ (hex value 0×3B) to execute arbitrary commands. In the original report there was an example which was:

https://somehost.com/nagios/cgi-bin/statuswml.cgi?ping=173.45.235.65%3Becho+%24PATH

This will return the contents of PATH environment variable since 0×3B is equal to ‘;’ character and 0×24 is the ‘$’ sign. The same vulnerability was also present in display_traceroute() function of the same CGI. To fix this, the Nagios developers wrote two new functions which you can see here:

int validate_arguments(void){
       int result=OK;
       if((strcmp(ping_address,"")) && !is_valid_hostip(ping_address)) {
               printf("<p>Invalid host name/ip</p>\n");
               result=ERROR;
               }
       if(strcmp(traceroute_address,"") && !is_valid_hostip(traceroute_address) ){
               printf("<p>Invalid host name/ip</p>\n");
               result=ERROR;
               }
       return result;
       }

Which uses is_valid_hostip() function to validate the given IP address. is_valid_hostip() is the second function that was added and it simply does this:

int is_valid_hostip(char *hostip) {
       char *valid_domain_chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVWXYZ0123456789.-";
       if(strcmp(hostip,"") && strlen(hostip)==strspn(hostip,valid_domain_chars ) && hostip[0] != '-' && hostip[strlen(hostip)-1] != '-')
               return TRUE;
       return FALSE;
       }

It checks that there are no characters apart from those declared in valid_domain_chars array. At last, the main() function was updated to include the check like this.

        document_header();

+       /* validate arguments in URL */
+       result=validate_arguments();
+       if(result==ERROR){
+               document_footer();
+               return ERROR;
+               }
+
        /* read the CGI configuration file */

~ by xorl on July 3, 2009.

Leave a Reply