Additional Topics

There are two additional topics to cover here. Some overflows don t allow for code execution but still result in issues nonetheless. Also, a few ramifications of the /GS compiler switch are worth noting. The following sections discuss some of these types of bugs .

Noncode Execution Overflows Can Be Serious, Too

Sometimes attackers find other ways to exploit overflows besides getting their code to run, and not all serious overflows throw exceptions. Certain overflows do not allow attackers to take control, but might instead allow them to read or manipulate extra data. Such is the case of Logon.exe, a utility that enables administrators to log on to a service. Because the password is cryptographically random each time, it is pretty hard to guess. Logging on without knowing the password requires either looking in memory (we assume this is off limits) or being crafty.

Let s see how this works. Note that the text in bold type is user input for the walkthrough.

 E:\Chapter8\Code\Logon\Debug>Logon.exe USAGE: Logon.exe <username> <password> 

Try entering bogus parameters:

 E:\Chapter8\Code\Logon\Debug>Logon.exe User Password Access Denied. 

Then start trying long strings:

 E:\Chapter8\Code\Logon\Debug>Logon.exe aaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Access Denied. E:\Chapter8\Code\Logon\Debug>Logon.exe aaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa a Access Denied. E:\Chapter8\Code\Logon\Debug>Logon.exe a aaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaa Welcome!! You are now logged in as a. 

That s a bit strange ”the service let you log on by using all letter a characters . Check whether it happens again:

 E:\Chapter8\Code\Logon\Debug>Logon.exe a ddddddddd dddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddddddd dddddddd Welcome!! You are now logged in as a. 

Using a different password with the same user name still worked! So you must file a bug report about this behavior because the program allows you to log on if you specify a long password, regardless of whether the password is correct. Let s look at why this is happening.

The class is defined as follows .

   #define CREDENTIAL_LENGTH 64 class Login { public:    Login();    void ClearCreds();    bool IsLoggedIn();    bool TryCreds(char *Username, char *Password);    virtual ~Login(); private:    char UserName[CREDENTIAL_LENGTH];    char PassPhrase[CREDENTIAL_LENGTH];    char CorrectPassPhrase[CREDENTIAL_LENGTH];    char Buffer[512]; };   

What is interesting about this is that the PassPhrase and CorrectPassPhrase are stored sequentially in memory. Look at the code that checks whether the password is correct:

   bool Password::IsLoggedIn() {    return(0==memcmp(PassPhrase,CorrectPassPhrase,CREDENTIAL_LENGTH)); }   

That looks good. How about the caller?

 bool Login::TryCreds(char *User, char *Password) {    FillMemory(UserName,CREDENTIAL_LENGTH,0x00);    strcpy(UserName,User);    FillMemory(PassPhrase,CREDENTIAL_LENGTH,0x00);    strcpy(PassPhrase,Password);    return IsLoggedIn(); } 

Aha! The strcpy(PassPhrase,Password); code looks suspicious. What would happen if this were to overflow the PassPhrase[] buffer? It would start to set the CorrectPassPhrase[] buffer because it comes right afterward in memory. If Password contained 2 * CREDENTIAL_LENGTH bytes, and the first half matched the second half, the function IsLoggedIn check would return true regardless of the real CorrectPassPhrase .

Fixing this is fairly easy: simply check the length of the input and fail if it is too large.

   bool Login::TryCreds(char *User, char *Password) {    if ((strlen(User) < CREDENTIAL_LENGTH) &&       (strlen(Password) < CREDENTIAL_LENGTH))    {       FillMemory(UserName,CREDENTIAL_LENGTH,0x00);       strcpy(UserName,User);       FillMemory(PassPhrase,CREDENTIAL_LENGTH,0x00);       strcpy(PassPhrase,Password);       return IsLoggedIn();    }    else    {       return false;    } }   

Trying out the fixed version shows that the bug is fixed for this case. Note that both the user name and password had to be validated because the same overflow existed for the user name, but it was not discovered immediately.

/GS Compiler Switch

