Skip to main content
added 19 characters in body
Source Link
  envstrings[0] = NULL;   // assume no environment strings
  if(!strncmp("environ", p, strlen("environ"))){
      field = memchr(p, ' ', strlen(p));  // find trailing space
      if(field == NULL){  // error if no trailing space
          reply(id, "missing environment value", sock, addr);
          return;
      }
      field++;   // increment to first character of key
      i = 0;     // init our index counter into envstrings
      while(1){  // loop as long as we need to
          envstrings[i] = field;   // save the next envstring ptr
          p = memchr(field, ' ', strlen(field)); // trailing space
          if(p==NULL){  // if no space then we need a newline
              p = memchr(field, '\n', strlen(field)); 
              if (p==NULL){
                  reply(id, "malformed environment value", sock, addr);
                  return;
              }
              *p = '\0';    // found newline terminate last envstring
              i++;              // count the envstring
              break;            // newline marks the end so break
          }
          *p = '\0';      // terminate the envstring
          field = p + 1;  // point to start of next envstring 
          i++; // count the envstring
  
      }
      envstrings[i] = NULL;   // terminate the list   
   }
  envstrings[i] = NULL;   // terminate 
 the list
}

Above is the the exact extract from the book. I was trying to minimize it and I didn't have much time to post so i had minor spell check errors but the logic isyeah the samecode was incorrect. I just repost my early question so to make this post validIt's corrected now. I had:

  envstrings[0] = NULL;   // assume no environment strings
  if(!strncmp("environ", p, strlen("environ"))){
      field = memchr(p, ' ', strlen(p));  // find trailing space
      if(field == NULL){  // error if no trailing space
          reply(id, "missing environment value", sock, addr);
          return;
      }
      field++;   // increment to first character of key
      i = 0;     // init our index counter into envstrings
      while(1){  // loop as long as we need to
          envstrings[i] = field;   // save the next envstring ptr
          p = memchr(field, ' ', strlen(field)); // trailing space
          if(p==NULL){  // if no space then we need a newline
              p = memchr(field, '\n', strlen(field)); 
              if (p==NULL){
                  reply(id, "malformed environment value", sock, addr);
                  return;
              }
              *p = '\0';    // found newline terminate last envstring
              i++;              // count the envstring
              break;            // newline marks the end so break
          }
          *p = '\0';      // terminate the envstring
          field = p + 1;  // point to start of next envstring 
          i++; // count the envstring
  
      }
                 
   }
  envstrings[i] = NULL;   // terminate the list
}

Above is the the exact extract from the book. I was trying to minimize it and I didn't have much time to post so i had minor spell check errors but the logic is the same. I just repost my early question so to make this post valid. I had:

  envstrings[0] = NULL;   // assume no environment strings
  if(!strncmp("environ", p, strlen("environ"))){
      field = memchr(p, ' ', strlen(p));  // find trailing space
      if(field == NULL){  // error if no trailing space
          reply(id, "missing environment value", sock, addr);
          return;
      }
      field++;   // increment to first character of key
      i = 0;     // init our index counter into envstrings
      while(1){  // loop as long as we need to
          envstrings[i] = field;   // save the next envstring ptr
          p = memchr(field, ' ', strlen(field)); // trailing space
          if(p==NULL){  // if no space then we need a newline
              p = memchr(field, '\n', strlen(field)); 
              if (p==NULL){
                  reply(id, "malformed environment value", sock, addr);
                  return;
              }
              *p = '\0';    // found newline terminate last envstring
              i++;              // count the envstring
              break;            // newline marks the end so break
          }
          *p = '\0';      // terminate the envstring
          field = p + 1;  // point to start of next envstring 
          i++; // count the envstring
  
      }
      envstrings[i] = NULL;   // terminate the list            
  }

Above is the the exact extract from the book. I was trying to minimize it and I didn't have much time to post so yeah the code was incorrect. It's corrected now.

