In the world of software development, we are up against new cyber security threats each day, and the risks and consequences of un-secure software is too great to be unaware of.  

Join presenter, Mary Kelly, to address the key security sins related to C++ applications, how they happen and how to resolve them.

Click on the link to the webinar replay below : 

C++ Software Security Sins

Buffer Overruns

The following is a snippet of a common “old school” buffer overrun:

void bufFunc (char *str)
{
     char buffer[10];
     strcpy (buffer, str);
}

int  _tmain(int argc, _TCHAR* argv[])
{
     char string [16];
     printf (“Input some data : ”);
     gets(string);
     bufFunc (string);
     printf (“Additional data here : ”);
}

When dealing with buffer overflows, here are a few common fixes:

  • Comment your code
  • Use the C++ Standard Library and STL
  • Use libraries that check bounds
  • For buffer overruns or overflows, there is a popular method called fuzz testing. Fuzz Testing or fuzzing as it’s known in many circles is a testing technique by which you test your inputs with generated semi-randomized values helping with stability and performance of your applications. I mentioned one fuzzing library that I use called libFuzzer.

Format String Problems

void vulnerable ()
{
     char buffer[60];
     if (fgets (buffer, sizeof (buffer), stdin) == NULL)
     return;
     printf(buffer);
}
void notVulnerable ()
{
     char buffer[60];
     if (fgets (buffer, sizeof (buffer), stdin) == NULL)
     return;
     printf (“%s”, buffer);
}

Recommended fixes:

  • Don’t pass user input directly as the format string to formatting functions
  • Use fixed format strings, or format strings from a trusted source
  • Keep an eye on compiler warnings and errors
  • When necessary to use format strings, use : printf("%s", user_input)
  • Even better, use don’t use the printf family of functions if you can avoid it, use stream operations

Integer Overflows

Integer overflow occurs when the result of an operation is larger than the allowed max max value for the data type of an operation and can cause crashes, logic errors, escalation of privileges, and execution of arbitrary code.

Some easy fixes you can do:

  • Study and understand your code.  Do a little bit of math!
  • Check all calculations used to determine that your memory allocations and array indexes can’t overflow.
  • Use unsigned variables for array offsets and sizes for memory allocation
  • Pay attention to your compiler warnings
  • Check for truncation and sign issues when working with size_t

Array new and delete

When you write new in your applications, you are creating unmanaged objects, and you are then required to call delete later on if you don’t want to risk leaks. So don’t use new and delete at all, as this is considered a C++ bad practices. Better yet, working in modern C++ allows you to use smart pointers and Standard library container classes that make it easier to match every new with exactly one delete.

Poor copy constructor and assignment declarations

In C++, a copy constructor is called when a new variable will be created from an object. If you don’t create a copy constructor, then your compiler generates a copy constructor. This sounds great! But if you don’t properly set up your constructor, then the errors replicate.

class PrtHolder
{
public:
     PtrHolder(void* p) : m_ptr(p)
     {
     }
     ~PtrHolder()
     {
            delete m_ptr;
     }
private:
     void* m_ptr;
};

When your class controls resources, you should declare a private copy constructor and assignment operator with no implementation, this way if a class external to the class with your private declaration attempts to invoke one of these, then you will get a compiler error about invoking a private method. Even if you accidentally call one internally, then you will get a link error.

Pointer initialization

Foo* pFoo;
if (GetFooPtr ( &pFoo ) )
{
     // some code
}
// If pFoo is uninitialized, this is exploitable
pFoo -> Release();

There are a few methods to use when wanting to avoid pointer problems. Use these steps in C++:

  • Initialize pointers when you declare them - Kind of a no brainer, but a great way to make your application a little easier to debug instead of worrying about some previously used pointer value
  • Zero pointers out after use
  • To avoid memory leaks, allocate memory from the heap and return it at the same abstraction level.
  • Return blocks to the heap while your pointers are still in scope
  • Make sure that the types of pointers match

Lack of STL knowledge

Know the standards to C++. There is an awesome group of people out there who make up rules regarding the evolution of the C++ language. Since C++11, there has been an uptick in features that help to avoid many pitfalls surrounding security of your C++ code.  My recommendation for learning more about the C++ STL or the C++ Standard Library is to check out www.cplusplus.com or cppreference.com.

Useful Resources

I usually like to recommend a few books or resources in my webinars and this one is no different. For learning about Software Security or ways to solve these “sins” with an emphasis on C++ applications, check out the following:

Writing Secure Code, Second Edition by Michael Howard and David LeBlanc

24 Deadly Software Security Sins: Programming Flaws and How to Fix Them by Michael Howard, David LeBlanc, John Viega

Software Security: Building Security In by Gary McGraw

Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) by Scott Meyers

STL Tutorial and Reference Guide by David Musser