Welcome to MSDN Blogs Sign in | Join | Help
Well, we are finally starting to see some nice weather in Seattle! I don't know if there is a better place to be on a sunny day!

BETA2 of Microsoft Threat Analysis & Modeling v2.0 (formerly codenamed “ACE Torpedo”) is now available for download here.

 

Check out this blog for more info: http://blogs.msdn.com/threatmodeling/

 

For those of you that haven't downloaded it yet, you should. It's a great tool that helps automate the creation of a threat model. Very slick and very useful!

 

 

 

 

It seems like more and more developers are making security mistakes when dealing with sockets. See if you can Spot the Bug.

void Socket_Setup(void)
{
  WORD wVersionRequested;
  WSADATA wsaData;
  wVersionRequested = MAKEWORD( 2, 2 );
  ::WSAStartup(wVersionRequested, &wsaData);
 
  SOCKET sTCPServer = ::socket(AF_INET, SOCK_STREAM, 0);
  struct sockaddr_in saTCPServAddr;
  saTCPServAddr.sin_family = AF_INET;
  saTCPServAddr.sin_addr.S_un.S_addr = ::htonl(INADDR_ANY);
  saTCPServAddr.sin_port = ::htons(5678);
  int len = sizeof(saTCPServAddr);
 
  int iFail =::bind(sTCPServer, (struct sockaddr*)&saTCPServAddr, len);
  DWORD dwErr;
  if(0 != iFail)
  {
    dwErr = ::WSAGetLastError();
    printf("\n\t Error occured.\n");
    return;
  }
 
  iFail = ::listen(sTCPServer, 2);
 
  struct sockaddr_in saClient;
  int iClsize = sizeof(saClient);
  SOCKET sClient = ::accept(sTCPServer, (struct sockaddr*)&saClient ,&iClsize);
 
  char strData[1024];
  ::recv(sClient, strData, 1024, 0);
 
  printf("\n\nRealServer--Data from client --- %s ---", strData);
 
  ::shutdown(sTCPServer, SD_BOTH);
 
  ::WSACleanup();
 
  return;
}

Great discussion on the last bug. For those of you that took a look at it, it dealt with insecure use of cryptography resulting in exposure to dictionary attacks. Here's a new one:

class CDatabase
{
private:
  HANDLE m_hwndMutex;
public:
  void InitDBConnection(void);
  void UpdateDB(void);
};
 
int _tmain(int argc, _TCHAR* argv[])
{
  CDatabase obj1;
  obj1.InitDBConnectionEx();
  obj1.UpdateDB();
 
  return 0;
}
 
void CDatabase::InitDBConnection(void)
{
  SECURITY_DESCRIPTOR SecurityDescriptor;
  InitializeSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
 
  SetSecurityDescriptorDacl(&SecurityDescriptor, TRUE, NULL, FALSE);
 
  SECURITY_ATTRIBUTES secAttribMutex;
  secAttribMutex.nLength = sizeof(SECURITY_ATTRIBUTES);
  secAttribMutex.lpSecurityDescriptor = &SecurityDescriptor;
  secAttribMutex.bInheritHandle = TRUE;
 
  m_hwndMutex = ::CreateMutex(&secAttribMutex,FALSE,"GUARD_DB_ACCESS_MUTEX");
 
  return;
}
 
void CDatabase::UpdateDB(void)
{
  //Acquire the db mutex
  WaitForSingleObject(m_hwndMutex,INFINITE);
 
  //Update the database
  //
 
 
  //Release the db mutex
  ReleaseMutex(m_hwndMutex);
 
  return;
}

 

Solution

            Kernel objects are used for inter process synchronization purposes viz. guarding access to the database, signaling occurrence of an event etc. Insecure creation of kernel objects may allow a malicious attacker to cause denial of service attacks. In the above example, in the insecure approach, a malicious attacker can lock the mutex and deny the valid application access to the database. Care must be taken while creating kernel objects to make sure that they do not exist before you actually create them. If they already exist, then before continuing the ownership and the access control on the kernel object must be changed. In the event that the kernel object did not exist before care must be taken to enforce strong access control on the kernel object.

 