added 19 characters in body
Source Link
  envstrings[0] = NULL;   // assume no environment strings
  if(!strncmp("environ", p, strlen("environ"))){
      field = memchr(p, ' ', strlen(p));  // find trailing space
      if(field == NULL){  // error if no trailing space
          reply(id, "missing environment value", sock, addr);
          return;
      }
      field++;   // increment to first character of key
      i = 0;     // init our index counter into envstrings
      while(1){  // loop as long as we need to
          envstrings[i] = field;   // save the next envstring ptr
          p = memchr(field, ' ', strlen(field)); // trailing space
          if(p==NULL){  // if no space then we need a newline
              p = memchr(field, '\n', strlen(field)); 
              if (p==NULL){
                  reply(id, "malformed environment value", sock, addr);
                  return;
              }
          }
          *p = '\0';    // found newline terminate last envstring
              i++;              // count the envstring
              break;            // newline marks the end so break
          }
          *p = '\0';      // terminate the envstring
          field = p + 1;  // point to start of next envstring 
          i++; // count the envstring
   
      //}
 count the envstring              
  }
  envstrings[i] = NULL;   // terminate the list
}
  envstrings[0] = NULL;   // assume no environment strings
  if(!strncmp("environ", p, strlen("environ"))){
      field = memchr(p, ' ', strlen(p));  // find trailing space
      if(field == NULL){  // error if no trailing space
          reply(id, "missing environment value", sock, addr);
          return;
      }
      field++;   // increment to first character of key
      i = 0;     // init our index counter into envstrings
      while(1){  // loop as long as we need to
          envstrings[i] = field;   // save the next envstring ptr
          p = memchr(field, ' ', strlen(field)); // trailing space
          if(p==NULL){  // if no space then we need a newline
              p = memchr(field, '\n', strlen(field)); 
              if (p==NULL){
                  reply(id, "malformed environment value", sock, addr);
                  return;
              }
          }
          *p = '\0';    // found newline terminate last envstring
          i++;              // count the envstring
          break;            // newline marks the end so break
      }
      *p = '\0';      // terminate the envstring
      field = p + 1;  // point to start of next envstring 
      i++;            // count the envstring
  }
  envstrings[i] = NULL;   // terminate the list
}
  envstrings[0] = NULL;   // assume no environment strings
  if(!strncmp("environ", p, strlen("environ"))){
      field = memchr(p, ' ', strlen(p));  // find trailing space
      if(field == NULL){  // error if no trailing space
          reply(id, "missing environment value", sock, addr);
          return;
      }
      field++;   // increment to first character of key
      i = 0;     // init our index counter into envstrings
      while(1){  // loop as long as we need to
          envstrings[i] = field;   // save the next envstring ptr
          p = memchr(field, ' ', strlen(field)); // trailing space
          if(p==NULL){  // if no space then we need a newline
              p = memchr(field, '\n', strlen(field)); 
              if (p==NULL){
                  reply(id, "malformed environment value", sock, addr);
                  return;
              }
              *p = '\0';    // found newline terminate last envstring
              i++;              // count the envstring
              break;            // newline marks the end so break
          }
          *p = '\0';      // terminate the envstring
          field = p + 1;  // point to start of next envstring 
          i++; // count the envstring
   
      }
                 
  }
  envstrings[i] = NULL;   // terminate the list
}
added 4829 characters in body
Source Link

I'm reading "Gray Hat Hacking The Ethical Hacker's HandbookExample Using find.c Using find.c as an example, Fourth Edition"how would this manual source code auditing process work? We need to start with user data entering the program. But I have a question concerning one ofAs seen in the exercisepreceding ITS4 output, a recvfrom() function call accepts an incoming UDP packet. They followingThe code

find.c surrounding the call looks like this:

char buf[65636];buf[65536];   // buffer to receive incoming udp packet
int sock, pid;     // socket descriptor and process id
sockaddr_in fsin;  // internet socket address information

//...
// Code to take care of the socket setup
//...

while(1){ // loop forever
    unsinged int alen = sizeof(fsin);
    // now read the next incoming UPD packet
    if(recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&fsub&fsin, &alen) < 0){
        // exit the program if an error occurred
        errexit("recvfrom: %s\n", strerror(errno));
    }

    pid = fork();       // fork a child to process the packet
    if(pid == 0){       // Then this must be the child
         manage_request(buf, sock, &fsin);  // child handles packet
         exit(0);           // child exits after packet is processed
    }
}
 

The preceding code shows a parent process looping to receive incoming UDP packets using the recvfrom() function. Following a successful recvfrom(), a child process is forked and the manage_request() function is called to process the received packet. We need to trace into manage_request() to see what happens with the user’s input. We can see right off the bat that none of the parameters passed in to manage_request() deals with the size of buf, which should make the hair on the back of our necks stand up. The manage_request() function starts out with a number of data declarations, as shown here:

