Mega Code Archive

 
Categories / Delphi / Ide Indy
 

Whats a buffer overflow and how to avoid it in Delphi

Title: What's a buffer overflow and how to avoid it in Delphi? Question: Buffer overflow problems always have been associated with security wholes. Lots of security problems have occurred due to buffer overflow. Answer: This article tries to explain what a buffer overflow is and what countermeasures (or counterattacks;) can be taken to avoid it. A buffer is a contiguous allocated block of memory, such as an array or a pointer in Pascal. In C and C++, there are no automatic bounds checking on the buffer (responsability by the programmer), which means a hacker can spoil the buffer. for example: int main () { int buffer[5]; buffer[7] = 10; } In Delphi I mean as far as Delphi uses Pascal style strings, programs are much safer than those made in C/C++. Strong types with internal checks of operations and ranges by the compiler at design-time can prevent an overflow. Delphi can use Pascal strings as well as generic windows strings (PChar). When interfacing with Win API there is no other option except using Pchar. This mean that potentially Delphi is a bit safer, because the potential problems can be isolated - more experienced developers are allowed to work with PChar, while less experienced allowed to work only with encapsulated functionality using Pascal String. Internal represantation of a Delphi String: ------------------------I-------------I---------------I--------------I referencecounter32bit length(32bit) payload(nbyte) unused space ------------------------I-------------I---------------I--------------I I---string variable On the heap we could find something like this: Type TLongString = record refCount: integer; length: integer; chars: array[1..length] of char; nullChar: char; end; PLongStr = ^TLongString; The Delphi compiler hiddens the fact that the string variable is a heap pointer to the above structure but setting the memory in advance is advisable: path: string; setLength(path, 1025); setLength(path, getSystemDirectory(pChar(path),length(path)-1)); Is it really safer? Ok. we can say it's safer against character-array buffer overflows or Delphi isn't used enough to make attacking it interesting. But most attacks succeded on the STACK but not on a HEAP structure, cause overriding a heap is more difficult . This does not imply that the language is overall safer - there could be very well Nignificant damage elsewhere, and char-based overflows are only one attack method. For example you declare a PChar buffer, you have the possibility to check with GetMem() the capacitiy otherwise you get an EOutOfMemory-exception: Buffer: PChar; //not a buffer before you use getmem Size:= FileSize(F); GetMem(Buffer, Size); //allocates n-Bytes on the heap BlockRead(F, Buffer^, Size); What's the danger? ------------------------------------------------------------ Most of "unsecure" programs are valid, and "almost" every compiler can compile it without any errors. However, the attacked program attempts to write beyond the allocated buffer memory, which might result in unexpected behavior. Because malicious code (assembly instructions to spawn a root shell) is an input argument to the program, it resides in the stack and not in the code segment. Therefore, the simplest solution is to invalidate the stack to execute any instructions. Execution is done by overwriting a function's return address (in the stack), an intelligent hacker might want to spawn a shell (with root permissions) by jumping the execution path to such code. A hacker or attacker places the code they are trying to execute in the buffer's overflowing area. With the manipulated return address it points back to the buffer and executes the intended code. That's it. Avoid it ------------------------------------------------------------ So Delphi is mostly secure against buffer overflows (Imho), cause of compiler and conceptual basics. Hence, the best way to deal with buffer overflow problems is to not allow them to occur in the first place. Developers should be educated about how to minimize the use of these vulnerable functions. It is advisable to make a buffer too large in the first place or implement a circular buffer (that you may receive data quicker than you process it, resulting in a buffer overflow): var Source, Dest: PChar; CopyLen: Integer; begin Source:= aSource; Dest:= @FData[FBufferEnd]; if BufferWriteSize Count then raise EFIFOStream.Create('Buffer over-run.'); Or you check with GetMem or a simple count-logic possible overflows: GetMem(Buffer, BufferSize); Ptr:= Buffer; Count:= 0; for I:= 0 to ListBox.Items.Count - 1 do begin Line:= ListBox.Items.strings[I]; // Check buffer overflow Count:= Count + Length(Line) + 3; if Count = BufferSize then Break; //the function aborts immediately. Or use try finally/raise to check buffer with a DefSize and free it: begin if FBufFixed then BSize := FBufSize else BSize:= DefSize; GetMem(Buffer, BSize); try BufEnd:= Buffer; Count:= Stream.Read(Buffer[0], BSize); BufEnd:= BufEnd + Count; if Count BSize then BufEnd[0]:= #0 else begin raise EStreamError.Create(LoadStr(SLineTooLong)); end; SetText(Buffer); finally FreeMem(Buffer, BSize); end; end; Apart from education or experience, modern compiler like Delphi change the way a program is compiled, allowing bounds checking to go into compiled code automatically, without changing the source code. These compilers generate the code with built-in safeguards that try to prevent the use of illegal addresses. Any code that tries to access an illegal address is not allowed to execute. Or a tool does it by protecting the return address on the stack from being altered. It places a canary word next to the return address whenever a function is called. If the canary word has been altered when the function returns, then some attacks or attempt has been made on the overflow buffers. Furthermore, it may affect the application's performance to a great extent. In some case, executable size and execution time may increase a certain way. That's the prize for more code security. Max Kleiner Update 16.9.2009 The run-time checks are useful to developers who not only want to write secure code but also care about the fundamental issue of writing correct code. When overflow checking is turned on (the {$Q+} compiler directive), the compiler inserts code to check that involved CPU flag and raise an exception if it is set. On the other side a buffer overrun that injects code into a running process is referred to as an exploitable buffer overrun. So the question remains how the compiler can help us: The directive {$Q+} checks for integer overflow operations or stack overflow; but for no buffer overflow! This feature was never designed to operate in production code. In Delphi the compiler checks at design-time for overflows. let's see how: var Buffer: array[0..25] of Char; Buffer:= 'this is too long and so on'; SetString(mystring, Buffer, sizeOf(buffer)); When you add a char to the string in buffer or you reduce the array to 0..24 the compiler reamrks: [DCC Error] ucompiler_help.pas(48): E2010 Incompatible types: 'Array' and 'string' But the prize is to use an array no way with buffers like buffer: PChar; I think there's always a trade off between open strings which you can't always control but are more flexible at runtime and fixed or static strings which are easier to design but one more char spoil it or the compiler is clever enough to warn.