IT story

이 코드는 왜 버퍼 오버 플로우 공격에 취약합니까?

hot-time 2020. 6. 13. 09:47
반응형

이 코드는 왜 버퍼 오버 플로우 공격에 취약합니까?


int func(char* str)
{
   char buffer[100];
   unsigned short len = strlen(str);

   if(len >= 100)
   {
        return (-1);
   }

   strncpy(buffer,str,strlen(str));
   return 0;
}

이 코드는 버퍼 오버플로 공격에 취약하며 이유를 알아 내려고 노력 중입니다. 나는 그것이 대신 len선언 된 것과 관련이 있다고 생각 하지만 실제로는 확실하지 않습니다.shortint

어떤 아이디어?


대부분의 컴파일러에서 최대 값 unsigned short은 65535입니다.

이 값을 초과하면 65536이 0이되고 65600이 65가됩니다.

이는 올바른 길이의 긴 문자열 (예 : 65600)이 검사를 통과하고 버퍼가 오버 플로우됨을 의미합니다.


사용 size_t의 결과를 저장하는 strlen()하지를, unsigned short및 비교 len바로의 크기를 인코딩한다는 표현 buffer. 예를 들어 :

char buffer[100];
size_t len = strlen(str);
if (len >= sizeof(buffer) / sizeof(buffer[0]))  return -1;
memcpy(buffer, str, len + 1);

문제는 여기 있습니다 :

strncpy(buffer,str,strlen(str));
                   ^^^^^^^^^^^

문자열이 대상 버퍼의 길이보다 큰 경우 strncpy는 여전히이를 복사합니다. 버퍼의 크기 대신 문자열의 문자 수를 복사 할 숫자로 지정합니다. 이를 수행하는 올바른 방법은 다음과 같습니다.

strncpy(buffer,str, sizeof(buff) - 1);
buffer[sizeof(buff) - 1] = '\0';

이것이하는 일은 버퍼의 실제 크기에서 널 종료 문자에 대한 1을 뺀 것으로 복사되는 데이터의 양을 제한하는 것입니다. 그런 다음 추가 된 보호 수단으로 버퍼의 마지막 바이트를 널 문자로 설정합니다. 그 이유는 strlenpy가 strlen (str) <len-1 인 경우 종료 널을 포함하여 최대 n 바이트까지 복사하기 때문입니다. 그렇지 않으면 널이 복사되지 않고 버퍼가 종료되지 않으므로 충돌 시나리오가 발생합니다. 끈.

도움이 되었기를 바랍니다.

편집 : 다른 사람의 추가 조사 및 입력시 기능에 대한 가능한 코딩은 다음과 같습니다.

int func (char *str)
  {
    char buffer[100];
    unsigned short size = sizeof(buffer);
    unsigned short len = strlen(str);

    if (len > size - 1) return(-1);
    memcpy(buffer, str, len + 1);
    buffer[size - 1] = '\0';
    return(0);
  }

문자열의 길이를 이미 알고 있으므로 memcpy를 사용하여 str이 참조하는 위치에서 버퍼로 문자열을 복사 할 수 있습니다. strlen (3)의 매뉴얼 페이지 (FreeBSD 9.3 시스템)에 따라 다음이 언급됩니다.

 The strlen() function returns the number of characters that precede the
 terminating NUL character.  The strnlen() function returns either the
 same result as strlen() or maxlen, whichever is smaller.

문자열의 길이에 null이 포함되어 있지 않다고 해석합니다. 그렇기 때문에 len + 1 바이트를 복사하여 null을 포함하고 테스트는 길이 <buffer-2의 크기인지 확인합니다. 버퍼는 위치 0에서 시작하기 때문에 1을 빼고 다른 하나는 빼고 공간이 있는지 확인합니다. 널을 위해.

편집 : 것으로 나타났습니다. 액세스가 0으로 시작하는 동안 무언가의 크기가 1로 시작하므로 98 바이트 이상이면 오류가 반환되지만 99 바이트 이상이어야합니다.

EDIT: Although the answer about a unsigned short is generally correct as the maximum length that can be represented is 65,535 characters, it doesn't really matter because if the string is longer than that, the value will wrap around. It's like taking 75,231 (which is 0x000125DF) and masking off the top 16 bits giving you 9695 (0x000025DF). The only problem that I see with this is the first 100 chars past 65,535 as the length check will allow the copy, but it will only copy up to the first 100 characters of the string in all cases and null terminate the string. So even with the wraparound issue, the buffer still will not be overflowed.

This may or may not in itself pose a security risk depending on the content of the string and what you are using it for. If it's just straight text that is human readable, then there is generally no problem. You just get a truncated string. However, if it's something like a URL or even a SQL command sequence, you could have a problem.


Even though you're using strncpy, the length of the cutoff is still dependent on the passed string pointer. You have no idea how long that string is (the location of the null terminator relative to the pointer, that is). So calling strlen alone opens you up to vulnerability. If you want to be more secure, use strnlen(str, 100).

Full code corrected would be:

int func(char *str) {
   char buffer[100];
   unsigned short len = strnlen(str, 100); // sizeof buffer

   if (len >= 100) {
     return -1;
   }

   strcpy(buffer, str); // this is safe since null terminator is less than 100th index
   return 0;
}

The answer with the wrapping is right. But there is a problem I think was not mentioned if(len >= 100)

Well if Len would be 100 we'd copy 100 elements an we'd not have trailing \0. That clearly would mean any other function depending on proper ended string would walk way beyond the original array.

The string problematic from C is IMHO unsolvable. You'd alway better have some limits before the call, but even that won't help. There is no bounds checking and so buffer overflows always can and unfortunately will happen....


Beyond the security issues involved with calling strlen more than once, one should generally not use string methods on strings whose length is precisely known [for most string functions, there's only a really narrow case where they should be used--on strings for which a maximum length can be guaranteed, but the precise length isn't known]. Once the length of the input string is known and the length of the output buffer is known, one should figure out how big a region should be copied and then use memcpy() to actually perform the copy in question. Although it's possible that strcpy might outperform memcpy() when copying a string of only 1-3 bytes or so, on many platforms memcpy() is likely to be more than twice as fast when dealing with larger strings.

Although there are some situations where security would come at the cost of performance, this is a situation where the secure approach is also the faster one. In some cases, it may be reasonable to write code which is not secure against weirdly-behaving inputs, if code supplying the inputs can ensure they will be well-behaved, and if guarding against ill-behaved inputs would impede performance. Ensuring that string lengths are only checked once improves both performance and security, though one extra thing can be done to help guard security even when tracking string length manually: for every string which is expected to have a trailing null, write the trailing null explicitly rather than expecting the source string to have it. Thus, if one were writing an strdup equivalent:

char *strdupe(char const *src)
{
  size_t len = strlen(src);
  char *dest = malloc(len+1);
  // Calculation can't wrap if string is in valid-size memory block
  if (!dest) return (OUT_OF_MEMORY(),(char*)0); 
  // OUT_OF_MEMORY is expected to halt; the return guards if it doesn't
  memcpy(dest, src, len);      
  dest[len]=0;
  return dest;
}

Note that the last statement could generally be omitted if the memcpy had processed len+1 bytes, but it another thread were to modify the source string the result could be a non-NUL-terminated destination string.

참고URL : https://stackoverflow.com/questions/29910520/why-is-this-code-vulnerable-to-buffer-overflow-attacks

반응형