Channels ▼


Secure Coding in C++/CLI

Source Code Accompanies This Article. Download It Now.

Enabling the /GS option does not make programs completely immune to buffer overflow vulnerabilities. Buffer overflows in the stack can still crash the program, and the possibility still exists for attackers to exploit a stack-based overflow to execute arbitrary code even with the /GS flag enabled. More importantly, the /GS flag cannot detect buffer overflows in either the heap or data segments.

To illustrate, Listing Two presents the previous example program rewritten to use the Win32 GUI. This program provides a menu bar with some simple options, including a File menu with two menu options: "Login" and "Exit." The Login command prompts the user for a password using a dialog window. Once the password is entered, the user presses the "OK" button and the password is checked against the recorded password.

 1. #include "stdafx.h"
 2. #include "TestItDan.h"
 3. #include <stdlib.h>
 4. #include <stdio.h>
 5. #include <windows.h>
 6. #define MAX_LOADSTRING 100
 7. struct user {
 8.     wchar_t *name;
 9.     size_t len;
10.     int uid;
11. };
13. HINSTANCE hInst;
15. TCHAR szWindowClass[MAX_LOADSTRING];
16. TCHAR lpszUserName[16] = L"guest";
17. TCHAR lpszPassword[16] = L"0123456789abcde";
18. struct user *userP = (struct user *)0xcdcdcdcdcdcdcdcd;
19. size_t userNameLen = 16;
20. size_t userPasswordLen = 0xffffffff;
25. int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow) {
26.     UNREFERENCED_PARAMETER(hPrevInstance);
28.     MSG msg;
29.     HACCEL hAccelTable;
30.     LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
31.     LoadString(hInstance, IDC_TESTITDAN, szWindowClass,                         MAX_LOADSTRING);
32.     MyRegisterClass(hInstance);
33. userP = (struct user *)malloc(sizeof(user));
34. if (!InitInstance (hInstance, nCmdShow)) {
35.     return FALSE;
36. }
37. hAccelTable =
          LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTITDAN));
38. while (GetMessage(&msg, NULL, 0, 0)) {
39.     if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
40.         TranslateMessage(&msg);
41.         DispatchMessage(&msg);
42.     }
43. }
44. return (int) msg.wParam;
45. }

109. INT_PTR CALLBACK GetPassword(HWND hDlg, UINT message, 
                             WPARAM wParam, LPARAM lParam) {
110.    TCHAR lpszGuestPassword[16] = L"NCC-1701";
112.    switch (message) {
113.      case WM_INITDIALOG:
114.        return (INT_PTR)TRUE;
115.      case WM_COMMAND:
116.        if (LOWORD(wParam) == IDOK) {
117.          EndDialog(hDlg, LOWORD(wParam));
118.          SendDlgItemMessage(hDlg, 
119.            IDC_EDIT1, 
120.            EM_GETLINE, 
121.            (WPARAM) 0,       // line 0 
122.            (LPARAM) lpszPassword
123.          );
124.        userP->len = userNameLen;
125.        if (wcscmp(lpszPassword, lpszGuestPassword) == 0) {
126.          return true;
127.        }
128.        else {
129.          MessageBox(hDlg, 
130.               (LPCWSTR)L"Invalid Password", 
131.               (LPCWSTR)L"Login Failed", 
132.               MB_OK
133.          ); 
134.        }
135.        return (INT_PTR)TRUE;
136.        }
137.        break;
138.      }
139.      return (INT_PTR)FALSE;
140.  }
Listing Two

The program is compiled and tested in the same environment except that the Unicode character set is specified and the buffer security check option (/GS) is enabled. We continue to use managed extensions (that is, Common Language Runtime support). The program builds cleanly without error.

This is a relatively simple program, although it is somewhat longer because of the code required to support the Windows GUI. There are several variables of interest from lines 17-20. The lpszPassword variable is an initialized static variable consisting of 16 wide characters (32 bytes). Following this variable is the userP pointer and two unsigned integers: userNameLen and userPasswordLen. After userP is initialized on line 33, these variables have the following addresses:

&lpszPassword = 0x0040911C
&userP =   0x0040913C
&userNameLen =  0x00409140
&userPasswordLen =   0x00409144

userP is 0x00554D30. userNameLen is 0x00000010. userPasswordLen is 0xffffffff. If we examine memory starting at the address of lpszPassword, we can clearly see the initial values for these variables (Example 3).

0040911C   <font color="#00FF00">30 00 31 00 32 00 33 00 34 00 35 00 36 00 37 00</font>
0040912C   <font color="#00FF00">38 00 39 00 61 00 62 00 63 00 64 00 65 00 00 00</font>
0040913C   <font color="#663300">30 4d 55 00</font> <font color="#FF0033">10 00 00 00</font> <font color="#0000CC">ff ff ff ff</font> 8a 00 07 02
0040914C   c6 00 07 02 02 01 07 02 00 00 00 00 01 00 00 00

Example 3: Memory starting at the address of lpszPassword.

The vulnerability in this program is in the call to SendDlgItemMessage on lines 118-123. The EM_GETLINE message specifies that a line of text from the edit control IDC_EDIT1 (the text edit box in the Login dialog) is copied to the fixed-length buffer lpszPassword. This buffer has sufficient space for 15 Unicode characters and a trailing null word. If more than 15 characters are input, a buffer overflow occurs. If 20 characters are entered, the 17th and 18th characters overwrite userP. The 19th and 20th characters overwrite userNameLen, and the trailing null word overwrites userPasswordLen.

Assuming that both userP and userNameLen are overwritten, an arbitrary memory write occurs on line 124 when userNameLen is assigned to the address stored in userP + 4 (the offset of len within struct user). By overwriting an address to which control is eventually transferred, attackers can take advantage of an arbitrary memory write to transfer control to arbitrary code. In this example, the return address on the stack is overwritten. Because the lpszGuestPassword variable is declared as an automatic variable in the GetPassword function, you can examine memory starting from the address of this variable.

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.