void CDatabase::InitDBConnection(void)
{
  m_hwndMutex = ::OpenMutex(MUTEX_ALL_ACCESS, TRUE, "GUARD_DB_ACCESS");
  if(NULL != m_hwndMutex)
  {
    //The mutex has already been created by some other process!
    //Either return an error OR change the ownership and the allowed access of the already existing mutex.
  }
 
  SECURITY_DESCRIPTOR SecurityDescriptor;
InitializeSecurityDescriptor(&SecurityDescriptor,SECURITY_DESCRIPTOR_REVISION);
 
  char strUserName[2048];
  ULONG uLen = 2048;
  ::GetUserName(strUserName, &uLen);
 
  PSID psidUser = ::LocalAlloc(LPTR, sizeof(SID));
  DWORD dwSidSize = sizeof(SID);
  char strReferencedDomainName[2048]="";
  DWORD dwDomainNameLen = sizeof(strReferencedDomainName);
  SID_NAME_USE sidAccType;
 
  BOOL bSuccess = ::LookupAccountName(NULL, strUserName, psidUser, &dwSidSize, strReferencedDomainName, &dwDomainNameLen, &sidAccType);
  if(FALSE == bSuccess)
  {
    DWORD dwErrorcode = ::GetLastError();
    if (ERROR_INSUFFICIENT_BUFFER == dwErrorcode)
    {
      psidUser = ::LocalAlloc(LPTR, dwSidSize);
      bSuccess = ::LookupAccountName(NULL, strUserName, psidUser, &dwSidSize, strReferencedDomainName, &dwDomainNameLen, &sidAccType);
    }
  }
 
  PACL pAcl = NULL;
  DWORD cbAcl = 0;
  cbAcl = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(psidUser);
  pAcl = (ACL*)LocalAlloc(LPTR, cbAcl);
  InitializeAcl(pAcl, cbAcl, ACL_REVISION);
 
  bSuccess = ::AddAccessAllowedAce(pAcl, ACL_REVISION_DS, GENERIC_ALL , psidUser);

  //Only allow access to required users
  SetSecurityDescriptorDacl(&SecurityDescriptor, TRUE, pAcl, FALSE);
 
  SECURITY_ATTRIBUTES secAttribMutex;
  secAttribMutex.nLength = sizeof(SECURITY_ATTRIBUTES);
  secAttribMutex.lpSecurityDescriptor = &SecurityDescriptor;
  secAttribMutex.bInheritHandle = TRUE;
 
 
  m_hwndMutex = ::CreateMutex(&secAttribMutex,FALSE,"GUARD_DB_ACCESS_MUTEX");
 
  return;
}

Wow, we had great feedback on the last bug. Someone emailed me and said that the biggest bug was the blue font on the black background. :)   

Here is another fun bug -
Courtesy of Neelay Shah, Consultant, Foundstone

class CUserManager
{
public:
  void   CreateLogin(String * strUserName, String * strPassword);
  void   AddLoginToDB(String * strUserName, Byte bytePasswordHash[]);
};
 
int _tmain()
{
  CUserManager objUsrMgr;
 
  String * struser = S"newuser";
  String * struserpass = S"password";
 
  objUsrMgr.CreateLogin(struser, struserpass);
 
  return 0;
}
 
void CUserManager::CreateLogin(String * strUserName, String * strPassword)
{
  System::Text::ASCIIEncoding *pAscii = new System::Text::ASCIIEncoding();
 
  Byte bytePassword[] = pAscii->GetBytes(strPassword);
 
  SHA1CryptoServiceProvider *pSha1 = new SHA1CryptoServiceProvider();
  Byte byteHash[] = pSha1->ComputeHash(bytePassword); 
 
  AddLoginToDB(strUserName, byteHash);
 
  return;
}
 
void CUserManager::AddLoginToDB(String * strUserName, Byte bytePasswordHash [])
{
  //Add the user name and the password hash to the database
  return;
}

Solution:

