Release Date: 7/30/2004
| Update Date: 7/30/2004
Michael HowardSecure Windows Initiative
Abstract: Michael Howard has studied a commonly ignored code constructure that may result in a severe buffer overflow problem, and then introduce an alternative to an arithmetic operation without overflowing side effects.
Talk about the structure
Very strange, there is such a lot of safety guidance document that people pay attention to dangerous functions. In C and C , there are very dangerous functions, but there is a matter of affirmation, and there are many dangerous developers who are using C and C .
Therefore, you may ask, "Michael, what do you want to discuss?"
I have to admit that some of the tired of some documents say that some functions are dangerous, and you should use a safer type instead of them. For example, "Don't use strcpy, it is dangerous. You should use StrncPy because it is safe." Nothing is much more away from the actual situation. It is possible to use Strcpy's code to be safe, and the calling strNcPy is unsafe code.
Functions like Strcpy are potentially dangerous because the source data is larger than the target buffer, and it comes from untrusted sources. If the source data comes from a trusted source, the call strcpy is safe before replication.
Void func (char * p) {
Const int max = 10;
Char BUF [MAX 1];
MEMSET (BUF, 0, SIZEOF (BUF));
IF (P && Strlen (P) <= max)
STRCPY (BUF, P);
}
Letter don't believe you, I just want to use this example somewhere. There is a commonly ignored structure that can cause buffer overflow, it is not a function call. It is like this:
While ()
* D = * s ;
There is no function call here, which is the coding structure that causes the Blanter WORM worm in the DCOM. In Buffer Overrun In RPC Interface Could Allow Code Execution, you can read more about this virus fixing.
The code is as follows:
HRESULT GETMACHINENAME (Wchar * Pwszpath) {
Wchar WSZMACHINENAME [N 1])
LPWSTR PWSZSERVERNAME = WSZMACHINENAME;
While (* pwszpath! = l '//')
* PWSZSERVERNAME = * Pwszpath ;
...
}
The problem here is that the While loop is bound in some characters in the source string. It does not limit the size of the target buffer. In other words, if the source data is not trusted, the buffer overflow will occur.
I have written a simple Perl script to search for these types of configurations in C and C code. Note that each instance of this script tag is not a defect, you need to determine whether the source data is trusted.
Use strict;
Use file :: find;
MY $ Recurse = 1;
######################################################################################################################################################################################################################################################################################################## #
Foreach (@argv) {
Next if / ^-./;
IF ($ recurse) {FindDepth (/ & processfile, $ _);
} else {
Find (/ & processfile, $ _);
}
}
######################################################################################################################################################################################################################################################################################################## #
Sub processfile {
MY $ file;
MY $ filename = $ _;
IF ($ recurse && ($ file :: find :: topdir ne $ file :: find :: dir)) {
$ FILE :: Find :: prune = 1;
Return;
}
# Only Accept C / C and Header Extensions
RETURN IF (! (//. [CH] (?: PP | XX)? $ / i));
WARN "$! / N" Unless File, "<". $ filename;
# RESET LINE NUMBER
$. = 0;
While () {
Chomp;
S / ^ / s //;
S // s $ //;
IF (// * / w / / / s {0,} = / s {0,} / * / w / / ) {
Print $ filename. "" "" "" $ _. "/ n";
}
}
Note this script only looks for * p constructs without looking for * p construction.
Assuming that you have discovered a defect, a method of safer code is to limit the copied data is not greater than the target buffer:
HRESULT GETMACHINENAME (Wchar * Pwszpath) {
Wchar WSZMACHINENAME [N 1])
LPWSTR PWSZSERVERNAME = WSZMACHINENAME;
SIZE_T CBMACHINENAME = N;
While (* pwszpath! = l '//' && --cbmachinename)
* PWSZSERVERNAME = * Pwszpath ;
...
}
Finally, any memory replication functions or constructs that do not limit the size of the target buffer should be strictly inspected.
Back to top
More introduction to integer overflow
In the previous article Reviewing Code for Integer Manipulation Vulnerabilities, I discussed the security defects related to the simple mathematical computing of the so-called integer overflow.
Recently, as part of Trustworthy Computing Engineering Series, I made a lecture on Microsoft's engineers a little overflow. In the lecture, I outline how to find an integer overflow and how to fix an integer overflow. What I am surprised is that many emails I have received are very good, but they are full of danger. Please allow me to do some explanations.
In this column, the code I mentioned is as follows:
IF (A B> max) Return -1;
It should be changed to this:
IF (A B> = A && A B // cool! } Three years ago, some people pointed out that some people will see this code, but I don't know what it uses, so it may delete the A B> = A section, because it looks purely, and now, the integer overflows and re-reable The appearance is in front of you. No way! In response, I wrote the header file below, and it's understandable. Yes, it looks like garbled, but this paragraph is the X86 assembly language. The reason why I use assembly language is because it allows me to directly access the JC operand, ie jump-on-carry. In other words, it detects whether the mathematical operation will result in overflow. #ifndef _inc_intoverflow_ #define _inc_intoverflow_ #ifdef _x86_ Inline bool uadd (size_t a, size_t b, size_t * r) { __ASM { MOV EAX, DWORD PTR [A] Add Eax, DWORD PTR [B] MOV ECX, DWORD PTR [R] Mov DWORD PTR [ECX], EAX JC SHORT J1 MOV Al, 1 JMP SHORT J2 J1: #ifdef _Debug INT 3 #ENDIF XOR Al, Al J2: } } Inline Bool Umul (size_t A, size_t b, size_t * r) { __ASM { MOV EAX, DWORD PTR [A] Mul DWORD PTR [B] MOV ECX, DWORD PTR [R] Mov DWORD PTR [ECX], EAX JC SHORT J1 MOV Al, 1 JMP SHORT J2 J1: #ifdef _Debug INT 3 #ENDIF XOR Al, Al J2: } } INLINE BOOL Umuladd (size_t mul1, size_t mul2, size_t add, size_t * r) { SIZE_T T = 0; IF (Umul (Mul1, Mul2, & T)) Return UADD (T, Add, R); Return False; } #ELSE # r "this code compiles only on 32-bit x86 #ENDIF / / _X86_ #ENDIF / / _INC_INTOVERFLOW_ Check this file, which includes explaining a simple document for these functions. Back to top Discover security vulnerability Although it took a little time, many people found a vulnerability. This comparison should be a comparison of regional constant comparisons or byte mode when making security decisions by comparing strings. In this example, the code I have written may allow access to Turkish sensitive data, because in Turkish, the letters "i" have four examples, two lowercase letters, two uppercase letters. You can read this in this area on http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemstringclasscomparetopic5.asp. Now let us go to the mistake of this month. What is the problem with this code? Void func (char * p) { Char BUF [10 1]; MEMSET (BUF, 0, SIZEOF (BUF)); // Limit String to 10 Chars Sprintf (buf, "% 10s", p); Printf ("Hello,% S / N", BUF); } Back to top A small intelligence game This intellectual game is actually unrelated with security, but when I think people are troubled by the integer overflow detection code, I pulled it from my memory. What is the use of this code? INT A = 0x42; INT B = 0x69; a ^ = b; B ^ = a; a ^ = b; The rules of this game are very simple, you can't compile or explain this code. Try only by observation to determine what it is. Michael Howard is a Senior Security Program Manager for the Microsoft Secure Windows Initiative group, one of the Writing Secure Code, and the second edition of the book is now released. He is still the main author of Designing Secure Web-Based Applications for Windows 2000. He is committed to ensuring that the system is designed, constructed, tested, and recorded in accordance with safety requirements. One of his favorite words is "one person's tool, the weapon of others." Reviewing code for Integer Manipulation VulneRabilities Michael HowardSecure Windows Initiative April 28, 2003 Summary: Michael Howard Dishes on Integer Manipulation Vulnerabilities and Outline A Security Plan That You Can Use To Safeguard Your Own Applications. (8 Printed A couple of years ago, very few people had heard of integer overflow attacks, and now it seems there's a new one every few days Below is a short list of some integer overflow security bugs found in the last few months.: Sun RPC XDR_Array OpenSSH Authentication Apache Chunked Encoding Microsoft Jscript Freebsd Socket and System Calls Snort TCP Packet ReassemblyMbly In this Month's Column, I'll Outline How The bugs Occur, How you can hunt for thermure, and how you can fix them. Oh, before I get into the core of this article, I'm happy to announce that Writing Secure Code received the RSA Conference Award for Industry Innovation at the RSA Security Conference held in San Francisco, April 2003. Okay, Back to Integer Attacks. I'm not going to explain what an integer is, I assume you know what they are, and you know that there are basically two flavors-signed and unsigned-where signed integers have the high-bit set to 1 when the value is negative This is a consequence of 2's Complement Arithmetic. And you Also Know There Different Size Integers, The Most Common Being 64-Bit, 32-Bit, 16-Bit, And 8-Bit in Length. That's All I'm Going To Say About Integers, And for The Purposes of this Paper, That's All You NEED KNOW.THERE ARE Three Main Integer Manipulation That Can Lead To Security VulneRabilities: Overflow and underflow signed version unsigned errors truncation On their own, these issues may not cause security errors. However, if your code exhibits one or more of these issues and manipulates memory, the potential for a buffer overrun error or application failure increases. Let's look at each in detail. Overflow and underflow Quick, What's WRONG WITH CODE? BOOL FUNC (SIZE_T CBSIZE) { IF (CBSIZE <1024) { // We never deal with a string trailing null Char * buf = new char [cbsize-1]; MEMSET (BUF, 0, CBSIZE-1); // Do Stuff DELETE [] BUF; Return True; } else { Return False; } } The code is correct, right? It validates that cbSize is no larger than 1 KB, and new or malloc should always allocate 1 KB correctly, right? Let's ignore the fact that the return value of new or malloc should be checked for the moment. Also, cbSize can not be a negative number, because it's a size_t. But what if cbSize is zero? Look at the code that allocates the buffer-it subtracts one from the buffer size request. Subtracting one from zero causes a size_t variable, which is An Unsigned Integer, To Wrap Under To 0xFffffff (Assuming A 32-Bit Value), or 4 GB. Your Application Just Died-or Worse! Here's A Similar Issue: BOOL FUNC (Char * S1, SIZE_T LEN1, Char * s2, size_t len2) { IF (1 len1 len2> 64) Return False; // accommodate for the trailing null in the addition Char * buf = (char *) Malloc (Len1 Len2 1); IF (buf) { Stringcchcopy (BUF, LEN1 LEN2, S1); Stringcchcat (buf, len1 len2, s2); } // DO Other stuff with buf IF (BUF) Free (BUF); Return True; } Once again, the code looks well written; it checks the data sizes, it verifies that malloc succeeded, and it uses the safe string handling functions, StringCchCopy and StringCchCat (you can read more about these string handling functions at http: // msdn. microsoft.com/library/en-us/dnsecure/html/strsafe.asp). However, this code suffers from an integer overflow. What if len1 is 64, and len2 is 0xFFFFFFFF? The code that determines the buffer size is legal adds 1, 64 and 0xFFFFFFFF together, which yields 64, because the addition operation wraps around. Next, the code allocates only 64 bytes, and the code then builds a new string of 64 bytes in length, and then concatenates 0xFFFFFFFFF bytes to the string. Once again, the application dies, and in some cases, some code when attacked with carefully crafted sizes can lead to exploitable buffer overrun attacks.There's another lesson here -safe string handling functions are not safe if you get the buffer size calculation incorrect. The Jscript overflow Attack THE SAME KIND OF OVERFLOW BUG CAN OCCUR WHEN MULTIPLYING, WHICH IS What HAPPENED IN The Microsoft Jscript bug. The Bug Only Manifests Itself WHEN USING The JScript Sparse Array Support: VAR arr = new array (); Arr [1] = 1; Arr [2] = 2; Arr [0x40000001] = 3; In this Example, THE ARRAY HAS Three Elements and a Length of 0x40000001 (1073741825 Decimal). But Since this Example Usess sparse arrays, it online. The C code that implements the JScript customized sort routine allocates a temporary buffer on the heap, copies the three elements into the temporary buffer, sorts the temporary buffer using the custom function, and then moves the contents of the temporary buffer back into the array. Here's The Code That Allocates The Temporary Buffer: TemporaryBuffer = (Element *) Malloc (ElementCount * SizeOf (Element)); An Element is a 20-byte data structure used to hold an array entry. It looks like the program will attempt to allocate around 20 GB for the temporary buffer. You might think that since most people do not have 20 GB of memory in their . WHEN Using 32-Bit Integer Arithmetic, We get an integer overflow attack because the result (0x0000000500000014) Is Too Large to Hold In A 32-bit value: 0x40000001 * 0x00000014 = 0x0000000500000014 C throws away all the bits that do not fit, so we get 0x00000014. This is why the allocation does not fail-the allocation does not attempt to allocate twenty billion bytes, but rather it attempts to allocate only twenty bytes. The sort routine then assumes that the buffer is large enough to hold the three elements in the sparse array, so it copies the 60 bytes that make up the three elements into a 20 byte buffer, overrunning the buffer by 40 bytes. Oops! Signed vs. unsigned ErrorS Take a Quick Look at The Following Code. It's Similar To The First Example. See if You can spot the error, and if you do, try to determine what the result is. Bool Func (Char * S1, INT LEN1, Char * s2, int len2) {charf [128]; IF (1 len1 len2> 128) Return False; IF (buf) { STRNCPY (BUF, S1, LEN1); STRNCAT (BUF, S2, LEN2); } Return True; } The problem here is the string sizes are stored as signed integers, so len1 can be larger than 128 as long as len2 is negative, hence the sum is less than 128 bytes. However, a call to strncpy will overflow the buf buffer. Truncation ErrorS Now Let's Look At The Last Attack Type, By Way of, You Guessed It, A Code Example. Bool Func (Byte * Name, DWORD CBBUF) { Unsigned short cbcalculatedbufsize = cbbuf; Byte * buf = (byte *) malloc (cbcalculatedbufsize); IF (buf) { Memcpy (BUF, Name, CBBUF); // do stuff with buf IF (BUF) Free (BUF); Return True; } Return False; } The attack, or at least the result, is a little like the JScript bug outlined earlier. What if cbBuf is 0x00010020? CbCalculatedBufSize is only 0x20 because only the lower 16-bits from 0x00010020 are copied. Hence only 0x20 bytes are allocated, and 0x00010020 BYTES Are Copied Into The Newly Allocated Target Buffer. Note That Compiling This Code with Microsoft Visual C ® / W4 Option Yields: Warning C4244: 'Initializing': Conversion from 'DWORD' To 'unsigned Short ', Possible Loss of Data Be aware That Operations Like The Following Do Not Flag A Warning: INT LEN = 16; Memcpy (BUF, SZDATA, LEN); The last argument to memcpy is a size_t, yet the argument, len, is signed. The warning is not issued because memcpy always assumes the third argument is unsigned and putting a cast to unsigned would not change the function outcome. Note you will get a warning if you attempt to assign a DWORD with a size_t, not because there is a potential loss of data on 32-bit platforms, but because there will be data loss on 64-bit platforms: warning C4267: '= ': Conversion from' size_t 'to' dword ', Possible Loss Of data YOU GET THIS WARNING BECAUS Compile with the --wp64 option, Which Tells The Compiler to Watch for 64-bit portability issues. Integer Manipulation Issues in Managed Code Integer manipulation errors can occur in managed languages, such as C # and Visual Basic® .NET, but the potential for damage is greatly reduced as your code does not have direct access to memory. However, calling into native code (assuming your code is granted the permission to call unmanaged code.) can still cause security issues like those noted above. Integers in the Common Language Specification (CLS) are signed, and one mistake is to validate signed integer arguments in managed code, when the variables are treated as unsigned Integers in the unmanaged code. This is a specific case of the more general advice of always check what you're passing to unmanaged code. Many integer manipulation bugs in managed code can cause reliability errors in Visual Basic .NET, because all such operations will raise the System.OverflowException if An Overflow or underflow Occurs. By Default, C # Does Not thving has exceptions. Use the checked keyword if you wish to check for these issues: UINT32 I = 0xffffffff0; UINT32 J = 0x00000100; UINT32 K; Checked {k = i j;} REMEDIES WHO WOULD HAVE THOUGHT THAT SIMPLY MANIPULATING INTEGHT THAT THATED LEAD TO SECURITY PROBLEMS? A Simple Remedy To Vulnerable Code Like this: IF (A B> max) Return -1; Is to use this code with unsigned integers: IF (A B> = A && A B // cool! } The First Operation A B> = a, checks for the wrap-around As for the multiplication issue in JScript, you can check that the number of elements is no larger than a predetermined value that is smaller than the largest amount of memory you are willing to allocate. For example, the following code could potentially allocate up to 64 MB: Const size_t max = 1024 * 1024 * 64; Const size_t elem_size = sizeof (element); const size_t max_elex = max / elem_size; IF (CELEMS> = MAX_ELEMS) Return False; Finally, Use Unsigned Integers (Such as DWord and size_t) for array indexes, buffer sizes, and the like. Key Code Reviewing Points Keep The Following In Mind When Compiling Or Reviewing Code for Integer-Related Issues: Compile C and C code with the highest possible warning level, / W4. Use size_t or DWORD for buffer sizes and element counts. There is no reason to use a signed value for such constructs. Keep in mind that a size_t is a different type depending On the Platform you use. a size_t is the size of a memory address, SO IT IS A 32-BIT VALUE ON A 32-BIT VALUE ON A 64-bit Platform. if Your Code Performs Any Kind of integer manipulation (addition, multiplication, and so on) where the result is used to index into an array or calculate a buffer size, make sure the operands fall into a small and well-understood range. Be wary of signed arguments to memory allocation functions (new, malloc, GlobalAlloc, and so on) because they are treated as unsigned integers. Watch out for operations that yield C4018, C4389, and C4244 warnings. Watch out for casts that cast away the C4018, C4389, and C4244 warnings. Investigate All Use of #pragma Warning (Disable, CNNNN) That Disable the C4018 , C4389, and C4244 warnings. In fact, comment them out, re-compile, and check all new integer-related warnings. Code migrated from other platforms or compilers may make different assumptions about data sizes. Watch out! If calling unmanaged code from managed code, make sure you confirm that it is signed correctly. Many arguments to Win32 APIs are unsigned ints or DWORDs, and many managed code variables are signed. Finally, if you are using managed code, make sure you catch OverflowExceptions, if appropriate. Spot The Security Flaw Many People Worked Out My Last Flaw. It is an integer overflow attack. So what's wrong with this c # code? String status = "no"; String sqlstring = "" Try { SqlConnection SQL = New SqlConnection (@ "Data Source = localhost;" "User ID = sa; password = password;"); SQL.Open (); SQLString = "SELECT HASSHIPPED" "From detail where id = '" ID "" " SQLCommand cmd = new sqlcommand (sqlstring, sql); IF ((int) cmd.executescalar ()! = 0) Status = "yes"; } catch (sqlexception se) { Status = SQLSTRING "failed / n / r"; Foreach (SQLERROR E IN Se.Error) { Status = E.Message "/ N / R"; } } catch (exception e) { Status = e.tostring (); } Michael Howard is a Senior Security Program Manager in the Secure Windows Initiative group at Microsoft and is the coauthor of Writing Secure Code, now in its second edition, and the main author of Designing Secure Web-based Applications for Windows 2000. His main focus in Life is Making Sure People Design, Build, Test, and Document Nothing Short of A Secure System. His Favorite Line IS "One Person's Feature IS ANOTHER's Exploit."