In a nutshell , the /GS compiler switch for some functions places a value between stack variables and critical values (including the stored value of EBP and the address to return to) on the stack. Right before using these values, the cookie is then used to determine whether an overrun occurred. If an overrun occurred, it stops rather than running the attacker s code. (See http://msdn.microsoft.com/library/en-us/dv_vstechart/html/vctchCompilerSecurityChecksInDepth.asp for details.)

That s nice ”but if the Microsoft VS.NET C runtime library is not included in the compilation, the value of the cookie used by the /GS compiler switch will not be random, and attackers can guess what the cookie value will be, meaning the /GS switch will not offer any real protection to victims. A call to __security_init_cookie in seccinit.c (which shows the cookie value to be dependent on a variety of factors, from how long the machine has been running to the current thread and process ID) can remedy the problem.

Testing Whether the Binary Was Compiled Using /GS

One way you can tell whether the cookie is random is to examine the behavior at run time to check the cookie value. If you have debug symbols, you can run the retail binary in the debugger and query the __security_cookie value directly.

To start, launch the application GSWindowsApp, launch the debugger and attach to the process, and then break in the debugger. You can then view the Watch window and add a watch on &__security_cookie . Figure 8-24 shows an example of this.

image from book
Figure 8-24: The value of the /GS security cookie

Close the application, run it again, reattach the debugger. Figure 8-25 shows what you get the next time. Compare the values of the cookie and where the cookie is located in memory with those shown in Figure 8-24. The cookie is stored at the same place both times for this application, but the value changes.

image from book
Figure 8-25: The value of the /GS security cookie and its location in memory
Note  

Think about it: will the security cookie always be stored in the same location for a particular application? Could that create some potential for issues?

If the security cookie value had been the same for both cases, you would flag that as a major issue.

The next question becomes: how can you tell whether the /GS switch was used to compile the binary?

Warning  

Even retail binaries compiled without the /GS switch on set a value for the __security_cookie .

One way to tell is to set a breakpoint on the __security_check_cookie function. If the EXE is linked with libraries that were compiled with the /GS switch turned on or it loads dependencies with the switch turned on (such as msvcrt ), you will see some hits with this regardless. The application will function fine and the hits will be occasional , if at all. You can examine the call stack to see whether the application (code your developers wrote) is on the top of the call stack or not to determine whether your code called this __security_check_cookie function.

Figure 8-26 shows a case where the code calls the __security_check_cookie function.

image from book
Figure 8-26: Code actually checking the cookie

By checking out the caller in Figure 8-26, you can tell which code called the security check handler and thus whether this particular hit on the breakpoint was caused by your code or another component s code. In this case, our code is revealed as causing the hit. Note this is valid to do only with the nonoverflow test data ”overflow test data corrupts the call stack and the caller cannot be trusted in the call stack window for that case.

Note  

A number of people have researched the /GS switch and have found that it really is only a defense- in-depth measure, and that there are ways around it. David Litchfield published some ways around the cookie at http://www.blackhat.com/presentations/bh-federal-03/bh-fed-03-litchfield.pdf .

/GS Information Disclosure Vulnerability

When you test a /GS -compiled binary for overruns and encounter the /GS dialog box shown in Figure 8-27, you have a fairly decent indication a buffer overrun has occurred. First, even though /GS aborts the program, these overruns should still be fixed. Programmers could copy the code or enough changes might occur to the function and the /GS switch might not protect victims against the (now advertised) overrun any longer.

image from book
Figure 8-27: The /GS dialog box

Consider this scenario briefly : you are testing or using the product, and you see the dialog box shown in Figure 8-27. This dialog box appears any time the /GS stack checks fail. If it appears, the same test case typically results in an exploitable condition for the same code built without the /GS switch.

Picture what happens when users get the message shown in the figure. On the surface, that s a good thing, right? Users are protected. The programmer should fix the bug.

But consider this: what happens if a malicious user sees this message and investigates only to find the problem exists in previous versions of the application that were not compiled with the /GS switch turned on? It really pays to take care of these overruns.



Hunting Security Bugs
Hunting Security Bugs
ISBN: 073562187X
EAN: 2147483647
Year: 2004
Pages: 156

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net