void CUserManager::CreateLogin(String * strUserName, String * strPassword)
{
  System::Text::ASCIIEncoding *pAscii = new System::Text::ASCIIEncoding();
 
  String * strPrependedSalt = CUserManager::GenerateRandomSalt();
  String * strAppendedSalt  = CUserManager::GenerateRandomSalt();
 
   //Prepend and apppend a random salt to the clear-text password so that making the dictionary attacks difficult.
  String * strPasswordWithSalt = String::Concat(strPrependedSalt, strPassword, strAppendedSalt);
 
  Byte bytePassword[] = pAscii->GetBytes(strPasswordWithSalt);
  SHA1CryptoServiceProvider *pSha1 = new SHA1CryptoServiceProvider();
  Byte byteHash[] = pSha1->ComputeHash(bytePassword); 
 
  //Add the 2 clear-text salts to the hash itself.
  String * strHash = String::Concat(strPrependedSalt, pAscii->GetString(byteHash), strAppendedSalt);
 
  AddLoginToDB(strUserName, pAscii->GetBytes(strHash));
 
  return;
}

Description:

           The given code snippet creates a new user. It takes care as in not storing the clear text password anywhere but instead stores the SHA1 hash of the users password in the database. However, one way hash functions are deterministic in nature. Given a string, the resultant hash produced by the one way hash algorithm is always the same. Using the hash algorithms alone can expose the application to “dictionary attacks”. Suppose a malicious user of the application gets hold of the user names and the associated password hashes (may be from the log file) the user can find out if there is any user who has the same password as his or if two users have the same password! Another twist to this is, if the one way hash algorithm is well known the attacker can pre-compute the hashes of all the well known passwords offline, and then employ a brute force attack to get access to the application

For those of you that don't know, Seattle doesn't typically get snow. Sure, it snows in the mountains and keeps us snowboarders and skiers happy, but the city is fairly mild. It actually snowed today in Seattle, Redmond, and surrounding cities, and people were panicking. Many left work early. Some went to buy tire chains. There was literally a dusting of snow on the ground because as soon as the big snowflakes hit the earth, they melted.

Bear in mind that I spent my entire life in Michigan. A 6 inch snow storm is not considered all that bad, but to Seattleites, 1 inch of snow that instantly melts causes chaos. Yes, downtown Seattle has some steep hills, but come on people -- there is almost no snow sticking to the ground!

The irony is that many left work in the early afternoon when "the blizzard" was at its worst. The weather and mass exodus of people caused the highways to get backed up early in the day. I left work during what should have been peak rush hour. The snow had stopped, there were far fewer people on the road, and I managed to get home quickly.

Since I feel like bantering some more, let me talk about snow + SUV drivers. I drove a sports car for 6 years in Michigan. Sports car + snow storm = death trap. I vowed that when I get a 4x4, I will drive at the same speed during storms that I did when I had my sports car. The only difference would be that this time I wouldn't be as worried about doing 360s on the freeway. I have since bought a SUV to accomodate my outdoor lifestyle and more treacherous driving conditions, and I love it. Every winter, I see people driving SUVs and pickup trucks whip past me and other cars during a cold, snow, icy night. "I've got 4x4. I can still go 70 MPH," they say. Ice is the great equalizer, my friends. 4x4s do a great job of helping you accelerate more quickly in slick conditions, but they do nothing to help you slow down. At the end of the day, when an automobile is whipping down the road at 70 MPH and slams on the brakes, there are 4 pieces of rubber touching the ground. Oh yeah, and lets not forget that SUVs weigh more than cars, so assuming all other variables, such as tires and ABS, are the same, the car will stop in a shorter distance.

 

Some people commented that the last bug was too easy, and it was, but buffer overruns are still common enough that I wanted to send the point home. This one is a bit more challenging.
Courtesy of Neelay Shah, Consultant, Foundstone

