Secure coding

SDL for C/C++ code in Visual Studio 2013: overview

Aleksander Czarnowski
July 29, 2016 by
Aleksander Czarnowski

Introduction

Over the years Microsoft had invested heavily into security, successfully changing market perception of own products from one of most insecure into reliable and secure software. Their Secure Development Lifecycle (SDL) process has been the main factor behind this crucial change. Microsoft deployed secure-by-default stance and introduced a number of best practices for developers. But their SDL program didn't stop there; they actually implemented those best practices internally. To aid Windows-based developers in secure coding further, some parts of SDL process has been reflected in various Visual Studio release for some time. In this article I describe some of the SDL features implemented in Visual Studio 2013 and Windows operating platform. Since at the time of writing Visual Studio 2015 has been available already one may argue why to deal with the older version. The rationale behind this decision is a fact that a lot of developers are still using 2013 version. Furthermore, most of this article applies to 2015 release as well, and some VS 2015 exclusive features are also being discussed along the way.

Compliance

Before we delve further into technical details, it is important to note that SDL best practices are becoming a common compliance requirement. Take for example Requirement 6: Develop and maintain secure systems and applications from
PCI-DSS. Obviously, SDL can help you meet this requirement. While there are many other standards or sector regulations demanding one or other form of SDL, they are usually technology and methodology agnostic. This is important observation since Visual Studio – for obvious reasons – supports Microsoft SDL process [1] exclusively. If you follow OWASP publications, for example, you are on your own implementing proper rule sets and other activities within Visual Studio boundaries. It doesn't mean you should not follow non-Microsoft best practices – on the contrary – but implementing those may require additional work.

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

Visual Studio 2013 security features

In the following sections, I will describe different safeguards provided by Visual Studio 2013 as part of SDL process. The previous version of VS already had some Secure Development Lifecycle related functionality included. A good example can be stack protection mechanism based on Canary (called cookie in MS nomenclature) concept which can be enabled by /GS option (hence it is sometimes referred as /GS protection). However, VS 2013 version is the first release that had so many security related features integrated. For a quick peek of the enhancement, please visit the following link: https://msdn.microsoft.com/en-us/library/k3a3hzw7(v=vs.120).aspx

Stack protection (/GS)

Due to the high popularity of buffer overflows in previous decades, a quest for eliminating this class of vulnerability has begun. Some tried to teach developers how to develop secure code, others provided security enhanced replacement functions for those usually being cause of buffer overflow (strcpy comes to mind immediately) or tried to modify a compiler in such way that it would detect buffer overflow and stop the potential attack. Microsoft VS 2013 provides all of above methods.

The compiler modification is usually based on placing a cookie also called a canary on the stack. If classical buffer overflow is triggered, the cookie gets overwritten on the stack. Before returning to a caller, function epilog checks the integrity of the cookie on the stack. If the value is different than a stack overflow has been detected. What happens further is based on the particular compiler (including its settings) and operating platform. The same concept is used in Visual Studio and hidden under – a bit misleading name – Enable Security Check.

The careful reader may immediately see a weakness in proposed protection scheme. If the cookie has a constant value, it may be possible to overwrite a stack data in such way that its value will not be changed. In the case of VS compiler, this problem has been solved by adding a cookie initialization code to startup library. When the process starts it gathers some information about the operating system and execution information: those inputs are used to calculate cookie value. Hence it is guaranteed to be random. It will differ even if you run the same process twice on the same machine.

When /GS option is enabled, you may further strengthen the stack protection scheme by using #pragma strict_gs_check [2] option. The rationale for this feature is to enable cookie based stack protection in all functions. By default, VS does not insert stack checking code into every function, so this #pragma statement allows to override this behavior. It is advised to be used in code that exposes critical services or has large attack surface.

Keep in mind that safeguards described above protect the return address on the stack. However, it does not protect against all types of buffer overruns even if we limit the attack scenario to the stack frame. For example, if you have a vtable pointer on the stack, overwriting buffer may allow to change it and regain control before the /GS check gets executed. A technique called VTable hijacking is one example of such attack.

SDL checks (/sdl)

Attempt to compile source code with insecure CRT functions like gets() or strcpy() while SDL checks are enabled results in the following error:

Code Analysis

Visual Studio also provides static source code analysis option that can be used to detect certain security related issues.

Running code analysis on insecure code – like our gets() example – results in highlighting possibly vulnerable code as shown below:

Furthermore Code Analysis window shows explanation for every possibly insecure code:

Unfortunately, the default ruleset provided with Visual Studio is quite limited. It does not detect strcpy() calls for example but one can say it is fine since compiler during compilation will either report a warning about it or throw an error when /sdl switch is enabled. However, since the ruleset can mark gets() calls as dangerous why it fails to do the same with other string related functions with long buffer overflow history is beyond me.

Another limitation is the simplicity of the ruleset regarding security. For example Microsoft own security challenge [3] can be compiled and source code can be scanned without any warning. One could argue that this example is from 2014 so rulesets from 2013 may fail to detect the problem however Update 5 used for this paper is from 2015.

Overall – from security standpoint – VS Code Analysis options sounds great as static analysis can provide very valuable information. Unfortunately due to a small number of rules and intelligence of analysis engine its usage is limited and therefore cannot be treated as a replacement for dedicated static source code analysis tools. Nevertheless, this feature can and should be used by the developer to enforce some SDL best practices at the source code level. To be honest many other IDEs, even for different languages than C/C++, also provide (if any) only very basic rules for detecting quality and security issues.

Don't forget the old best practices

Just because some best practices are old or haven't been updated for some time doesn't mean you shouldn't use them. Below are some SDL best practices for C/C++ coding that aren't limited to VS 2013 or 2015 only but are available for a number of compilers.

Strsafe.h

Strsafe.h [4] library is still available and if for some reason you have to use functions like strcpy or strcat you should consider using library replacements. To use it, include it in your stdafx.h like any other library:

#include <Strsafe.h>

Below is a table for character count based functions:

The next table deals with byte count based functions:

Be aware that those functions aren't part of VS 2015 off-line documentation.

Safe *_s replacements in CRT

Visual Studio within stdio.h provides a set of secure replacements for insecure functions likes gets or strcpy. For example instead of code:

char buf[16];

strcpy(buf, msg);

You can write:

char buf[16];

strcpy_s(buf, 10, msg);

If the safe replacement for a particular CRT function is available, the compiler will generate a warning unless /sdl checks are enabled. In such case, an error instead of warning is being generated. Please notice that so-called "safe" replacement functions can still be misused.

VS CRT security enhancements provide Secure Template Overload [5]
feature when _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES is set to 1. This enables automatic replacement of CRT insecure function into their secure counterpart without warning generation.

Additional security features in CRT

Secure replacement for some of CRT functions is not the only enhancement available to programmers. Another nice feature is parameter validation. Parameter validation checks for NULL pointers, invalid enumeration constants and validates integer range. When the invalid parameter is detected invalid handler routine is being called. This handler called Invalid Parameter Handler Routine by default executes Watson crash reporting. However, this behavior can be changed by setting own routine using _set_invalid_parameter_handler() function.

Besides validation CRT functions also enforce proper data format. For example, secure CRT functions ensure that strings are properly null terminated which is not always the case with insecure versions. Furthermore, format string syntax is being checked resulting in detecting incorrect type fields. This helps to prevent some format string vulnerabilities.

When the buffer is being passed, secure CRT functions requires the size of the buffer to be passed as well. The size of a buffer is being validated before the actual operation takes place on the buffer to assure that the size is correct. This helps to prevent some buffer overruns bugs.

When using secure CRT functions enforcement of Windows secure file I/O API is the default. The same rule applies to process APIs which also results in an inability to specify ACLs where insecure functions do not provide such option. Using own ACLs instead of relying on default ones helps tighten the security of the application.

SafeInt class library

SafeInt [6] is a C++ class library providing safeguards against integer overflow that can occur during mathematical operations. For complete class reference, please consult MSDN [7].

To use SafeInt class just include SafeInt.hpp in your project. It defines SAFEINT_HPP symbol so you can check if it is already included.

The library defines SafeInt and SafeIntException classes.

SafeInt can be used with other compilers besides ones provided by Visual Studio package. For more details including SafeInt source code download, please consult SafeInt homepage [7].

During compilation, SafeInt tries to detect used the compiler. Check if your compiler is supported if you plan to use SafeInt outside of VS environment.

Checked Iterators

This is another overflow prevention measure present in VS. Checked Iterators [8] ensure that bounds of your container are not overwritten. If _SECURE_SCL is defined as one all unsafe use of iterators causes program termination. Checked Iterators can be using with both debug and release builds.

Checked Iterator will call already described Invalid Parameter Handler Routine if code tries to move past the boundary of a container.

Setting _SECURE_SCL to 0 results in not checking all standards iterator for boundaries.

Run-time checks

VS provides a set of run-time checks that can be enabled either through #pragma [9] statement or /RTC[n] [10] command line option. Before using run-time checks, you need to remember that this feature should be disabled in release builds. Furthermore, run-time checks cannot be used in conjunction with compiler optimization options. Hence using this feature makes only sense in debug builds.