void manage_request(char *buf, int sock, struckstruct sockaddr_in* addr){

  char init_cwd[1024];
  char cmd[512];
  char outf[512];
  char replybuf[65536];
  char *user;
  char *password;
  char *filename;
  char *keyword;
  char *envstrings[16];
  char *id;
  char *field;
  char *p;
  int i;

Here, we see the declaration of many of the fixed-size buffers noted earlier by RATS. We know that the input parameter buf points to the incoming UDP packet, and the buffer may contain up to 65,535 bytes of data (the maximum size of a UDP packet). There are two interesting things to note here: First, the length of the packet is not passed into the function, so bounds checking will be difficult and perhaps completely dependent on well-formed packet content. Second, several of the local buffers are significantly smaller than 65,535 bytes, so the function had better be very careful how it copies information into those buffers. Earlier, it was mentioned that the buffer at line 172 is vulnerable to an overflow. That seems a little difficult given that there is a 64KB buffer sitting between it and the return address.

The function proceeds to set some of the pointers by parsing the incoming packet, which is expected to be formatted as follows:

id some_id_value\n
user some_user_name\n
password some_users_password\n
filename some_filename\n
keyword some_keyword\n
environ key=value key=value key=value ...\n

The pointers in the stack are set by locating the key name, searching for the following space, and incrementing by one character position. The values become null terminated when the trailing \n is located and replaced with \0. If the key names are not found in the order listed, or trailing \n characters fail to be found, the input is considered malformed and the function returns. Parsing the packet goes well until processing of the optional environ values begins. The environ field is processed by the following code (note, the pointer p at this point is positioned at the next character that needs parsing within the input buffer):

  envstrings[0] = NULL;   // assume no environment strings
  if(!strncmp("environ", p, strlen("environ"))){
      field = memchr(p, ' ', strlen(p));  // find trailing space
      if(field == NULL){  // error if no trailing space
          reply(id, "missing environment value", sock, addr);
          return;
      }
      field++;   // increment to first character of key
      i = 0;     // init our index counter into envstrings
      while(1){  // loop as long as we need to
          envstrings[i] = field;   // save the next envstring ptr
          p = memchr(field, ' ', strlen(field)); // trailing space
          if(p==NULL){  // if no space then we need a newline
              p = memchr(field, '\n', strlen(field)); 
              if (p==NULL){
                  reply(id, "malformed environment value", sock, addr);
                  return;
              }
          }
          *p = '\0';    // found newline terminate last envstring
          i++;              // count the envstring
          break;            // newline marks the end so break
      }
      *p = '\0';      // terminate the envstring
      field = p + 1;  // point to start of next envstring 
      i++;            // count the envstring
  }
  envstrings[i] = NULL;   // terminate the list
}

Following the processing of the environ field, each pointer in the envstrings array is passed to the putenv() function, so these strings are expected to be in the form key=value. In analyzing this code, note that the entire environ field is optional, but skipping it wouldn’t be any fun for us. The book mentionproblem in the code results from the fact that we can exploit this program by usingthe while loop that processes each new environment string fails to do any bounds checking on the counter i, but the declaration of envstrings only allocates space for 16 pointers. If more than 16 environment strings are provided, the variables below the envstrings array on the stack will start to get overwritten. We have the makings of a buffer overflow exploitation onat this point, but the pointerquestion becomes: “Can we reach the saved return address?” Performing some quick math tells us that there are about 67,600 bytes of stack space between the envstrings array and the saved frame pointer/saved return address. Because each member of the envstrings becausearray occupies 4 bytes, if we add 67,600/4 = 16,900 additional environment strings to our input packet, the programpointers to those strings will overwrite all of the stack space up to the saved frame pointer. Two additional environment strings will give us an overwrite of the frame pointer and the return address. How can we include 16,918 environment strings if the form key=value is in our packet? If a minimal environment string, say x=y, consumes 4 bytes counting the trailing space, then it would seem that our input packet needs to accommodate 67,672 bytes of environment strings alone. Because this is larger than the maximum UDP packet size, we seem to be out of luck. Fortunately for us, the preceding loop does no boundary validationparsing of each environment string, so we can do the following:

environ +there is no reason for a malicious user to use properly formatted (# of spaces before shellcodekey=value) +strings. It is left to you to verify that placing approximately 16,919 space characters between the keyword environ and the trailing carriage return should result in an overwrite of the saved return address. Since an input line of that size easily fits in a UDP packet, all we need to do now is consider where to place our shellcode.

  The book statesanswer is to make it the last environment string, and the nice thing about this vulnerability is that we don'tdon’t even need to knowdetermine what addressvalue to overwrite the saved return address with because, as the preceding code will dohandles it for us. Finding out why this is soUnderstanding that point is also left to you as an exercise for.

Above is the readerthe exact extract from the book. I was wondering If I'm correcttrying to minimize it and I didn't have much time to post so hear me outi had minor spell check errors but the logic is the same. I just repost my early question so to make this post valid. I had:

Thehe code handles this because when the function memchr returns a pointer, that pointer is pushed on the stack and the address pushed on the stack will be the starting point of our shellcode due the space before our shellcode?

I'm reading "Gray Hat Hacking The Ethical Hacker's Handbook, Fourth Edition". But I have a question concerning one of the exercise. They following code

find.c

char buf[65636];   // buffer to receive incoming udp packet
int sock, pid;     // socket descriptor and process id
sockaddr_in fsin;  // internet socket address information

//...
// Code to take care of the socket setup
//...

while(1){ // loop forever
    unsinged int alen = sizeof(fsin);
    // now read the next incoming UPD packet
    if(recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&fsub, &alen) < 0){
        // exit the program if an error occurred
        errexit("recvfrom: %s\n", strerror(errno));
    }

    pid = fork();       // fork a child to process the packet
    if(pid == 0){     // Then this must be the child
         manage_request(buf, sock, &fsin);  // child handles packet
         exit(0);           // child exits after packet is processed
    }
}
 
void manage_request(char *buf, int sock, struck sockaddr_in* addr){

  char init_cwd[1024];
  char cmd[512];
  char outf[512];
  char replybuf[65536];
  char *user;
  char *password;
  char *filename;
  char *keyword;
  char *envstrings[16];
  char *id;
  char *field;
  char *p;
  int i;

  envstrings[0] = NULL;   // assume no environment strings
  if(!strncmp("environ", p, strlen("environ"))){
      field = memchr(p, ' ', strlen(p));  // find trailing space
      if(field == NULL){  // error if no trailing space
          reply(id, "missing environment value", sock, addr);
          return;
      }
      field++;   // increment to first character of key
      i = 0;     // init our index counter into envstrings
      while(1){  // loop as long as we need to
          envstrings[i] = field;   // save the next envstring ptr
          p = memchr(field, ' ', strlen(field)); // trailing space
          if(p==NULL){  // if no space then we need a newline
              p = memchr(field, '\n', strlen(field)); 
              if (p==NULL){
                  reply(id, "malformed environment value", sock, addr);
                  return;
              }
          }
          *p = '\0';    // found newline terminate last envstring
          i++;              // count the envstring
          break;            // newline marks the end so break
      }
      *p = '\0';      // terminate the envstring
      field = p + 1;  // point to start of next envstring 
      i++;            // count the envstring
  }
  envstrings[i] = NULL;   // terminate the list
}

The book mention that we can exploit this program by using a buffer overflow exploitation on the pointer array envstrings because the program does no boundary validation so we can do the following:

environ + (# of spaces before shellcode) + shellcode.

  The book states that we don't need to know what address to overwrite the return address with because the code will do it for us. Finding out why this is so is an exercise for the reader. I was wondering If I'm correct so hear me out.

The code handles this because when the function memchr returns a pointer, that pointer is pushed on the stack and the address pushed on the stack will be the starting point of our shellcode due the space before our shellcode?

Example Using find.c Using find.c as an example, how would this manual source code auditing process work? We need to start with user data entering the program. As seen in the preceding ITS4 output, a recvfrom() function call accepts an incoming UDP packet. The code surrounding the call looks like this:

char buf[65536];   // buffer to receive incoming udp packet
int sock, pid;     // socket descriptor and process id
sockaddr_in fsin;  // internet socket address information

//...
// Code to take care of the socket setup
//...

while(1){ // loop forever
    unsinged int alen = sizeof(fsin);
    // now read the next incoming UPD packet
    if(recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&fsin, &alen) < 0){
        // exit the program if an error occurred
        errexit("recvfrom: %s\n", strerror(errno));
    }

    pid = fork();       // fork a child to process the packet
    if(pid == 0){       // Then this must be the child
         manage_request(buf, sock, &fsin);  // child handles packet
         exit(0);           // child exits after packet is processed
    }
}

The preceding code shows a parent process looping to receive incoming UDP packets using the recvfrom() function. Following a successful recvfrom(), a child process is forked and the manage_request() function is called to process the received packet. We need to trace into manage_request() to see what happens with the user’s input. We can see right off the bat that none of the parameters passed in to manage_request() deals with the size of buf, which should make the hair on the back of our necks stand up. The manage_request() function starts out with a number of data declarations, as shown here:

void manage_request(char *buf, int sock, struct sockaddr_in* addr){

  char init_cwd[1024];
  char cmd[512];
  char outf[512];
  char replybuf[65536];
  char *user;
  char *password;
  char *filename;
  char *keyword;
  char *envstrings[16];
  char *id;
  char *field;
  char *p;
  int i;

Here, we see the declaration of many of the fixed-size buffers noted earlier by RATS. We know that the input parameter buf points to the incoming UDP packet, and the buffer may contain up to 65,535 bytes of data (the maximum size of a UDP packet). There are two interesting things to note here: First, the length of the packet is not passed into the function, so bounds checking will be difficult and perhaps completely dependent on well-formed packet content. Second, several of the local buffers are significantly smaller than 65,535 bytes, so the function had better be very careful how it copies information into those buffers. Earlier, it was mentioned that the buffer at line 172 is vulnerable to an overflow. That seems a little difficult given that there is a 64KB buffer sitting between it and the return address.

The function proceeds to set some of the pointers by parsing the incoming packet, which is expected to be formatted as follows:

id some_id_value\n
user some_user_name\n
password some_users_password\n
filename some_filename\n
keyword some_keyword\n
environ key=value key=value key=value ...\n

The pointers in the stack are set by locating the key name, searching for the following space, and incrementing by one character position. The values become null terminated when the trailing \n is located and replaced with \0. If the key names are not found in the order listed, or trailing \n characters fail to be found, the input is considered malformed and the function returns. Parsing the packet goes well until processing of the optional environ values begins. The environ field is processed by the following code (note, the pointer p at this point is positioned at the next character that needs parsing within the input buffer):

  envstrings[0] = NULL;   // assume no environment strings
  if(!strncmp("environ", p, strlen("environ"))){
      field = memchr(p, ' ', strlen(p));  // find trailing space
      if(field == NULL){  // error if no trailing space
          reply(id, "missing environment value", sock, addr);
          return;
      }
      field++;   // increment to first character of key
      i = 0;     // init our index counter into envstrings
      while(1){  // loop as long as we need to
          envstrings[i] = field;   // save the next envstring ptr
          p = memchr(field, ' ', strlen(field)); // trailing space
          if(p==NULL){  // if no space then we need a newline
              p = memchr(field, '\n', strlen(field)); 
              if (p==NULL){
                  reply(id, "malformed environment value", sock, addr);
                  return;
              }
          }
          *p = '\0';    // found newline terminate last envstring
          i++;              // count the envstring
          break;            // newline marks the end so break
      }
      *p = '\0';      // terminate the envstring
      field = p + 1;  // point to start of next envstring 
      i++;            // count the envstring
  }
  envstrings[i] = NULL;   // terminate the list
}

Following the processing of the environ field, each pointer in the envstrings array is passed to the putenv() function, so these strings are expected to be in the form key=value. In analyzing this code, note that the entire environ field is optional, but skipping it wouldn’t be any fun for us. The problem in the code results from the fact that the while loop that processes each new environment string fails to do any bounds checking on the counter i, but the declaration of envstrings only allocates space for 16 pointers. If more than 16 environment strings are provided, the variables below the envstrings array on the stack will start to get overwritten. We have the makings of a buffer overflow at this point, but the question becomes: “Can we reach the saved return address?” Performing some quick math tells us that there are about 67,600 bytes of stack space between the envstrings array and the saved frame pointer/saved return address. Because each member of the envstrings array occupies 4 bytes, if we add 67,600/4 = 16,900 additional environment strings to our input packet, the pointers to those strings will overwrite all of the stack space up to the saved frame pointer. Two additional environment strings will give us an overwrite of the frame pointer and the return address. How can we include 16,918 environment strings if the form key=value is in our packet? If a minimal environment string, say x=y, consumes 4 bytes counting the trailing space, then it would seem that our input packet needs to accommodate 67,672 bytes of environment strings alone. Because this is larger than the maximum UDP packet size, we seem to be out of luck. Fortunately for us, the preceding loop does no parsing of each environment string, so there is no reason for a malicious user to use properly formatted (key=value) strings. It is left to you to verify that placing approximately 16,919 space characters between the keyword environ and the trailing carriage return should result in an overwrite of the saved return address. Since an input line of that size easily fits in a UDP packet, all we need to do now is consider where to place our shellcode. The answer is to make it the last environment string, and the nice thing about this vulnerability is that we don’t even need to determine what value to overwrite the saved return address with, as the preceding code handles it for us. Understanding that point is also left to you as an exercise.

Above is the the exact extract from the book. I was trying to minimize it and I didn't have much time to post so i had minor spell check errors but the logic is the same. I just repost my early question so to make this post valid. I had:

he code handles this because when the function memchr returns a pointer, that pointer is pushed on the stack and the address pushed on the stack will be the starting point of our shellcode due the space before our shellcode?

Source Link
Loading