void
Socket_Setup(void)
{
   WORD wVersionRequested;
   WSADATA wsaData;
   wVersionRequested = MAKEWORD( 2, 2 );
   ::WSAStartup(wVersionRequested, &wsaData);

   SOCKET sTCPServer = ::socket(AF_INET, SOCK_STREAM, 0);
   struct sockaddr_in saTCPServAddr;
   saTCPServAddr.sin_family = AF_INET;
   saTCPServAddr.sin_addr.S_un.S_addr = ::htonl(INADDR_ANY);
   saTCPServAddr.sin_port = ::htons(5678);
   int len = sizeof(saTCPServAddr);

   int iFail =::bind(sTCPServer, (struct sockaddr*)&saTCPServAddr, len);
   DWORD dwErr; 
   if(0 != iFail)
   {
      dwErr = ::WSAGetLastError();
      printf("\n\t Error occured.\n");
      return;
     }

   iFail = ::listen(sTCPServer, 2);

   struct sockaddr_in saClient;
   int iClsize = sizeof(saClient);
   SOCKET sClient = ::accept(sTCPServer, (struct sockaddr*)&saClient ,&iClsize);

   char strData[1024];
   ::recv(sClient, strData, 1024, 0);

   printf("\n\nRealServer--Data from client --- %s ---", strData);
   
   ::shutdown(sTCPServer, SD_BOTH);
   
   ::WSACleanup();

   return;
}

 

Solution

Wow, we had a lot of great posts to this one!

Assuming nothing was missed, here is the solution:

void Socket_Setup(void)
{
  WORD wVersionRequested;
  WSADATA wsaData;
  wVersionRequested = MAKEWORD( 2, 2 );
  ::WSAStartup(wVersionRequested, &wsaData);
 
  SOCKET sTCPServer = ::socket(AF_INET, SOCK_STREAM, 0);
  struct sockaddr_in saTCPServAddr;
  saTCPServAddr.sin_family = AF_INET;
  saTCPServAddr.sin_addr.S_un.S_addr = ::htonl(INADDR_ANY);
  saTCPServAddr.sin_port = ::htons(5678);
  int len = sizeof(saTCPServAddr);
 
  //Approach 1
  //Enable exclusive use so that another process cannot bind to the same socket
  //BOOL bExclusiveUse = TRUE;
  //int iValLen = sizeof(BOOL);
  //::setsockopt(sTCPServer, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
  //(char*)&bExclusiveUse, iValLen);
 
  int iFail =::bind(sTCPServer, (struct sockaddr*)&saTCPServAddr, len);
  DWORD dwErr;
  if(0 != iFail)
  {
    dwErr = ::WSAGetLastError();
    printf("\n\t Error occured.\n");
    return;
  }
 
  //Approach 2:
  //Strong ACL the socket only local admin’s and local service can bind
  //Starting Win 2003 Server SP1
  PSECURITY_DESCRIPTOR psD = NULL;
  if(!ConvertStringSecurityDescriptorToSecurityDescriptor(
    "D:(A;;GA;;;LS)(A;;GA;;;BA)",
    SECURITY_DESCRIPTOR_REVISION,
    &psD, NULL))
  {
    printf("Convert Failed \n");
  }
 
  if(!SetKernelObjectSecurity(
    (HANDLE)sTCPServer,
    DACL_SECURITY_INFORMATION, psD))
  {
    printf ("SetKernelObjectSecurity failed %d",GetLastError());
  }
 
  iFail = ::listen(sTCPServer, 2);
 
  struct sockaddr_in saClient;
  int iClsize = sizeof(saClient);
  SOCKET sClient = ::accept(sTCPServer, (struct sockaddr*)&saClient ,&iClsize);
 
  char strData[1024];
  ::recv(sClient, strData, 1024, 0);
 
  printf("\n\nRealServer--Data from client --- %s ---", strData);
 
  ::shutdown(sTCPServer, SD_BOTH);
 
  ::WSACleanup();
 
  return;
}
 

Description:
Creating sockets and binding them to any interface can be tricky. This can enable a malicious attacker to write a malicious server program which binds to same port but to a specific port which results in the malicious server getting the client connections along with their data. This is referred as “socket hijacking”. One approach is to strong ACL the socket (starting Win 2003 server and above). The above example shows the socket to be strong ACLed to allow only the local administrators and the Local Service account.  Another approach is to set the “SO_EXCLUSIVEADDRUSE” socket option which disallows another process to bind to the same port on all available interfaces.