Run-time checks provides useful set of features:

  • Runtime stack frame checking
  • Detection of uninitialized variables
  • Data loss during when assigning a value to a smaller data type

Platform provided safeguards

SDL process is not only based on Visual Studio compilers specific safeguards or external C/C++ libraries but also embraces Windows platform security related functionality. Below is a summary of selected platform safeguards.

Privileges

Privilege is a mechanism of Windows, not Visual Studio. Never the less due to their crucial role in Windows security model no SDL related paper can be treated as complete without mentioning it. Below are some most basic, yet crucial tips:

  • Do not run your code with elevated privileges (like Administrator Privileges). Use CheckPrivilege to determine which privileges are enabled in a token.
  • Whenever possible use Restricted Tokens. You can create a Restricted Token using CreateRestrictedToken.
  • Use impersonation if your code starts running with high privileges, however, keep in mind that this does not work like privilege separation on some BSD systems for example. The reason is that if a process running under impersonated account calls RevertToSelf, it returns to original privileges. Even if the process is not calling it directly, remember that attacker may create a remote thread and inject a call to RevertToSelf for example.

Guard Pages

Memory related functions likes VirtualProtect and VirtualAlloc accept PAGE_GUARD (0x100) constant [11]. When running process tries to access an address within guard page range, the system raises a STATUS_GUARD_PAGE_VIOLATION (0x80000001) exception and clears PAGE_GUARD attribute for this memory region. Guard Pages are an interesting mechanism that can be used inside application to control large data structures that tend to grow (rapidly). Such structures are prone to memory corruption issues, hence adding an additional layer of security makes a lot of sense. The possible drawback is that this mechanism is not enforced in any way by the VS compiler itself. Instead, it is the job of the developer to deploy it which means writing additional code – something that most people try to avoid at any cost.

A side note: Guard Pages – as a safeguard - are also available on other than Windows operating platforms. Consult your operating system documentation for implementation details.

User address space layout randomization (ASLR) aware executable

VS enables you to take advantage of address space randomization layout (ASRL) feature introduced in Windows Vista. It can be controlled through /DYNAMICBASE [12] option. In the case of Visual Studio 2013, this option is enabled by default so no additional action is required to generate an executable that can dynamically rebase itself during loading. This feature can also be controlled through project option under Randomized Base Address property.

Like with Guard Pages, ASLR is available on number non-Windows platforms as well. Consult your operating system documentation for implementation details.

Additional tools: dynamic testing and fuzzing

Not every SDL activity can be or must be performed within Visual Studio environment. For example, SDL Verification phase defines among others following two practices:

  • #11: Perform Dynamic Analysis
  • #12: Perform Fuzz Testing

Microsoft is kind enough to provide additional, free tools aiding you in undertaking those activities:

#11: Perform Dynamic Analysis #12: Perform Fuzz Testing

  • AppVerifier
  • BinScope

  • MiniFuzz File Fuzzer
  • SDL Regex Fuzzer

One tool missing from Microsoft list is their own WinDbg package [13]. WinDbg - thanks to its scripting capabilities and plugin architecture - makes a good starting point for instrumenting binaries. It can not only be used to monitor target application during fuzzing or testing but can be turned into fuzzer as well. In many cases as far as binary instrumentation goes you can gain the same functionality with IDA [14] but the choice is yours.

Selected Visual Studio 2015 C++ security features

While this paper deals with Visual Studio 2013, it would not be fair not to cover some new functionality found in 2015 version (most current at the time of writing). For a more comprehensive list take a look at https://msdn.microsoft.com/en-us/library/k3a3hzw7(v=vs.140).apex

No more gets()

Visual Studio 2015 CRT does not support insecure functions like gets() for example. Instead only secure replacements – for example in the case of gets(), secure replacement is called gets_s() - are being supported. This is minor change especially comparing to CFG described in next section.

Control Flow Guard

Control Flow Guard (CFG) also referred sometimes as /guard option [15] is new exploit mitigation technique available to the developer.

Control Flow Guard option can be enabled either from command line using /guard:cf or from IDE by setting Project -> Properties -> Configuration Properties -> C/C++ -> Code Generation -> Control Flow Guard to Yes. Please note that this safeguard is by default turned off, so you need to turn it explicitly on.

CFG tries to detect memory corruption based exploitation techniques by controlling execution address and indirect calls. Please note that to use this safeguard the underlying platform must be "CFG-aware" which means x86 and x64 Windows 10 and Windows 8.1 with installed November 2014 update rollup (KB3000850).

