Fix the buffer overflow problem!

xiaoxiao2021-03-06  121

When David Leblanc and I determine the directory of "Writing Secure Code" book, we have clearly realized that the problem must focus on buffer overflow problems, because there are already too many developers who have prisoned too much when writing code. Such errors cause the emergence of buffer overflows that can be utilized. In this article, I will focus on why the buffer overflow and its repair methods.

Why caulk a buffer overflow?

There is a lot of conditional spills, including:

Use non-type secure language, such as C / C . Access or copy the buffer in an insecure manner. The compiler places the buffer next to the key data structure in memory or adjacent.

Now let's take a closer look at each condition.

First, the buffer overflows appear in C and C because these languages ​​do not perform array boundary checks and type security checks. C / C allows developers to create programs that are very close to hardware, allowing direct access to memory and computer registers. As a result, excellent performance is achieved; it is difficult to have any applications that can run like a well-written C / C application. The buffer overflow will also occur in other languages, but rarely see. If this error occurs, it is usually not caused by the developer, but an error in the runtime environment.

Second, if the application gets data from the user (or attacker) and copying the data to the buffer maintained by the application, it may cause the buffer overflow. In other words, the code assigns n bytes to the buffer, but copies more than N-bytes of data into the buffer. This is like a 14 ounce of water in the 12 oz. Where is the 4 ounce of water? Over overflow!

The last point is also the most important point, the compiler typically places the buffer next to "interested" data structure. For example, when a buffer of a function is adjacent to the stack, the return address of the function in the memory is close to the buffer. At this time, if an attacker can overflow the buffer, he can overwrite the return address of the function, so that when the function is returned, return to the address defined by the attacker. Other interested data structures include C V tables, exception handler addresses, function pointers, and more.

Let's take an example below.

What is wrong with the following code?

Void CopyData (Char * szdata) {

Char cdest [32];

STRCPY (CDest, SZDATA);

// Use CDest

...

}

Surprisingly, this code may not have any mistakes! This depends entirely on the way of calling CopyData (). For example, the following code is secure:

Char * sznames [] = {"michael", "cheryl", "blake"};

CopyData (szname [1]);

This code is secure because the name is hardcoded and knows that each string does not exceed 32 characters in length, so calling Strcpy is always safe. However, if the unique parameters of CopyData and SZDATA come from an unreliable source (such as socket or file), strcpy will copy the data until the empty character is touched; if the length of this data is greater than 32 characters, CDEST buffer The district will overflow, and any data other than the buffer outside the memory will be destroyed. Unfortunately, here, the broken data is from the return address from CopyData, which means that when CopyData is complete, it continues to execute the position specified by the attacker. This is really bad! Other data structures are also sensitive. Assume that a C class V table is destroyed, as follows:

Void CopyData (Char * szdata) {

Char cdest [32];

Cfoo foo;

STRCPY (CDest, SZDATA);

Foo.init ();

}

This example assumes that the CFOO class has a virtual method, and a V-table address list (like all C classes). If the V table is destroyed due to the CDest buffer, any virtual method of the class (in this case is init ()) may call the address specified by the attacker, not the address of the init (). By the way, if you think that your code does not call any C method, it is wrong, because one method always is called, that is, the false prevention function of the class! Of course, if a class does not call any method, it should be thinking about it exists.

Repair buffer overflow

Now, we continue to discuss some more practical content - how to delete and prevent buffer overflow in your code.

Migrate to managed code

In February and March 2002, we held Microsoft Windows® Security Push events. During this period, my working group was trained for more than 8,500 people in design, writing, testing, and recording security functions. One suggestion we propose for all designers is to develop a plan, migrating the corresponding applications and tools from the unit Win32® C code to the managed code. In doing, there are many reasons, mainly to help reduce buffer overflow. In the hosted code, it is difficult to create a code containing buffer overflow because the code written cannot directly access pointers, computer registers or memory. You should consider, or at least you want to migrate some applications and tools into the managed code. For example, the management tool is a good migration object. Of course, we must also reality because it is impossible to migrate all applications from C to C # or other managed languages ​​at one night.

Follow the following important rules

When writing C and C code, pay attention to how to manage data from users. If a function has a buffer from an unreliable source, follow the rules:

The length of the code transfer buffer is required. Detect memory. Take preventive measures.

Now let's take a closer look at each case.

Require code to pass the length of the buffer

If any function call has a similar feature, an error will appear:

Void function (char * szname) {

Char szbuff [MAX_NAME];

// Copy and use SZNAME

STRCPY (SZBUFF, SZNAME);

}

The problem with this code is that the function cannot determine the length of SZNAME, which means that the data will not be securely replicated. The function should know the size of the szname:

Void function (char * szname, dword cbname) {char szbuff [MAX_NAME];

// Copy and use SZNAME

IF (cbname

STRNCPY (SZBUFF, SZNAME, MAX_NAME-1);

}

However, you can't trust cbname to be trusted. Attackers can set this name and buffer size, so you must check!

Detect memory

How to discriminate SZNAME and CBNAME is effective? Do you believe that users will provide effective values? In general, the answer is negative. A simple way to verify if the buffer size is effective is to detect memory. The following code segment shows how to complete this verification process in the debug version of the code:

Void function (char * szname, dword cbname) {

Char szbuff [MAX_NAME];

#ifdef _Debug

// detection

MEMSET (SZBUFF, 0X42, CBNAME);

#ENDIF

// Copy and use SZNAME

IF (cbname

STRNCPY (SZBUFF, SZNAME, MAX_NAME-1);

}

This code will attempt to write value 0x42 to the target buffer. You may think, why do you do this instead of copying the buffer? By writing a fixed known value at the end of the target buffer, the force code fails when the source buffer is too large. At the same time, it is also possible to discover the development error in the development process. It is better to fail with the malicious effective code that runs an attacker. This is why no copy of the attacker's buffer.

Note: You can only do this in the debug version to capture the buffer overflow during the test.

Take preventive measures

To be honest, the detection is useful, but it does not allow you to attack. Really safe way is to write a prevention code. You will notice that the code has been protected. It will check if the data of the entry function does not exceed the internal buffer SZBuff. However, when some functions are processed or copied, there is a potential serious security issue if not used. The key here is unreliable data. When checking the buffer overflow error, the flow of data should be tracked in the code and check the various data assumptions. When you realize that some assumptions are incorrect, you may be amazed to the mistakes found.

The functions you need to note include common functions such as strcpy, strcat, gets. However, it is also not possible to exclude "secure N versions" - Strncpy and Strncat. These functions are considered to be safer and reliable because they allow developers to limit the size of data copied to the target buffer. However, developers will also be wrong when using these functions! Please see the following code. Can you see the shortcomings?

#define size (b) (SizeOf (b))

Char buff [128];

STRNCPY (BUFF, SZSOMEDATA, SIZE (BUFF));

Strncat (buff, szmoredata, size (buff));

Strncat (buff, szevenmoradata, size (buff));

If you need a prompt, please pay attention to the last parameter of each string handler. Want to give up? Before I give the answer, I often joked, if you disable the "unsafe" string handler, use a more secure N version, I am afraid you have to spend your resident in repairing new errors. . The following is the reason. First, the last parameter is not the overall size of the target buffer. It is the size of the remaining space of the buffer, and the buff will be substantially reduced when the code adds content to the buff. The second question is that even if the user passes the buffer size, they usually decrease one by one. So when calculating the string size, do you have empty characters that contain the end? When I investigate this problem, it is usually half a minute. One half of them believes that at the calculation buffer is really considered at the end of the tail character, and the other half will not think so. Third, in some cases, N version may not end the character with empty characters as the result string, so be sure to read the document. If you write C code, consider using ATL, STL, MFC, or your favorite string processing classes to process strings without direct processing bytes. The only potential shortage is that performance declines, but in general, most of these classes will make the code more powerful and maintainable.

Compile with / GS

This new compile time in Visual C ® .NET is inserted into the value of some functions, which helps reduce the potential weaknesses of the stack-based buffer overflow. Keep in mind that this option will not fix your code and you cannot delete any errors. It's just like a baseball cracker to help you reduce the potential of certain types of buffer overflows to become the potential of buffer overflows that can be used, so as to write code and execute in the process. It can be regarded as a small insurance measure. Note that this option will be enabled by default for new native Win32 C projects created using Win32 Application Wizard. In addition, this option is also used when Windows .NET Server compiles. For more information, see Brandon Bray's Compiler Security Checks in Depth.

Exclude hidden dangers

Below I gave some code, at least one security hazard. Can you find it? I will announce the answer in the next article!

Wchar g_wszcomputername [internet_max_host_name_length 1];

// Get the server name and convert it to a Unicode string.

Bool getServerName (extension_control_block * pecb) {

DWORD DWSIZE = SizeOf (g_wszcomputername);

Char szcomputername [internet_max_host_name_length 1];

IF (PECB-> GetSerVariable (PECB-> ConnID,

"Server_name",

Szcomputername,

& dwsize)) {

// The rest of the code is omitted

Michael Howard is one of the security program managers of the Microsoft Secure Windows Initiative group, and one of the "Writing Secure Code" authors. His main job is to ensure people design, build, test, and record unfamiliar safety systems. What is his favorite, it is "short, and the inch".

转载请注明原文地址:https://www.9cbs.com/read-125834.html

New Post(0)