It has been a while since the last bug was up. We certainly had some great discussion around it. I will try to get more bugs up on the site on a regular basis to keep everyone on their toes at all times :-)
Courtesy of Neelay Shah, Consultant (Foundstone)

#define MAX_STR_LEN     255

            :

            :

            :

      char strUserInput[MAX_STR_LEN+1];

      scanf(“%s”, &strUserInput);

            :

            :

            :

 

Solution       

 

#define MAX_STR_LEN     255

            :

            :

            :

      char strUserInput[MAX_STRING_LEN+1];

      fgets(strUserInput,MAX_STR_LEN+1,stdin);

            :

            :

            :

                       

Everyone figured out that in the bad way of programming the ‘scanf()’ function is used to read the user input from the console. Now, scanf() does not check for the length of the input string and if a malicious user enters a string longer than the maximum length it will lead to overwriting the memory following ‘strUserInput’.  There is more than one good way to fix this. In our solution, we use the ‘fgets()’ function to read the user input and using which you can control the maximum length of the user input. This is a case of buffer overflow and perhaps easy but I added it because I think it is still very prevalent.

 

It's been a little while since we've had a new bug up. We had some good feedback on the last one. Here is a shorter one:
Courtesy of Shanit Gupta, Consultant (Foundstone)

try
     {
         ElevatePrivilege();
         ReadSecretFile();
         LowerPrivilege();
     }
     catch(FileException fe)
     {
         ReportException();
     }

 

Suggested Implementation: 

  try
     {
         ElevatePrivilege();
         ReadSecretFile();
     }
     catch(FileException fe)
     {
         ReportFileException();
     }
    catch(Exception e)
    {
        ReportException();
    }
   finally()
   {
       LowerPrivilege();
       AllDone();
   }
   return;

 

Description: In the error prone code there is no way for the application to lower privileges if there is any exception in “ReadSecretFile” function. But in the suggested code, the finally block will execute irrespective of whether exception occurs or not and hence the privileges will be lowered once the secret file is read.

If you haven't taken a look at the solution to the last bug, please do so. There were 4 bugs in that short chink of code -- all of which are found in Visual Studio 2005! One is issued as a compiler warning and the other 3 are found by PREfast.

Here is the next "Spot the Bug." This one is certainly more lengthy and complex than the last. There were some typos in the original post which were fixed.
Courtesy of Shanit Gupta, Consultant (Foundstone)

private void TransferFunds ()
{
...
 if (fromaccount.balance  > amount) {
  fromaccount.balance -= amount;
  toaccount.balance += amount; 
 }
...
}

private void RetrieveAccountInfo (int payer, int payee)
{
...
...
...
fromAccount = RetrieveFundInfo(myConnection, payer);
toAccount = RetrieveFundInfo(myConnection, payee);
TransferFunds();
CommitChanges (fromAccount.number, fromAccount.balance);
CommitChanges (toAccount.number, toAccount.balance);
}

private int RetrieveFundInfo (SqlConnection myConnection, int accountNumber)
{
 string mySelectQuery = "SELECT balance FROM Customers where accountnumber = " + “’” + accountNumber + “’”;
 SqlCommand myCommand = new SqlCommand(mySelectQuery,myConnection);
 SqlDataReader myReader = myCommand.ExecuteReader();
 return myReader.GetInt32(0);
}

private void CommitChanges (int accountNumber, int balance);
{
 string mySelectQuery = "UPDATE Customers Set Balance = “ + “’” + balance + “’”  + “where AccountNumber = " + “’” + accountNumber + “’”;
 SqlCommand myCommand = new SqlCommand(mySelectQuery,myConnection);
 myCommand.ExecuteNonQuery();
}

Suggested Implementation:

private void TransferFunds ()

{

..

      if (fromaccount.balance  > amount) {

            fromaccount.balance -= amount;

            toaccount.balance += amount; 

      }

}

 

private void RetrieveAccountInfo (int payer, int payee)