Last but not least it is possible to disable CFG explicitly using /guard: cf- option.

CERT Secure Coding Standard

When discussing C++ secure coding best practices one should not ignore CERT guidelines for C++ [16] and Secure Coding in C++ [17]. While it is definitively worth reading if you are targeting Windows platform do not expect many details regarding it – both publications are more Unix-centric. [17] also targets mainly gcc compiler. Keep in mind that safeguards operating on the native code generation level are implemented differently per-compiler basis. Consequently, some of PE file format security related features are not discussed in those publications.

Summary

In Visual Studio 2015 C++ compiler isn't installed by default which tells a lot how Microsoft sees the future of particular languages. At the same time, VS supports for example open source Python language. This also clearly demonstrates the direction Microsoft wants to go and thinks where developers want to go, especially considering the cloud shift. Nevertheless, there are sectors still using C/C++ code for the mission-critical application, and C/C++ isn't going anywhere anytime soon. A number of core applications in financial and payments sectors have been written in C/C++, some of them using Microsoft compilers. Usually, vendors and their customers aren't willing for complete rewrite just because some newer languages becomes popular among developers. No one is willing to touch accounting algorithms that are considered bug free and tested. This means that C/C++ security issues will stay with us for some time, and we still have to deal with them. On the other hand, many secure coding concepts from C/C++ can be easily replicated to other languages. Even if you are developing your code for one of dynamic scripting languages based on byte-code you still have to remember that underlying VM or interpreter infrastructure is probably implemented in C/C++. This means that understanding C/C++ related security issues can help you not only to deal with vulnerabilities in those layers that can indirectly affect your code but also actually to avoid some of them in the first place.

Microsoft also provides some best practices for managed code / manage compilers. Those were not discussed in this article as it is concentrated on C/C++ exclusively. Some C++ related issues have not been discussed either due to space constraints. Doing so would require a book instead of the article.

PE+ file format and its security features have been barely discussed. Understanding executable format is crucial for understanding implications and limitations of different safeguards introduced during code generation and linking phases. Operating system security features also have an impact on executable file format and the way it is loaded into memory. A good example may be SDL requirement for no shared sections in release binaries.

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

As a baseline for this text Microsoft Visual Studio Community Edition 2013, version 12.0.40629.00 Update 5 has been used.

Bibliography

  1. Microsoft Secure Development Lifecycle https://www.microsoft.com/en-us/sdl/
  2. strict_gs_check https://msdn.microsoft.com/en-us/library/bb507721(v=vs.120).aspx
  3. Microsoft Security Challenge https://blogs.msdn.microsoft.com/vcblog/2014/02/04/challenge-vulnerable-code/
  4. About Strsafe.h https://msdn.microsoft.com/en-us/library/windows/desktop/ms647466(v=vs.85).aspx
  5. Secure Template Overloads https://msdn.microsoft.com/en-us/library/ms175759(v=vs.120).aspx
  6. SafeInt project code page: https://safeint.codeplex.com/
  7. SafeInt MSDN documentation: https://msdn.microsoft.com/en-us/library/dd570023(v=vs.120).aspx
  8. Checked Iterators https://msdn.microsoft.com/en-us/library/aa985965(v=vs.120).aspx
  9. runtime_checks https://msdn.microsoft.com/en-us/library/6kasb93x(v=vs.120).aspx
  10. /RTC (Run-Time Error Checks) https://msdn.microsoft.com/en-us/library/8wtf2dfz(v=vs.120).aspx
  11. Memory protection constants https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx
  12. /DYNAMICBASE https://msdn.microsoft.com/en-us/library/bb384887(v=vs.120).aspx
  13. Windows Debugging Tools https://msdn.microsoft.com/en-us/library/windows/hardware/ff551063(v=vs.85).aspx
  14. IDA https://www.hex-rays.com
  15. /guard option https://msdn.microsoft.com/en-us/library/dn919635.aspx
  16. SEI CERT C++ Coding Standard https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=637
  17. Secure Coding in C and C++ By Robert C. Seacord Published Apr 2, 2013, by Addison-Wesley Professional
Aleksander Czarnowski
Aleksander Czarnowski

Professionally working in Information Security area since 1990. In 1997 joined AVET Information and Network Security as founding member and CEO. Working on different cloud security related projects including Cloud Service Level Agreement Standardisation Guidelines published by European Commission and ISO 270xx standards as a member of Polish Committee for Standardization. He is an author and co-author of several publication regarding information security. Since September 2015 he is a member of ECSA Advisory Board.