{

...

...

...

SqlTransaction myTrans;

myTrans = myConnection.BeginTransaction("SampleTransaction");

  try

    {

 

fromAccount = RetrieveFundInfo(myConnection, payer);

toAccount = RetrieveFundInfo(myConnection, payee);

TransferFunds();

CommitChanges (fromAccount.number, fromAccount.balance);

      CommitChanges (toAccount.number, toAccount.balance);

      myTrans.Commit();

 

}

catch(Exception e)

    {

      try

      {

        myTrans.Rollback("SampleTransaction");

      }

      catch (SqlException ex)

      {

        if (myTrans.Connection != null)

        {

          Console.WriteLine("An exception of type " + ex.GetType() +

                            " was encountered while attempting to roll back the transaction.");

        }

      }

   

      Console.WriteLine("An exception of type " + e.GetType() +

                        " was encountered while inserting the data.");

      Console.WriteLine("Neither record was written to database.");

    }

    finally

    {

      myConnection.Close();

    }

}

 

 

private int RetrieveFundInfo (SqlConnection myConnection, int accountNumber)

{

      string mySelectQuery = "SELECT balance FROM Customers where accountnumber = " + “’” + accountNumber + “’”;

      SqlCommand myCommand = new SqlCommand(mySelectQuery,myConnection);

      SqlDataReader myReader = myCommand.ExecuteReader();

      Return myReader.GetInt32(0);

}

 

private void CommitChanges (int accountNumber, int balance);

{

        string mySelectQuery = "UPDATE Customers Set Balance = “ + “’” + balance + “’”  + “where AccountNumber = " + “’” + accountNumber + “’”;
        SqlCommand myCommand = new SqlCommand(mySelectQuery,myConnection);
        myCommand.ExecuteNonQuery();

}

 

 

Description: Race conditions happen in a multi-threaded (process) environment where there is a window of vulnerability for state corruption and compromise. The condition occurs owing to the fact that the information read is different from information processed, which leaves the state inconsistent or incorrect. One way to avoid this possibility is by gaining exclusive locks on the rows that are being read and updated. The suggested code uses “SqlTransaction myTrans” which locks the rows for isolation property of the transaction.  

I created this bug a couple of weeks ago for a conference I spoke at to illustrate how so few lines of code could be so buggy. Where's the bug here?

char dest[50], src[100];
int x, y;

if (x=1)
{
   strcpy(dest,src); 
   dest[50] = '\0';
}

return y;


Solution:
Alright, so I admit it -- this chunk of code is a bit nonsensical. But I will say that people do make these mistakes all the time, but probably not all at the same time. :)

This code has 4 security defects:
1. The if statement with "=" instead of "==". Many of you would argue that this is of a quality issue than a security issue, and you'd be right. But security is certainly a subset of quality, and this can cause the code to do things that it shouldn't do.
2. In strcpy, src is larger than dest, causing a buffer overrun.
3. Arrays start at 0, not 1! Therefore, we are writing past the last allocated spot on the array.
4. The variable y is not initialized.

Now that you've heard the bad news about all that's wrong with this code, it's time for some good news. I bet you didn't know that Visual Studio 2005 catches all of these problems! Strcpy is caught by the compiler and noted as a warning. We've created safe versions of these libraries in Visual Studio 2005 called Safe CRT libraries. PREfast catches the other 3 bugs -- even the "=" error. With these tools and proper education, we hope to get developers all over the world wrting more secure code!

 

I think the last bug stumped a few people. Can you find the security vulnerability in this one?
Courtesy of Neelay Shah, Consultant, Foundstone

#define STD_HASH_LEN 11
#define MAX_HASH_LEN 31                 

char * strPassHash = (char*)malloc(sizeof(char)*STD_HASH_LEN);

            :

            :     //Create the hash

            :

// Now suppose you need to recreate the hash which would be of length = MAX_HASH_LEN

      strPassHash = (char*)realloc(strPassHash, MAX_HASH_LEN);

            :

            :    

            :

Solution

      #define STD_HASH_LEN   11

      #define MAX_HASH_LEN 31

                 

      char * strPassHash = (char*)malloc(sizeof(char)*STD_HASH_LEN);

                  :

                  :     //Create the hash

                  :

      // Now suppose you need to recreate the hash which would be of length = //MAX_HASH_LEN

            char * strNewPassHash = (char*)realloc(strPassHash, MAX_HASH_LEN);

      if(NULL == strNewPassHash)

      {

            // Not enough free memory…free the old hash and return an error.

            free(StrPassHash);

            printf(“Error…Not enough free memory”);

            return;

      }

 

      strPassHash = strNewPassHash;

                  :

                  :    

                  :

 

Description:

In the bad way, if realloc() fails for want of memory it returns a NULL and the pointer to the old memory is lost. Now in normal cases this memory leak may not be a security threat but in case the memory is shared and its contents are sensitive like a password hash for example it may lead to a security threat. The good way of programming gets around this by using an extra pointer.

 

Alright all, here is the next bug. This one is courtesy of Mike Howard.

__declspec(noinline) void* AllocBlocks(size_t cBlocks) {

      // allocating no blocks is an error
      if (cBlocks == 0)
            return NULL; 

      // Allocate enough memory
      // Upcast the result to a 64-bit integer
      // and then check result against 32-bit UINT_MAX 
      // this makes sure there's no integer overflow

      ULONGLONG alloc = cBlocks * 16;
      
return (alloc < UINT_MAX)

      ? malloc(cBlocks * 16)
      
: NULL;

}

Solution

We did not have a lot of activity around this bug. This one was tough.

The problem is that the int overflow check doesn’t work…

__declspec(noinline) void* AllocBlocks(size_t cBlocks) {

      // allocating no blocks is an error
      if (cBlocks == 0)
           
return NULL;

      // Allocate enough memory
      // Upcast the result to a 64-bit integer
      // and then check result against 32-bit UINT_MAX 
      // this makes sure there's no integer overflow

      ULONGLONG alloc = cBlocks * 16;
      
return (alloc < UINT_MAX)

      ? malloc(cBlocks * 16)
      : NULL;

}

These lines are all wrong

      ULONGLONG alloc = cBlocks * 16;
      
return (alloc < UINT_MAX)

cBlocks * 16 is a 32-bit multiply, so the multiply always yields a 32-bit value. Then that result is assigned to a ULONGLONG, but the result is always less than UINT_MAX, because the calculation may have already overflowed. Fix is:

      ULONGLONG alloc = (ULONGLONG)cBlocks * 16;

If you have a few minutes, check this out. It is hilarious!!!

www.escapeYESTERWORLD.com 

 

The first bug was just a warm-up and people were asking for a more difficult bug. What's wrong with this chunk of code, and better yet, how do you fix it?
Courtesy of Shanit Gupta, Consultant, Foundstone

private HttpCookie SessionIdentifier ()
{
   HttpCookie myCookie = new HttpCookie("SessionId");
   Random objRand = new Random (DateTime.Now.Millisecond); 
   myCookie.Value(“SessionId”) = random(objRand.Next()) ;
   return myCookie;
}

Solution:

There was a good chat around this one. Here's one good way to implement it:

byte[] randomCharacters = new Byte[64];

//RNGCryptoServiceProvider is an implementation of a random number generator.

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

private HttpCookie SessionIdentifier (RNGCryptoServiceProvider cryptoRNG)
{
 HttpCookie myCookie = new HttpCookie("SessionId")
 crptoRNG.GetBytes(randomCharacters); // The array is now filled with cryptographically strong random bytes.
 myCookie.Value(“SessionId”) = randomCharacters.toString();  return myCookie;
}

Description: Many developers think that a function/class such as “random” or likes is capable of generating random numbers that are not predictable. Some of them go on to believe that that time in seconds or even milliseconds can server as a good random number or at least a good seed for the random number. Further the deterministic nature of computers makes it extremely simple to calculate the seed of PRNG and the following pseudo random numbers provided a good sample of inputs are available. We recommend the use of cryptographically secure PRNGs which do not generate the same set of random numbers even when seeded with the same string. More information can be found here.

More Posts Next page »
 
Page view tracker