Writing Secure Code Vista
Writing Secure Code Vista
Writing Secure Code Vista
MICROSOFT EBOOK
These license terms are an agreement between Microsoft Corporation (or
based on where you live, one of its affiliates) and you. Please read them.
They apply to the licensed content named above, which includes the media
on which you received it, if any.
By using the licensed content, you accept these terms. If you do not accept
them, do not use the licensed content.
If you comply with these license terms, you have the rights below.
1. OVERVIEW.
Licensed Content. The licensed content includes the eBook and associated
media.
License Model. The licensed content is licensed on a per user per device basis.
Not to distribute, rent, lease, lend, reproduce, transit, adapt, modify, link to,
post, forward, make derivative works based upon, disseminate, publish or
sublicense the licensed content or combine the licensed content with other
third party content, or otherwise use the licensed content in an unauthorized
or illegal manner;
To keep the licensed content in the same format in which it was provided;
and
4. ENTIRE AGREEMENT. This agreement is the entire agreement for the licensed
content.
5. APPLICABLE LAW.
a. United States. If you acquired the licensed content in the United States,
Washington state law governs the interpretation of this agreement and
applies to claims for breach of it, regardless of conflict of laws principles. The
laws of the state where you live govern all other claims, including claims
under state consumer protection laws, unfair competition laws, and in tort.
b. Outside the United States. If you acquired the licensed content in any
other country, the laws of that country apply.
6. LEGAL EFFECT. This agreement describes certain legal rights. You may have
other rights under the laws of your country. You may also have rights with
respect to the party from whom you acquired the licensed content. This
agreement does not change your rights under the laws of your country if the laws
of your country do not permit it to do so.
7. DISCLAIMER OF WARRANTY. THE LICENSED CONTENT IS LICENSED
AS-IS. YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO
EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. YOU MAY
HAVE ADDITIONAL CONSUMER RIGHTS UNDER YOUR LOCAL LAWS
WHICH THIS AGREEMENT CANNOT CHANGE. TO THE EXTENT
PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NON-INFRINGEMENT.
8. LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. YOU
CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT
DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER
DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL,
INDIRECT OR INCIDENTAL DAMAGES.
It also applies even if Microsoft knew or should have known about the possibility of
the damages. Some states do not allow the exclusion or limitation of incidental or
consequential damages, so the above limitation or exclusion may not apply to you.
They also may not apply to you because your country may not allow the exclusion or
limitation of incidental, consequential or other damages.
Please note: As this licensed content is distributed in Quebec, Canada, some
of the clauses in this agreement are provided below in French.
Remarque : Ce le contenu sous licence tant distribu au Qubec, Canada,
certaines des clauses dans ce contrat sont fournies ci-dessous en franais.
EXONRATION DE GARANTIE. Le contenu sous licence vis par une licence est
offert tel quel . Toute utilisation de ce contenu sous licence est votre seule risque
et pril. Microsoft naccorde aucune autre garantie expresse. Vous pouvez bnficier
de droits additionnels en vertu du droit local sur la protection dues consommateurs,
que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les
garanties implicites de qualit marchande, dadquation un usage particulier et
dabsence de contrefaon sont exclues.
LIMITATION DES DOMMAGES-INTRTS ET EXCLUSION DE
RESPONSABILIT POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et
de ses fournisseurs une indemnisation en cas de dommages directs uniquement
hauteur de 5,00 $ US. Vous ne pouvez prtendre aucune indemnisation pour les
autres dommages, y compris les dommages spciaux, indirects ou accessoires et
pertes de bnfices.
Cette limitation concerne:
tout ce qui est reli au le contenu sous licence, aux services ou au contenu (y
compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ;
et
PUBLISHED BY
Microsoft Press
A Division of Microsoft Corporation
One Microsoft Way
Redmond, Washington 98052-6399
Copyright 2007 by David LeBlanc and Michael Howard
All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form
or by any means without the written permission of the publisher.
Library of Congress Control Number: 2007922582
Printed and bound in the United States of America.
1 2 3 4 5 6 7 8 9 QWT 2 1 0 9 8 7
Distributed in Canada by H.B. Fenn and Company Ltd.
A CIP catalogue record for this book is available from the British Library.
Microsoft Press books are available through booksellers and distributors worldwide. For further information about international editions, contact your local Microsoft Corporation office or contact Microsoft
Press International directly at fax (425) 936-7329. Visit our Web site at www.microsoft.com/mspress.
Send comments to [email protected].
Microsoft, Microsoft Press, Active Directory, ActiveX, Authenticode, BitLocker, Hotmail, Internet
Explorer, MSDN, Outlook, SQL Server, Visual Basic, Visual C++, Visual Studio, Win32, Windows,
Windows CardSpace, Windows Media, Windows NT, Windows Server, and Windows Vista are either
registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.
Other product and company names mentioned herein may be the trademarks of their respective owners.
The example companies, organizations, products, domain names, e-mail addresses, logos, people, places,
and events depicted herein are fictitious. No association with any real company, organization, product,
domain name, e-mail address, logo, person, place, or event is intended or should be inferred.
This book expresses the authors views and opinions. The information contained in this book is provided
without any express, statutory, or implied warranties. Neither the authors, Microsoft Corporation, nor its
resellers, or distributors will be held liable for any damages caused or alleged to be caused either directly
or indirectly by this book.
Acquisitions Editor: Ben Ryan
Developmental Editor: Devon Musgrave
Project Editor: Valerie Woolley
Editorial and Production Services: Carlisle Publishing Services
Body Part No. X13-62470
Programming Microsoft
ASP.NET 3.5
George Shepherd
Dino Esposito
ISBN 9780735624269
ISBN 9780735625273
JavaScript
Step by Step
Steve Suehring
ISBN 9780735624498
Eric Griffin
ISBN 9780735626065
Your hands guide to learning fundamental Webdevelopment skills. This tutorial steps you through
an end-to-end example, helping build essential skills
logically and sequentially. By the end of the book, youll
have a working Web site, plus the fundamental skills
needed for the next levelASP.NET.
Introducing Microsoft
Silverlight 2,
Second Edition
Laurence Moroney
ISBN 9780735625280
ALSO SEE
Developing Service-Oriented AJAX
Applications on the Microsoft Platform
ISBN 9780735625914
ISBN 9780735622012
ISBN 9780735621770
microsoft.com/mspress
For my mother and father. Thank you for encouraging me in my earliest geeky
endeavors. For buying my first chemistry set, microscope and rock collection.
But most of all, I cant express how much I appreciate you
getting me my very first computer.
Michael
To my wife, Jennifer, who has put up with yet another book. To Mr. Pennington, our school librarian,
who I set out to annoy by programming a noisy computer in his library Ive been hooked on
programming since. To Floyd R. Hacker, who introduced me to Windows,
and one fateful day took a huge pile of research data, pulled it
into Excel and quite unknowingly set me along this path.
David
iii
Contents at a Glance
1
2
3
4
5
6
7
8
9
Code Quality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
User Account Control, Tokens, and Integrity Levels . . . . . . . . . . . . . . .
Buffer Overrun Defenses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Networking Defenses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Creating Secure and Resilient Services . . . . . . . . . . . . . . . . . . . . . . . . . .
Internet Explorer 7 Defenses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Cryptographic Enhancements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Authentication and Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Miscellaneous Defenses and Security-Related Technologies . . . . . . .
1
13
49
75
97
121
135
151
163
Table of Contents
Foreword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii
Target audience . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xviii
How does this book relate to Writing Secure Code?. . . . . . . . . . . . . . . . . . . . . . . . . xviii
How to Read This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix
Getting Started with the Code in this Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xx
Whats on the Companion Web Site? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi
System Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi
Microsoft Press Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxii
Questions and Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxii
Code Quality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
SAL by Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
__in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
__out. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
__in_opt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
__inout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
__inout_bcount_full(n) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
__inout_bcount_part(n,m) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
__deref_out_bcount(n) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
www.microsoft.com/learning/booksurvey/
vii
viii
Table of Contents
Table of Contents
ix
SE_RELABEL_PRIVILEGE (32L) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
SE_INC_WORKING_SET_NAME (SeIncreaseWorkingSetPrivilege) . . . . . . . . . . 45
SE_INC_WORKING_SET_ PRIVILEGE (33L) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
SE_TIME_ZONE_NAME (SeTimeZonePrivilege) . . . . . . . . . . . . . . . . . . . . . . . . . 45
SE_TIME_ZONE_ PRIVILEGE (34L) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
SE_CREATE_SYMBOLIC_LINK_NAME (SeCreateSymbolicLinkPrivilege) . . . . . 45
SE_CREATE_SYMBOLIC_LINK_ PRIVILEGE (35L). . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Call to Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Networking Defenses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
IPv6 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Teredo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Network List Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
The Windows Vista RSS Platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Winsock Secure Socket Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Windows Firewall with Advanced Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Global Firewall Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Creating Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Working with Rule Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Call to Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Table of Contents
Table of Contents
xi
xii
Table of Contents
www.microsoft.com/learning/booksurvey/
Foreword
Writing Secure Code for Windows Vista is a very important book for everyone in the hightechnology industry. The ideas expressed in this book represent the latest thinking in creating
the trustworthy experience every user of technology expects.
Microsofts vision for the use of technology by all segments of society is based on the notion that
Moores law will continue, creating ever more powerful, and ever less expensive, computers that
can be applied in an increasing number of ways to meet the needs of all types of people. With
the continued growth in broadband networks, including new wireless technologies, these
devices will be connected to each other locally and across the world, giving people unprecedented access to information, media, and services with a level of convenience we are only starting to fathom. This vision is not only Microsofts; it is shared by many across the hightechnology industry. No matter your commercial or technological allegiance, this vision spells
out how our industry will grow, how we will generate commercial success for our businesses,
and how we will all contribute to a world that is more convenient for everyone and that allows
every person to reach his or her full potential. It is a wonderful visionand none of us can wait
to get there.
There is one important baseline requirement to fulfill this vision. If people of all walks of life
are to join us in this wonderful, connected, digital world, they will need to trust the technology. People want to know that their privacy is guaranteed. People want to know that their critical information is safe from theft and vandalism. People want their experience to be reliable
and simple. This is growing harder all the time.
At Microsoft, we track the activities of the hackers and criminals who work on Internet very
closely. The environment is growing more complicated: offenders range from people who
hack into systems for fun and fame to organized crime. It is estimated that Internet-based
criminals caused billions of dollarsin losses across the world. That kind of money can fund a
lot of engineering, and it means criminals are more persistent than ever in finding even the
most obscure vulnerability in every kind of high-technology product. It is no longer feasible to
assume that nobody will notice a vulnerability in a product. We have reached the age where if
a vulnerability is present, it will get noticed and exploited.
As hacking for profit grows, the criminal community is becoming more organized in sharing
information on vulnerabilities. We now live in an environment where criminals specialize in
weaponizing vulnerability information, driving the time between the identification of a vulnerability and a profitable exploit of that vulnerability to zero. These specialized criminals are
the arms dealers of organized crime on the Internet, and they sell their wares to the highest
bidder.
Because of the evolution of the criminal hacker community, it is even more important for
everyone producing high-technology products to use rigorous engineering standards, such as
xiii
xiv
Foreword
the Secure Development Lifecycle, to minimize the chance that there is a vulnerability in a
product. Because high-technology products are made by human beings, there is no guarantee
of zero vulnerabilities. However, we as an industry can band together and use our community
to maximize our defenses.
As a software platform, Windows has a special role in helping software companies of all kinds
make it as easy as possible to deliver software that is secure from vulnerabilities and exploits.
Writing Secure Code for Windows Vista was written to aid anyone writing a Windows application to follow security best practices that are proven to deliver fundamentally more secure software products. Windows Vista was designed with features that give software writers platform
tools that aid in making their software secure. These features represent Microsofts best understanding of what software applications need in the areas of authentication, data protection,
and protection from exploits. By using the new security features of Windows Vista, applications become more secure and trustworthy in customers eyes.
The journey to create the connected digital vision is a long one, and there will be many fits
and starts as the high-technology industry works together to deliver convenience and value to
all types of people. One important requirement of success is creating technology products
that people trust. Writing Secure Code for Windows Vista will help all of us deliver fundamentally more secure and trustworthy products to our customers.
Jon DeVaan
Senior Vice President of Engineering Excellence
Microsoft Corporation
February 2007
Acknowledgments
We have now written four books together so far, and we are proud of every one. Writing books
is never easy, but its made a lot easier when you have a great group of people helping you
along the way. What sets this book apart from the other is the list of acknowledgements. Its
huge! In fact, the list is so big we have decided to break it up into groups! Every person listed
below contributed something to this book, from code review to fact checking and from little
ideas to making sure we covered critically important topics.
Windows
Dave Cutler, Saji Abraham, Daniel Wang, Hunter Hudson, Gov Maharaj, Richard Ward, Walter
VonKoch, Sean Lyndersay, Mike Sheldon, Sandeep Singhal, Henry Sanders, Neeraj Garg,
Deepak Bansal, Ravi Rao, Stan Pennington, David Bennett, David Kennedy, Landy Wang,
Chuck Reeves, Andy Harjanto, Kim Cameron, Mike B. Jones, Garrett Serack, Brent Schmaltz,
Adrian Marinescu, Ramesh Chinta, Chittur Subbaraman, Christian Huitema, Eran Yariv, and
Mahesh Mohan.
Internet Explorer
Marc Silbey, Rob Franco, Dean Hachamovitch, Jeremy Dallman, Zhenbin Xu, Eric Lawrence,
Alex Kuang, Tariq Sharif, Joshua Allen, Venkat Kudallur, and Chris Wilson.
Microsoft Office
Alan Myrvold, Tom Gallagher, Lawrence Landauer, Hidetake Jo, and Mike Marcelais.
Developer Division
Mark Lacey
xv
xvi
Acknowledgments
Microsoft Consulting
Aaron Margosis
Microsoft Press
Devon Musgrave, as usual guided us seamlessly through the book-writing process and to Ben
Ryan for agreeing to do the book. Valerie Woolley our editor was a dream to work with. One
thing we both love about working with Microsoft Press, is the editors dont try to force their
style over our writing style. And we appreciate that!
And to anyone we missed. Were sorry.
Michael Howard
David LeBlanc
Redmond, WA
March 2007
Introduction
In this Introduction:
Target Audience . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xviii
How does this Book Relate to Writing Secure Code? . . . . . . . . . . . . . . . . . . . . . . xviii
How to Read This Book. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix
Getting Started with the Code in this Book. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xx
What's on the Companion Web Site? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi
System Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi
Microsoft Press Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxii
Computers hooked up to the Internet are attacked and compromised every day. There is no
doubt that attackers are moving further up the software stack and attacking software installed
on computers. This may in part be due to software vendors spending more time shoring up
the defenses of their operating systems. Between the release of Windows XP in 2001 and
Windows Vista in 2006, the security landscape evolved dramatically: while attacks are much
more common, more troublesome is the rise in system compromises. You often hear that Windows is the most attacked platform. Thats true, so the next comment may seem alarming: we,
the authors, are not overly concerned about attacks, attacks will always happen. It is compromises that people ought to be worried about. And this made people at Microsoft think long
and hard about how to make Windows Vista a considerably more secure product.
Windows Vista is the most secure operating system released by Microsoft. The sheer magnitude of defensive engineering added to the operating system is staggering, and we were
actively involved in many of these defenses. Even with all this work, attacks will continue.
Were not saying Windows Vista will have no security bugs, it will; but numerous defenses
have been added to the operating system to reduce the likelihood that attacks successful and
to reduce the chance that a vulnerability can be exploited in the first place. The goal of these
defenses is to reduce the likelihood of compromise.
So what are you doing to secure your application in light of the increased attack potential?
Remember, attacks will happen! Just because your application may not have the word
Microsoft in its title does not mean it wont be attacked! What makes security a fascinating
subject is that unlike, say, computer performance or reliability, which are disciplines that
focus on person vs. computer issues, security is all about person vs. person. Security issues
will never completely go away because there will always be the malevolent human
influence.
In addition to tidying up the code, and fixing design problems, Microsoft has also made a
huge amount of new functionality available that everyone can use to make applications more
xvii
xviii
secure. Unlike our previous books, which largely covered fundamentals, this book outlines
how your applications that run on top of Windows Vista can take advantage of these defenses
to protect shared customers. This book goes beyond the basic defensive strategies you can
employ to also cover the some of the new security features you can use to add defenses to your
applications to help you meet business you or your customers objectives.
Microsoft has spent over five years adding defenses and new, modern security features to
Windows Vista, and you can use these defenses and security features within your application
to reduce the chance your application is compromised. You can reduce the chance of attack in
three ways:
Reduce affected users if no one uses your application, youre not worth attacking.
Reduce the attack surface components that have fewer points of entry (especially by
default) are attacked less often.
Harden your attack surface to the point that something else becomes the path of least
resistance.
We really dont advise taking the first approach, but this is what will happen if you dont pay
attention to security and stay current. The last two approaches are what wed like to help you
with, to you can- protect your customers by taking advantage of operating system defenses
and features, and that is the simple goal of this book to explain how you can use these
Windows Vista features.
Target Audience
The target audience for this book is primarily developers building software for Windows
Vista. There are some design considerations in the book too, so designers and architects will
get some benefit. Note however, that this book is very code-heavy.
It is also assumed you have some knowledge of the Windows authentication and authorization system. While some background information is provided in this book, it is assumed the
reader knows how objects are protected in Windows. If you do not, you should read a book
such as Windows Internals Fourth Edition, by Mark Russinovich and David Solomon (Microsoft
Press 2005).
Introduction
xix
contains design and coding best practice as they apply to Windows Vista. This book is not a
superset of Writing Secure Code, this book is an adjunct to Writing Secure Code.
xx
Finally, you should add the following line before you include any header files in your application. A good place is the first line in stdafx.h.
#define _WIN32_WINNT 0x0600
If you dont add this line to your code, your code will probably fail to compile if it references
functions, definitions, constants or structures new to Windows Vista.
Introduction
xxi
Important
Note that all the sample C and C++ code makes full use of SAL (described in
chapter 1) where appropriate and compiles with no warnings at warning level 4 (/W4) and
has no PREfast warnings (/analyze.) The only exception is where we show examples of insecure practices.
System Requirements
Any Windows Vista capable machine can run the sample code that accompanies this book
and is locate on the companion Web site. The Minimum Requirements for Windows
Vista are:
20 gigabytes (GB) hard disk drive capacity with 15GB available space
To get a better Windows Vista experience, including Windows Aero visual effects, your
computer needs to meet these minimum requirements:
1GHz processor
1 GB of system memory
DirecteX 9-capable graphics processor with a Windows Vista Display Driver Model
(WDDM) driver, 128 GB of graphics memory (if the GPU uses dedicated memory otherwise, no additional graphics memory is required), Pixel Shader 2.0 and 32 bits per pixel.
xxii
Chapter 1
Code Quality
In this chapter:
The Windows Vista Quality Gates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
All C/C++ String Buffers Annotated with SAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Banned APIs Are Removed from the Codebase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Banned Cryptography Removed from the Codebase . . . . . . . . . . . . . . . . . . . . . . . . . 9
Static Analysis Used to Find and Fix Bugs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Unmanaged C/C++ Compiled with /GS and Linked with /SafeSEH,
/DynamicBase/, and NXCompat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .11
Call to Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .11
References. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12
Code qualityand security is a subset of qualityis critically important in the field of software.
In fact, nothing is more important than code quality, with the possible exception of good
design. In Windows Vista we spent a great deal of time reducing the chance that developers
will inject new security bugs into the operating system, as well as finding bugs and removing
them from the codebase.
With that said, getting the code and design 100 percent secure is impossible because security
is a constant arms race between software developers and attackers. All you can strive for is to
raise the security bar as far as possible in your code and to continue to do so as you create new
code. And this is why other defenses are so important: there will always be design and code
security bugs, but defenses can often eliminate a bug or reduce the chance that an attack will
be successful. This chapter focuses on getting the code right; it does not discuss designlevel issues that can be found through threat modeling. A good reference for the present
threat modeling state of the art can be found in The Security Development Lifecycle (Howard
and Lipner 2006).
The security engineering effort in Windows Vista is simply astronomical, and you can implement many of the process changes we made to Windows Vista within your own organization.
This chapter outlines what we did (a) to help reduce the chance that developers will add new
security bugs to the code and (b) to find existing bugs.
Note
This chapter does not focus on defensive features available in Windows Vista; rather,
it outlines some of the process improvements and code changes we made to the operating
system.
1
Two of the major tasks undertaken in Windows Vista were removing banned application programming interfaces (APIs) and adding Standard Annotation Language (SAL) annotations to
C functions and C++ methods. Both these tasks, explained later in this chapter, proved very
effective at finding and removing security bugs. An analysis of 63 buffer-related security bugs
that did not affect Windows Vistabut that did affect Windows 2000, Windows XP, or
Windows Server 2003discovered the following:
Code that used banned APIs accounted for 26 (41 percent) of the bugs.
Not Found
16%
Found through
SAL 43%
Banned API
Removal
41%
Figure 1-1
Chart of bugs prevented in Windows Vista because of banned API removal and SAL
annotation.
In other words, the combination of SAL and banned API removal alone meant 84 percent of
the buffer-related security bugs found in versions of Windows prior to Windows Vista did not
affect Windows Vista because the bug was found via SAL or the problem was solved because
the banned API was removed and replaced with a safer function. Of course, classes of bugs
exist that will not be prevented through the use of SAL and banned API removal.
Stop and think about these figures for moment. Removing banned APIs is pretty straightforward and adding SAL annotations is not particularly complex, yet doing these two tasks
found or prevented 52 out of 63 bugs! I think by any measure this is a great example of bang
for the buck! One bug was prevented because of banned API removal and SAL annotation,
which indicates that to be effective, you must perform both tasks on your code.
This chapter starts with a brief explanation of the Windows Vista Quality Gates and then continues by describing the security requirements required by the quality gates.
Chapter 1
Code Quality
quality gate. For example, the security quality gate had the following requirements, which we
will discuss throughout this chapter:
Unmanaged C/C++ compiled with /GS and linked with /SafeSEH, /DynamicBase and
/NXCompat
Essentially, new code must pass the quality gate before it can be accepted into the Windows
Vista source code tree. The quality gates apply to all developers, and are a very simple and
effective way of reducing bugs that enter the system. But most importantly, the quality gates
are a means to enforce code policy. Many, but not all, of the security quality gate requirements
are derived from the Security Development Lifecycle (SDL). In fact, some security quality gate
requirements, as they apply to Windows Vista, go above and beyond the SDL requirements.
The rest of this chapter focuses on the Windows Vista security quality requirements in detail.
You should know that SAL comes in two flavors: one is called declspec syntax,
and the other is attribute syntax. Windows Vista uses the former, and the examples in this
book also follow the declspec syntax. However, Microsoft Visual Studio 2005 does support
both syntaxes.
When you annotate a function with SAL, any code that calls that function will get the benefit
of the annotation. To this end, we have annotated the majority of C runtime functions
included with Visual Studio 2005 and the Windows functions included in the Windows Vista
Software Development Kit. This means youll get the benefit of the annotations added by
Microsoft, and you might find bugs lurking in your code.
SAL defines proper use of buffers. A C/C++ pointer can be used to represent the address of a
single object, or an array of objects. Sometimes the size is known at compile time, and
sometimes its only known at run time. Because C/C++ pointer types are overloaded, you
cant rely on the type system to help you program with buffers properly! Thats why we have
SAL.
The Windows Vista security quality gate stated that all mutable non-constant string buffers
for new features must be annotated with SAL. In reality, this translated into all functions that
read from or write to a buffer.
SAL by Example
Probably the best way to explain SAL is by showing an example. Lets say you have a C/C++
function:
void FillString(
TCHAR* buf,
size_t cchBuf,
TCHAR ch) {
for (size_t i = 0; i < cchBuf; i++)
buf[i] = ch;
}
We wont insult your intelligence by explaining what the function does, but what makes this
code interesting is that two of the arguments, buf and cchBuf, are closely linked: buf
should be at least cchBuf characters long. If buf is not as big as cchBuf claims it is, then the
FillString function could overflow the buf buffer. If you compile the code below with Visual
Studio 2005, at warning level 4 (/W4) youll see no warnings and no errors, yet clearly a
buffer overrun vulnerability is in this code:
TCHAR *b = (TCHAR*)malloc(200*sizeof(TCHAR));
FillString(b,210,'x');
What SAL does is allow a C or C++ developer to inform the compiler of the relationship
between the two arguments by using syntax such as this:
void FillString(
__out_ecount(cchBuf) TCHAR* buf,
size_t cchBuf,
TCHAR ch) {
for (size_t i = 0; i < cchBuf; i++)
buf[i] = ch;
}
Note the use of __out_ecount(n) just before buf in the argument list. This is a macro that wraps
some very low-level SAL constructs you should never have to worry about, but in essence
__out_ecount(n) means this:
buf is an out parameter, which means it will be written to by the callee, and buf cannot be
NULL. The length of buf is n elements, in this case cchBuf TCHARS.
Chapter 1
Code Quality
When this code is compiled with a Microsoft C++ compiler that has the /analyze option, such
as the version shipped with Visual Studio 2005 Team Suite or any of the Visual Studio Team
System products, or cl.exe included with the Windows Vista Software Development Kit, youll
see output like this:
c:\code\saltest\saltest.cpp(54) : warning C6203: Buffer overrun for non-stack buffer 'b'
in
call to 'FillString': length '420' exceeds buffer size '400'
c:\code\saltest\saltest.cpp(54) : warning C6386: Buffer overrun: accessing 'argument 1',
the writable size is '200*2' bytes, but '420' bytes might be written: Lines: 53, 54
c:\code\saltest\saltest.cpp(54) : warning C6387: 'argument 1' might be '0': this does not
adhere to the specification for the function 'FillString': Lines: 53, 54
__in
The function using __in will only read from the single-element buffer, and the buffer must be
initialized (not NULL); as such __in is exactly the same as __in_ecount(1) and __in is implied
if the argument is a const. In fact, __in is somewhat redundant, and its better to use const
because the compiler can perform better optimizations in some cases.
The following function prototype shows how you can use __in:
BOOL AddElement(__in ELEMENT *pElement);
__out
The function using __out fills a valid (not NULL) buffer, and the buffer can be dereferenced by
the calling code. The following function prototype shows how you can use __out.
BOOL GetFileVersion(
LPCWSTR lpsFile,
__out FILE_VERSION *pVersion);
__in_opt
The function using __in_opt expects an optional buffer, meaning the buffer can be NULL. The
following code shows how you could use __in_opt. In this example, if szMachineName is
NULL, the code will return operating system information about the local computer.
BOOL GetOsType(
__in_opt char *szMachineName,
__out MACHINE_INFO *pMachineInfo);
__inout
The function using __inout expects a readable and writeable buffer, and the buffer must be
initialized by the caller. Here is some sample code that shows how you might use __inout.
size_t EncodeStream(
__in HANDLE hStream,
__inout STREAM *pStream);
__inout_bcount_full(n)
The function expects a buffer that is n-bytes long that is fully initialized on entry and exit.
Note the use of bcount rather than ecount: b means bytes and e means elements; for example, a Unicode string in Windows that is 12 characters (an element in SAL parlance) long is 24
bytes long. There is an element variant too: __inout_ecount_full(n). The following code example takes a BYTE * that points to a buffer to switch from big-endian format to little-endian format so it makes sense that the incoming buffer is fully initialized and is a fully initialized buffer
on function exit. Youll also see another SAL macro in the function prototype, __out_opt,
which means the data will be written to by the function, but it can be NULL. In the case of a
NULL exception point, the function will not return exception data to the caller.
void ConvertToLittleEndian(
__inout_bcount_full(cbInteger) BYTE *pbInteger,
DWORD cbInteger,
__out_opt EXCEPTION *pException);
__inout_bcount_part(n,m)
The__inout_bcount_part(n,m) annotation is a variant of __inout_bcount_full, but rather than
initializing the entire buffer, the code will only fill up to m-bytes in the destination buffer. The
following function prototype implies that Read() will copy no more than cbCount bytes into
pBuff, and the actual number of bytes is reflected in pcbHowMuchRead. Note that the function
will return one size_t argument as the last argument, unless its NULL (the opt part of the
SAL macro name).
HRESULT Read(
__inout_bcount_part(cbCount, *pcbHowMuchRead) LPVOID pBuff,
__in size_t cbCount,
__out_ecount_opt(1) size_t *pcbHowMuchRead);
__deref_out_bcount(n)
The function argument marked with __deref_out_bcount(n) when dereferenced will be set to
an uninitialized buffer of n-bytes, in other words, *p is initialized, but **p is not.
Chapter 1
Code Quality
HRESULT StringCbAlloc(
size_t cb,
__deref_out_bcount(cb) char **ppsz) {
*ppsz = (char*)LocalAlloc(LPTR, cb);
return *ppsz ? S_OK : E_OUTOFMEMORY;
}
This bug really has little to do with any function argument, rather it occurs because the code
calls malloc() but does not check that the return value from malloc() is non-NULL. If you look
at the function prototype for malloc() in malloc.h in Visual Studio 2005 or the Windows Vista
Software Development Kit, youll see this:
__checkReturn __bcount_opt(_Size) void *__cdecl malloc(__in size_t _Size);
Because the return from malloc() could be NULL (because the function may fail), we use a
__bcount_opt(n) SAL macro (note the use of opt in the macro name). If we change the code
that calls malloc() to check the return is non-NULL prior to calling FillString, the C6387 warning goes away. Dont confuse an optional NULL return value with __checkReturn; the latter
detects whether you ignored the result altogether. For example:
size_t cb = 10 * 12;
malloc(cb);
This code will yield this warning when compiled with /analyze:
c:\code\saltest\saltest.cpp(30) : warning C6031: Return value ignored: 'malloc'
Make sure you have a solid code baseline that compiles cleanly.
Determine ahead of time which SAL annotations you are going to use. For example, are
you going to focus on annotating read and write buffers, or write buffers exclusively?
A great SAL documentation source is the comment block at the start of the sal.h header file, as
well as the SAL annotations already in the Windows Vista Software Development Kit headers
and the C runtime headers included with Visual Studio 2005.
In addition to using SAL, proper use of Hungarian notation (Simonyi 1999) around string
buffers can help prevent problems. If you know that cbCount is a count of bytes, and cchCount
is a count of characters, this can be helpful in spotting problems during code review. While its
possible to go overboard with Hungarian notation, using it with buffer size variables will save
you a lot of trouble.
A security purist would say this code should be changed to use a safer function call, but the
realist would make no change because the code is safe. By the way, the purist would win the
argument if the source buffer increases because of localization!
Tip
If you decide not to replace a banned API with a safer alternative, you should add a
comment to the code explaining why the API is not replaced. This will save time in the future
when someone asks, Why didnt we replace this function?
The list of banned APIs is not static; it evolves if bugs are found. That being said, the list of
banned APIs at Microsoft has not changed in several years. When analyzing security bugs, we
make a note of the offending function calls, if any, and if we see the function call used in three
or more security bugs, we ban that function for new code. But we can only ban the function if
there is a viable replacement.
The good news is the process of removing banned APIs is pretty much a no-brainer if you use
Visual Studio 2005 because all banned APIs are deprecated. If your code uses a banned API,
the compiler will emit a C4996 warning:
c:\code\test.cpp(101) : warning C4996: 'wcscpy': This function or variable may be unsafe.
Consider using wcscpy_s instead.
Chapter 1
Code Quality
You only need to replace security-related calls to banned cryptographic algorithms. For example, if you use MD5 as a glorified checksum, then replacing it with SHA512
is probably overkill. Of course, MD5 is insecure when used to sign a document.
10
warnings. An examination of a large codebase where many of the integer-related warnings had
been disabled found potentially exploitable conditions in about 20 percent of the places
where the warning had been masked.
Warning Description
4018
4389
4244
4996
6029
6053
6057
6059
6200
Index <name> is out of valid index range <min> to <max> for non-stack buffer
<variable>
6201
Buffer overrun for <variable>, which is possibly stack allocated: index <name> is out of
valid index range <min> to <max>
6202
Buffer overrun for <variable>, which is possibly stack allocated, in call to <function>:
length <size> exceeds buffer size <max>
6203
Buffer overrun for buffer <variable> in call to <function>: length <size> exceeds buffer
size
6204
6209
6248
6383
Also, make sure you review code, looking for constructs that disable any of these warnings.
For example, the following code will disable all 6204 warnings:
#pragma warning ( disable : 6204 )
Chapter 1
Code Quality
11
A more correct way to suppress warnings is to use the following, which only suppresses the
warning on the line of code following the #pragma.
#pragma warning ( suppress : 6204 )
FxCop Warnings
Load your managed assemblies into FxCop and then triage all the security rule messages.
Call to Action
Consider using SAL for any new code, and annotate functions that take writeable buffers
as arguments. Consider annotating readable buffers too.
Over time you should remove all banned APIs from your C and C++ codebase. Use the
list provided at http://msdn.microsoft.com/security as a starting list. Functions like strcpy
and strcat should be removed first because they are most prone to error.
Over time you should remove all banned cryptography from your codebase. Use the list
of banned cryptographic algorithms provided at http://msdn.microsoft.com/security.
Also start planning for crypto-agility.
Determine as soon as possible a good toolset to use, and draw up a list of warnings you
consider heinous. Any error or warning that relates to buffer overruns or integer overflow problems should be top of the list to fix.
Compile your code with /GS, and link with /SafeSEH, /DynamicBase and /NXCompat.
12
References
Howard, Michael, and Steve Lipner. The Security Development
Lifecycle. Redmond, WA: Microsoft Press, 2006.
(MSDN 2005)
(Simonyi 1999)
Chapter 2
14
these locations, and software that writes to these locations will operate correctly only if its
run by high-privilege users. By default, user accounts in Windows XP are administrators, and
because it is the default, software developers assumed this default configuration and built
their software accordingly. While the default for Windows 2000 for a newly created user was
a standard user, Windows 2000 was typically used in a business setting. When Windows XP
is joined to a domain, the default domain user has only limited user rights, and new standard
users are added to the Power Users group for application compatibility.
Important
The Power Users group is deprecated in Windows Vista because it is so powerful; the group is still in the operating system, but no account is in the group, and no Access
Control Lists (ACLs) are left that reference Power User.
Windows Vista changes all the rules. Users are unprivileged users (often referred to as standard users); by default, administrators are unprivileged users as well. The reason Microsoft
enabled standard users to fully use Windows was to reduce the potential threat to the system
if the user or administrator ran malicious software. When code, malicious or not, runs with
full administrative privileges, it is not constrained to portions of the operating systemit can
manipulate any part of the operating system. User Account Control (UAC) enables most
applications to run properly without administrative privileges and provides users with a clear
indication when they launch an action that requires full administrator privileges.
The goal of this chapter is to explain how you can write applications that work correctly and
take advantage of the new user account-related features in Windows Vista. In this chapter we
discuss the following topics:
Virtualization
Integrity levels
This chapter is code-dense, and the reader is urged to refer to the complete code samples
included in the Ch02 folder of the sample code. Lets get started.
Chapter 2
15
allows administrators to wield greater control and management over corporate desktops.
However, local escalation of privilege vulnerabilities trump UAC, and every operating system
on the planet has had, and continues to have, local escalation of privilege bugs.
Of course, it is safer to run as a standard account to help reduce the chance that malicious software can corrupt or modify the operating system (for example, installing keystroke loggers or
rootkits). Remember that running as a standard user does not prevent malicious software
from copying itself to locations controlled by this user, such as the users profile directory. Nor
does running as a standard user prevent malicious software from accessing and possibly copying private data that can only be accessed by the user. Running as a standard user is very
important to the health of the computer, but it is not a panacea.
Privileges
Token type
Impersonation level
The following code snippet shows how to access the token of the current process from C or
C++:
HANDLE hToken = NULL;
BOOL f = OpenProcessToken(GetCurrentProcess( ),TOKEN_READ,&hToken);
if (f && hToken) {
// Cool, we have the token
} else {
printf("Error getting token. Err=%d\n",GetLastError( ));
}
if (hToken) {
CloseHandle(hToken);
hToken = NULL;
}
You can get the token of another process with code such as this as long as you know the process identity (the PID) and have the necessary rights to the process and token.
HANDLE h = NULL, hToken = NULL;
if ((h = OpenProcess(PROCESS_QUERY_INFORMATION,0,pid)) &&
OpenProcessToken(h,TOKEN_QUERY,&hToken))
// success
else
16
If the process is running as a different user account and you dont have access, you will need
to enable the debug privilege (SE_DEBUG_NAME) using AdjustTokenPrivileges prior to calling
OpenProcess.
The following code shows how to get the current process token using .NET 1.0:
using System.Security;
using System.Security.Principal;
...
try {
WindowsIdentity id = WindowsIdentity.GetCurrent();
IntPtr token = id.Token;
// Cool, we have the token
} catch (SecurityException e) {
Console.WriteLine(e.Message);
}
You can view some of the information in a processs token in Windows Vista using the whoami
/fo list /all command, which will produce output like this:
USER INFORMATION
---------------User Name: homecomputer\toby
SID:
S-1-5-21-2014314949-3535451937-4269012161-1002
GROUP INFORMATION
----------------Group Name: Everyone
Type:
Well-known group
SID:
S-1-1-0
Attributes: Mandatory group, Enabled by default, Enabled group
Group Name:
Type:
SID:
Attributes:
BUILTIN\Users
Alias
S-1-5-32-545
Mandatory group, Enabled by default, Enabled group
<snip>
Group Name:
Type:
SID:
Attributes:
PRIVILEGES INFORMATION
---------------------Privilege Name: SeShutdownPrivilege
Description:
Shut down the system
State:
Disabled
Chapter 2
17
The token is for a normal user account, named Toby, and as you can see, the users token is
not bristling with dangerous privileges.
You can use a tool such as Process Explorer to spelunk the token associated with a running
process. The screen shot on the next page is an example from a process running as Toby.
More Info
The Windows Vista development team adopted personas to represent the core
customers for the operating system. The three most well known are two consumers and one
IT professional. The two consumers are Abby Salazar, a mother in the home, and Toby
Salazar, Abbys son. The IT professional persona is Ichiro. The personas are concrete representations of target customers and help guide the development team when making hard
design and feature decisions.
The users token is associated with every process the user executes, and the token is used by
the operating system to make all access and privilege decisions. By default, every thread that
starts within the process also gets the token, although this can be replaced using impersonation APIs such as ImpersonateNamedPipeClient or SetThreadToken.
When a protected resource (for example, a file, shared memory, or a registry key) is accessed
by a running process, Windows will compare the access control list (ACL) on the object with
the thread token to determine if access should be granted or not. The Windows access control
model is explained in detail in Microsoft Windows Internals, 4th ed. (Russinovich 2005).
More Info
Tobys token is not special; any user logging on as a standard user in Windows XP or
Windows Server 2003 would see a similar token (with the exception of the new privileges
added to Windows Vista) and new SID type, the mandatory label (which is explained in more
detail in the Integrity Control section later in this chapter).
18
Figure 2-1
Elevating to Administrator
When a standard user attempts to perform an administrative task (for example, allow a program through the firewall), the user is prompted to enter an administrative user account name
and password. This dialog box is called the Credential Prompt. If the user does not have the
credentials for an administrator, the application fails to start.
Chapter 2
Group Name:
Type:
SID:
Attributes:
BUILTIN\Administrators
Alias
S-1-5-32-544
Group used for deny only
Group Name:
Type:
SID:
Attributes:
BUILTIN\Users
Alias
S-1-5-32-545
Mandatory group, Enabled by default, Enabled group
19
<snip>
Group Name: Mandatory Label\Medium Mandatory Level
Type:
Unknown SID type
SID:
S-1-16-8192
Attributes: Mandatory group, Enabled by default, Enabled group
PRIVILEGES INFORMATION
---------------------Privilege Name: SeShutdownPrivilege
Description:
Shut down the system
State:
Disabled
Privilege Name:
Description:
State:
SeChangeNotifyPrivilege
Bypass traverse checking
Enabled
Privilege Name:
Description:
State:
SeUndockPrivilege
Remove computer from docking station
Disabled
SeTimeZonePrivilege
Change the time zone
Disabled
Note again, this account is not bristling with privileges, and the administrator SID is set to deny.
20
that explicitly denies the Finance group access to the object, and if Paige is a member of
the Finance group, removing the Finance group SID from Paiges token would allow
Paige access to the objectbecause shes no longer indentified as a member of the
Finance group! However, if the Finance group SID stays in Paiges token but is set to a
deny-only SID, then she will be denied access to the object. Thus, SIDs are not removed
from tokens but are specially marked for deny only. In other words, a deny-only SID is
considered only with Deny ACEs, not with Allow ACEs.
The most common SIDs set to deny only are local administrators, backup operators,
and network operators.
Chapter 2
21
Companion Content The Ch02\MegaDump sample on this books companion Web site
is a more complete token analysis tool.
Important
Most applications dont need to know if they run in an elevated token process.
We found that, in almost all cases, the developer should just call IsUserAnAdmin..
If you want more detail about whether the token is elevated or not, you can use the following
code:
TOKEN_ELEVATION_TYPE ElevationType;
cbSize = sizeof TOKEN_ELEVATION_TYPE;
if (GetTokenInformation(hToken,
TokenElevationType,
&ElevationType,
sizeof(ElevationType),
&cbSize)) {
switch(ElevationType) {
case TokenElevationTypeDefault :
// do something
break;
case TokenElevationTypeFull :
// do something
break;
case TokenElevationTypeLimited :
// do something
break;
}
}
Here is a list of the three token levels that describe the relative strength of a token.
TokenElevationTypeDefault
Tokens with the default elevation do not have a linked token. Standard users (for example,
Toby) authenticate to the system and are assigned a token with a default elevation type. Their
logon session is not linked to an elevated session. The built-in Administrator account (disabled by default) may also be assigned a default elevation type if the UAC policy, Admin
Approval Mode for the built-in Administrator account is disabled.
TokenElevationTypeFull
The token has all groups and privileges enabled that are assigned to this user account. Typically, this applies to users who are members of the Administrators group, but it also applies to
a few other groups that require elevation to use full privileges, such as Backup Operators. The
users full-privilege logon session is associated with (or linked to) another logon session for
the same user with administrative groups and privileges filtered.
22
TokenElevationTypeLimited
The user is a member of the Administrators group or has administrative privileges assigned to
the user account that are filtered from the token. The limited elevation type indicates that an
associated or linked logon session is available for the same user that has all administrative
groups and privileges enabled.
You can add a manifest file like this by following this process in Visual Studio 2005:
1. Launch Visual Studio 2005.
2. Open the project that requires elevation to run correctly.
3. Create a new XML file.
4. Add the text from the manifest to the XML file.
5. Save the file as projectname.uac.manifest alongside your project source code, where projectname is the name of your project.
6. Right-click Source Files in Solution Explorer.
7. Click Add and then click Existing item.
8. Find the manifest file and click on it.
9. Rebuild the project.
Note that when you compile the project using Visual Studio 2005, youll get a benign warning
that looks like this:
Elevate.uac.manifest : manifest authoring warning 81010002: Unrecognized Element
"requestedPrivileges" in namespace "urn:schemas-microsoft-com:asm.v3".
The manifest tool (mt.exe) included with Visual Studio 2005 does not recognize the requestedPrivileges element. But dont worry, the element is understood by Windows Vista.
Chapter 2
23
Caution
Windows XP SP2 has a bug when loading an image with an embedded manifest
that supports requestedPrivileges. This bug can cause Windows XP to crash when the manifest
is formatted in a specific manner. A bug fix is available (Microsoft 2006a).
When you run the application, you will be prompted to elevate to Administrator or prompted
to enter an administrators credentials, depending on your user account type.
Important
Applications marked to elevate will fail to load during operating system startup.
Notice the reference to requestedExecutionLevel in the manifest file; this option can be one of
three values as noted in Table 2-1.
Table 2-1
Name
Comment
asInvoker
Runs the application as the invoking user and does not prompt for
administrator credentials or confirmation. If the user is an administrator, then the application runs as an admin.
highestAvailable
requireAdministrator
The uiAccess element in the manifest should always be false, unless your application is an
Accessibility application, in which case it is true because this setting allows the application to
bypass User Interface (UI) protection levels to drive input to higher privilege windows on the
users desktop.
24
components, and some of these components might require administrator privileges and
some might notbut MMC cant know which components you want to use a priori.
Because MMC doesnt have a way to elevate or restart itself, the standard user finds himself
at a disadvantage. As a normal user who wants to perform administrative tasks, the user has
to exit MMC and then explicitly start MMC as admin. For mixed-mode applications that
need both administrative and standard user actions, developers should strive to mark their
application as asInvoker and then provide an elevation path for the administrative actions.
Use SendMessage or PostMessage to send most messages to higher-privilege application windows. These APIs return success but silently discard the message.
Chapter 2
25
If you are using Microsoft PowerShell, you can start an elevated application using this syntax:
(new-object com shell.application)
.shellexecute("cmd.exe",$null,$pwd.path,"runas",1)
Or, from the Windows Script Host (WSH), you can use this syntax:
var cmd = "cmd.exe";
var args = "";
new ActiveXObject("Shell.Application").ShellExecute(cmd, args, "", "runas");
If you program in C# or another .NET language, you can use code like this:
Process p = new Process();
ProcessStartInfo psi = new ProcessStartInfo(@ "cmd.exe");
psi.UseShellExecute = true;
psi.Verb = "runas";
p.StartInfo = psi;
p.Start();
If you want to run elevated script commands, it is highly recommended that you do so from
an elevated command window.
26
The following code shows how you can use this feature:
HRESULT CoCreateInstanceElevated(
__in_opt
HWND
hwndParent,
__in
REFCLSID
rclsid,
__in
LPCWSTR
lpwszElevationType,
__in
REFIID
riid,
__deref_out_opt PVOID
*ppv) {
wchar_t wszCLSID[64];
wchar_t wszMonikerName[128];
*ppv = NULL;
if (0 == StringFromGUID2(rclsid, wszCLSID, _countof(wszCLSID)))
return E_OUTOFMEMORY;
int err = swprintf_s(wszMonikerName,
_countof(wszMonikerName),
L"Elevation:%s!new:%s",
lpwszElevationType,
wszCLSID);
if ( -1 == err)
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
BIND_OPTS3 bo;
ZeroMemory(&bo, sizeof(bo));
bo.cbStruct
= sizeof(bo);
bo.hwnd
= hwndParent;
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
return CoGetObject(wszMonikerName, &bo, riid, ppv);
}
The first argument to CoGetObject is the moniker, which contains information that uniquely
identifies a COM object. The format for an elevation moniker is:
Elevation:Administrator!new{GUID}
or
Elevation:Highest!new{GUID}
Administrator maps to requireAdministrator and Highest maps to highestAvailable. You can also
replace !new with lclsid; the difference is !new will call IClassFactory::CreateInstance automatically but using !clsid does not.
Warning
Both the COM client and the COM server must be configured correctly to support elevation, which rules this technology out as a way to provide application compatibility.
For a full explanation of the settings and much more information, please refer to The COM
Elevation Moniker (MSDN 2006a). An elevated COM object represents a security boundary
and must not blindly expose administrator functionality to a client. A client may be under the
control of rogue software and may use different functions and parameters than the user
intended. To protect against this, the object should be driven solely through a user interface
that it presents to the user. Failure to do so might allow rogue software to elevate.
Chapter 2
27
You can also load the small shield icon by setting the second argument to SHGSI_ICON |
SHGSI_SMALLICON.
You should use the shield icon in user interface elements to indicate to the user which operations require elevation. For example, if an elevated action is to be performed after pressing
a button, then the button should display the shield icon. If the process is already elevated,
then there should be no shield icons displayed at all. The simplest way in a native application to mark a button as requiring elevation is to use the Button_SetElevationRequiredState
macro:
CWnd *pBtn = GetDlgItem(IDC_BACKUP_FILES);
Button_SetElevationRequiredState(pBtn->GetSafeHwnd(),!IsElevated( ));
Figure 2-2
A window showing the elevation shield. When the user clicks this button, she is
prompted to elevate, and then an elevated process is launched.
When a user clicks a button that starts an elevated task, you can start the task as either an elevated process, or better yet, an elevated COM object that displays a dialog box.
28
Virtualization
Before we describe virtualization, we want to point something out. Virtualization is intended
only as a bridge to assist with migration of legacy applications, and developers should not
take advantage of it for new applications. Applications that require virtualization to work correctly will not be logo-certified for Windows Vista, and virtualization is not supported for
native Windows 64-bit applications.
With that out of the way, virtualization is a feature that allows a legacy application to run as a
normal user, when normally it would fail if the application were not run by an administrator.
The biggest reason, by far, that applications fail to run as a user is that the application attempts
to write configuration or application-specific data to protected locations of the operating system.
Warning
The following code will fail unless it is run by an administrator or Power User on Windows XP
SP2 because its trying to write to the Program Files directory. Yet it runs correctly on Windows Vistaas long as its a 32-bit process executed by a normal user:
wchar_t wszPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(NULL,
CSIDL_PROGRAM_FILES,
NULL,
0,
wszPath))) {
PathAppend(wszPath, L"Savedgame.xml");
HANDLE hFile = CreateFile(
szPath,
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE ) {
// failed
} else {
// success
CloseHandle(hFile);
hFile = NULL;
}
}
This code works correctly on Windows Vista because application file and registry writes are virtualized by the operating system; write operations to protected locations (in this case, the Program Files directory) are redirected. If you look in the Program Files directory after running this
Chapter 2
29
application, you will see no file. So where is the file? Take a look in the C:\Users\
username\AppData\Local\VirtualStore\Program Files folder. As you can see, the file write
operation was redirected to a location the user can write to, and the application works correctly.
Tip
If you navigate using Explorer to a directory that has virtualized files, you will see the
words, Compatibility Files appear in the toolbar. If you click on this, you will see all the virtualized files.
You can see if a process is virtualized by looking in the Task Manager (see Figure 2-3). By
default the Virtualization column is not displayed, but you can display it by:
1. Launch Task Manager.
2. Click the Processes tab.
3. Click View.
4. Click Select Columns.
5. Scroll to the bottom of the list, and enable Virtualization.
Figure 2-3
30
You can also determine if a process is virtualized with the following code:
bool IsProcessVirtualized(__out bool& fVirt) {
HANDLE hToken = NULL;
bool fRet = false;
if (!OpenProcessToken(GetCurrentProcess( ),TOKEN_QUERY,&hToken))
return fRet;
DWORD IsVirtualized = 0;
DWORD cbLen = 0;
if (GetTokenInformation (
hToken,
TokenVirtualizationEnabled,
&IsVirtualized,
sizeof IsVirtualized,
&cbLen)) {
fVirt = !!IsVirtualized;
fRet = true;
}
if(hToken)
CloseHandle(hToken);
return fRet;
}
You can look at important virtualization events in the Event Viewer. Open the Event
Viewer, and expand Applications and Services Logs, then expand Microsoft, then expand
Windows, and finally expand UAC-FileVirtualization.
When updating existing code to run on Windows Vista or writing new code for Windows
Vista, developers should ensure that, during run time, applications only store user data under
the locations noted in Table 2-2.
Table 2-2
Directory
Environment
Variable
SHGetKnownFolderPath
Folder ID
Purpose
C:\Users\user\AppData\Roaming %appdata%
FOLDERID_RoamingAppData Roaming
Per-user data
C:\Users\user\AppData\Local
%localappdata%
FOLDERID_LocalAppData
C:\ProgramData
%programdata% FOLDERID_ProgramData
Process is 32-bit
Per-user data
Per-machine
data
Chapter 2
31
64-bit processes
Administrator processes
Services
Impersonated callers
Note that Visual Studio often embeds a manifest in the compiled application. You can
disable this by opening the project properties, expanding to the Manifest Tool option, clicking Input and Output, and then setting Embed Manifest to No.
If the application is named tool.exe, then save the manifest as tool.exe.manifest. The manifest
should include the following string:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
32
Integrity Levels
Windows Vista is the first version of Windows to include support for integrity protection.
Integrity protection adds a layer of defense to help reduce the chance that malicious software
will damage the operating system. Integrity levels are often referred to in the various Windows
development kits as mandatory labels. It is important to point out that integrity levels dont
prevent data disclosure; a privacy leak is still a privacy leak in the face of integrity protections.
Integrity protections allow or disallow only write operations, no read operations.
Important
The goal of integrity levels is to protect the operating system from damage,
not to protect user data from disclosure.
The tenet of integrity levels is pretty simple. A process of a lower integrity level cant write to an
object of a higher integrity level. Integrity controls in Windows Vista provide assurance that processes of lower trustworthiness (that is, lower integrity) cannot modify files or system objects of
higher trustworthiness (see Figure 2-4). This is often expressed as write-down, no write-up.
In Windows Vista, all protected objects are labeled with an integrity level. Most user and system
files and registry keys on the system have a default label of medium integrity. The primary
exception is a set of specific folders and files writeable by Internet Explorer 7 at Low integrity.
Most processes run by standard users are labeled with medium integrity, and most services are
labeled with System integrity. The root directory is protected by a high-integrity label.
When a process attempts to open an object for write access, the integrity level is checked first.
If that check succeeds, then a normal DACL check is performed. The flowchart in Figure 2-5
shows the process.
Chapter 2
Write operations
High Integrity
Low Integrity
Figure 2-4
Integrity model in Windows Vista. Lower-integrity subjects cannot write to higherintegrity objects.
Start
Is process
Integrity level
>= object
Integrity
level?
Write
Operation?
Does object
DACL allow
process identity
requested
access?
Access
Granted
Access
Denied
Figure 2-5
33
34
The integrity levels in Windows Vista are represented in classic, well-known SID nomenclature, as shown in Table 2-3.
Table 2-3
Integrity SIDs
Integrity Levels
SID
S-1-16-4096
S-1-16-8192
S-1-16-12288
S-1-16-16384
Note
If you look at the output from whoami earlier in this chapter you will see the following entry:
Group Name:
Type:
SID:
Attributes:
As you can see, the SID in the token shows that this process is running at medium integrity.
And yes, Unknown SID type is a bug; it should read Mandatory SID type.
Process Explorer also shows the integrity level for the application as shown in Figure 2-6.
When a user elevates to administrator, the integrity label included in the full-privilege token is
a high-integrity level label.
Figure 2-6
By default, all processes run at a medium-integrity level; this screen shot shows a
process running at medium.
Chapter 2
35
You can experiment with integrity levels with the following code, which will run a process at
low-integrity level:
DWORD err = 0;
HANDLE hToken = NULL;
HANDLE hNewToken = NULL;
PROCESS_INFORMATION ProcInfo = {0};
PSID pIntegritySid = NULL;
try {
if (!OpenProcessToken(
GetCurrentProcess( ),
TOKEN_QUERY | TOKEN_DUPLICATE,
&hToken)) {
wprintf(L"OpenProcessToken failed (%d)\n", GetLastError( ));
throw GetLastError( );
}
TOKEN_ELEVATION_TYPE ElevationType;
DWORD cbSize = sizeof TOKEN_ELEVATION_TYPE;
if (GetTokenInformation(hToken,
TokenElevationType,
&ElevationType,
sizeof(ElevationType),
&cbSize)) {
if (ElevationType == TokenElevationTypeFull) {
throw ERROR_ACCESS_DENIED;
}
} else {
wprintf(L"GetTokenInformation failed (%d)\n", GetLastError());
throw GetLastError( );
}
if (!DuplicateTokenEx(
hToken,
TOKEN_ALL_ACCESS,
NULL,
SecurityImpersonation,
TokenPrimary,
&hNewToken)) {
wprintf(L"DuplicateTokenEx failed (%d)\n", GetLastError());
CloseHandle(hToken);
hToken = NULL;
throw GetLastError();
}
if (!ConvertStringSidToSid(
L"S-1-16-4096",// Low integrity SID
&pIntegritySid)) {
wprintf(L"ConvertStringSidToSid failed (%d)\n", GetLastError( ));
throw GetLastError( );
}
TOKEN_MANDATORY_LABEL til = {0};
til.Label.Attributes = SE_GROUP_INTEGRITY;
til.Label.Sid = pIntegritySid;
36
You can create a low-integrity SID using ConvertStringSidToSid as shown in the sample code, or
use the following code:
SID LowIntegritySid = {
SID_REVISION, 1,
{SECURITY_MANDATORY_LABEL_AUTHORITY},
SECURITY_MANDATORY_LOW_RID;
Companion Content This books companion Web site includes a sample application
named SetMIC in the Ch02 folder to start a process at an equal or lower integrity level than
the user.
Important
Note that in the sample code we reject the request to launch an application if
the user has a full admistrative token. Doing so can lead to a potential elevation of privilege
vulnerability because the spawned process would be a low-integrity process running as an
administrator. You can spawn a low-integrity process from a high-integrity process, so long
as the high-integrity process is not executing with a full token.
Chapter 2
37
As a test, log on as an administrator, start a command shell at low integrity with the SetMIC
tool, and then try writing to the root directory with the following command:
echo "Hello, World!" > c:\test.txt
It will fail because the process performing the write operation is of a lower integrity level than
the object being written to (low versus high). Note that an account cannot create a process at
a higher integrity level than its own integrity level. If you attempt to do so, the call to SetTokenInformation will fail, and GetLastError will return 1314 (privilege not held) unless the process
identity has the SE_RELABEL_NAME privilege enabled.
S means a SACL.
The flags option describes optional control flags that indicate various protection and
inheritance options.
The inheritance option describes optional flags that control inheritance and inheritance
propogation.
38
The mask value is one of the following: NW (No-Write up), NR (No-Read up), or
NX (No-Execute up). The most common is NW. These settings are explained a little
later.
The level value is the actual integrity level: LW, ME, HI or SI for low, medium, high, and
system, respectively.
The following code shows how to set a No-Write up (NW), medium integrity level (ME) for an
object, in this case a file.
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
wchar_t *wszSacl = L"S:(ML;;NW;;;ME)";
if (ConvertStringSecurityDescriptorToSecurityDescriptor(
wszSacl,
SDDL_REVISION_1,
&(sa.lpSecurityDescriptor),
NULL)) {
wchar_t *wszFilename = L"c:\\files\\foo.txt";
HANDLE h = CreateFile(wszFilename,
GENERIC_WRITE,0, &sa,
CREATE_ALWAYS, 0,NULL);
LocalFree(sa.lpSecurityDescriptor);
if (INVALID_HANDLE_VALUE == h) {
wprintf(L"CreateFile failed (%d)", GetLastError( ));
} else {
CloseHandle(h);
h = NULL;
}
} else {
wprintf(L"SDDL Conversion failed (%d)", GetLastError( ));
}
You can look at or set the integrity level of a directory or file on an NTFS volume in Windows
Vista with the icacls command. For example, the following command shows that the
C:\Users\username\AppData\LowLocal folder used by Internet Explorer in protected mode
is set to low integrity:
C:\Users\michael\AppData>icacls LocalLow
LocalLow CONTOSO\Michael:(I)(OI)(CI)(F)
NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
BUILTIN\Administrators:(I)(OI)(CI)(F)
Mandatory Label\Low Mandatory Level:(OI)(CI)(NW)
You can set the integrity level of a file or directory using the /setintegritylevel argument.
Note
Chapter 2
39
40
A process (subject) integrity level must be equal to or higher than the integrity level of an
object to modify the object, assuming the normal NW mask is used.
A process can lower the integrity level of an object for which it can obtain modify access.
When a server impersonates a client on the same machine, the integrity level in the
impersonation token is the same as the integrity level of the client process.
Chapter 2
Tip
41
Note that the icacls tool will only set the NW integrity mask.
Low-integrity
level Internet
Explorer
Process
Administrative
Internet
Explorer
Process
Potentially
Malicious Web Site
Figure 2-7 A high-level view of Internet Explorer 7.0 Protected Mode in Windows Vista. The process
that parses potentially malicious content runs as a low-integrity process.
42
Note that a low-integrity process can send data to a higher-privilege process listening on an
RPC or LRPC endpoint. Security-aware processes should use client impersonation to determine if the caller has sufficient access to read or modify server managed resources. Also,
because sockets dont use ACLs (other than to open the port in the first place), you can send
data from a low-integrity process to a high-integrity process using sockets.
Important
Note
Integrity controls in Windows Vista are a very useful and easy-to-use defense. If your
application is one socket away from the Internet, you should consider adding an option to
enable your application to run at low integrity. This will help isolate any potential damage an
exploit could cause. You should limit which applications run at low integrity to only critical
applications that are likely to come under attack, otherwise in a pathological case, if all applications run at low-integrity and access low-integrity resources, then all applications can compromise all data!
File Warnings
File warnings show file system access issues, such as trying to write to the \Windows directory. In general, file I/O issues can be fixed by writing to user-writable directories, such as anything under %userprofile%.
Chapter 2
43
Registry Warnings
Registry warnings show system registry access issues, such as trying to write to any portion of
HKLM. These issues can be fixed by writing to HKCU.
INI Warnings
INI warnings show issues using WriteProfileXXX, which were originally used for 16-bit
Windows applications, but are still used in some applications today. Calls to WriteProfileXXX
and WritePrivateProfileXXX should be replaced with modern configuration functions, such as
the registry APIs. You can also remedy profile function issues by using the IniFileMapping
registry settings (Margosis 2006).
Token Warnings
Token warnings show token checking issues, such as analyzing a process token to determine
group membership. It is generally deemed bad practice to analyze a user token to determine
access policy. Rather, you should rely on the operating system to perform standard access
checks when it accesses resources.
Privilege Warnings
Privilege warnings show privilege issues, such as enabling the Debug Privilege, which is normally only accessible to administrators. Privilege issues are harder to fix, because chances are
good your application needs the privilege or privileges to operate correctly. If this is the case,
then your application is probably a good candidate to break into partswhere one or more
components perform the privileged operations and you launch these components through a
COM moniker or an elevated process.
Process Warnings
If you call CreateProcess or CreateProcessAsUser on an executable that is manifested as
RequireAdministrator or is flagged as an installer by Vista, your code will receive an
44
SE_TRUSTED_CREDMAN_ACCESS_NAME
(SeTrustedCredManAccessPrivilege)
SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE (31L)
This privilege is used by Credential Manager during Backup/Restore. No accounts should
have this privilege because it is only assigned to Winlogon. Users saved credentials might be
compromised if this privilege is given to other entities.
Chapter 2
45
SE_RELABEL_NAME (SeRelabelPrivilege)
SE_RELABEL_PRIVILEGE (32L)
Granted to no accounts by default, this privilege allows a user to set any integrity level, potentially violating the No-Write up integrity rule.
SE_INC_WORKING_SET_NAME (SeIncreaseWorkingSetPrivilege)
SE_INC_WORKING_SET_ PRIVILEGE (33L)
Granted to all users by default, this privilege allows a principal to increase the working set for
a process.
SE_TIME_ZONE_NAME (SeTimeZonePrivilege)
SE_TIME_ZONE_ PRIVILEGE (34L)
Granted to all users by default, this privilege allows a user to set the computers time zone, but
not the system time.
SE_CREATE_SYMBOLIC_LINK_NAME
(SeCreateSymbolicLinkPrivilege)
SE_CREATE_SYMBOLIC_LINK_ PRIVILEGE (35L)
This privilege determines if the user can create a symbolic link. Symlinks have been a source
of many Mac OS X, Linux, and Unix security bugs in the past [(CVE-2005-2714), (CVE-20061247), and (CVE-2006-4124)]. The mklink command requires this privilege, which is granted
only to administrators by default.
Call to Action
Windows Vista is here, and user accounts are no longer administrators by default, so
make sure your application runs correctly as a standard user.
Do not, under any circumstances, recommend that your customers disable UAC.
If your application runs successfully as a normal user account simply because of folder
and registry virtualization, you should plan to fix your code as soon as possible, because
virtualization will be removed eventually.
Do not use the fact that your application can write to the Program Files directory as a
sign your application is running as an administrator or power user. It was true in most
46
cases in Windows XP that a user writing the Program Files was an elevated user, but it
is not the case in Windows Vista because of virtualization, because virtualization only
applies to non-administrator processes.
It is highly recommended that you download the Standard User Analyzer tool, run your
application through it, and triage the output. Any warning could indicate issues that may
make your application fail to run correctly as a low-privilege user.
If your application is one socket away from the Internet, consider lowering the integrity
level of the process to Low.
References
Russinovich, Mark, and David Solomon. Microsoft Windows Internals
4th Edition. Redmond, WA: Microsoft Press, 2005.
(Russinovich 2005)
(Microsoft 2006a) The computer may restart when you add a manifest that has the
Windows Vista extension to an .exe file or to a .dll file in Windows XP Service Pack 2
(SP2), http://support.microsoft.com/Default.aspx?kbid=921337. October 2006.
The COM Elevation Moniker, http://msdn2.microsoft.com/en-us/library/
ms679687.aspx. MSDN 2006.
(MSDN 2006a)
(Wille 2007)
http://chrison.net/UACElevationInManagedCodeANETCOMComponentElevated.aspx.
February 2007.
Application Compatibility Tools, http://technet.microsoft.com/en-us/
windowsvista/aa905078.aspx.
(Microsoft 2006b)
(Microsoft 2006c)
(Microsoft 2006d)
and AIX 5.3.0 allows local users to overwrite arbitrary files, http://cve.mitre.org/cgi-bin/
cvename.cgi?name=CVE-2006-1247.
(CVE-2005-2714) Common Vulnerabilities and Exposures. Passwd in Directory Services in
Chapter 2
47
(CVE-2006-1247)
(CVE-2005-2714)
(CVE-2006-4124)
(Wikipedia 2006)
(MSDN 2002)
Chapter 3
50
study and debate, and this book cant possibly address the topic thoroughly enough. A good
reference on the topic is Bugs or Defects? (Humphrey 1999). The numbers quoted here are
reasonably close to those reported by various researchers. For example, Humphrey cites
figures from three different projects from the Jet Propulsion Laboratory. Despite rigorous
development practices, from 6.5 to 9 defects/kloc were found after the programs were compiled, unit tested and integration tested. An average developer with average development practices will make 50 to 100 mistakes/kloc, many of which may be caught by compiler warnings
and testing. Code review, education, experience, and static analysis tools can drive the actual
count to the lower portion of the range. Fuzzing, or better yet, targeted test harnesses to validate the code, will also drive down the defect count. Unfortunately, you arent likely to drive
defects to zero for any non-trivial application, and the closer to zero defects you try to get, the
more expensive the code becomes to create, so it might be best to just accept that there will be
the occasional error.
Mitigations
% Exploitable Defects
Code Quality
Improvements
Figure 3-1
Out of the defects that do exist in the code, some will be exploitable. Even the most trivial
defenses can eliminate some attacks. For example, an off-by-one overwrite of a stack buffer
used to be typically exploitable, but with the introduction of stack protection, these are now
stopped. The techniques we discuss in this chapter are all efforts to ensure that some of the
errors that escape us dont result in an exploitable condition.
The second point wed like to make is that Microsoft didnt actually pioneer much of this, and
this text does not claim thats the case. What we are doing is delivering these features in an
extremely widely used, general-purpose operating system while doing our best to maintain
backward compatibility and good performance with an extremely wide range of applications.
Chapter 3
51
ASLR
Address Space Layout Randomization (ASLR) is a technique that makes it more difficult for an
exploit to locate system APIs, which in turn makes it harder to leverage system APIs in exploits
to run arbitrary code. The approach involves randomizing where system libraries are loaded,
all of the starting points of the stack, and the starting points of the heaps. On a Windows XP
system, all of these are known to an attacker and may vary somewhat depending on the
service pack level of the system. There are even references available on the Web that document this information by version and service pack level.
ASLR includes randomizing all of the following elements of a running program:
Image randomization
Stack randomization
Heap randomization
The addresses where the executable and DLLs are loaded varies
One common attack is to call LoadLibrary to force the application to load a DLL. The assembly
needed to do this is very trivial, and amounts to:
Push location of path buffer
Call location of LoadLibrary
Thus, if the attacker can write a path into a buffer with a known locationwhich need not
involve an overflowand then redirect execution into a spot where a very small amount of
shell code is located, your application will load a library, possibly across the network, and
invoke DllMain. As an attacker, this is a handy technique because a very complex attack can
be written in a high-level language, while the minimum size of the shell code needed becomes
very small.
ASLR can thwart such an attack by eliminating preconditions needed by the attacker. The
first problem the attacker encounters is that the address to be jumped into needs to be
known, although not exactly. If an attacker has a large buffer to work with, writing a large
number of NOP (no-op) instructions into a buffer creates a NOP slide, and the instruction
pointer only needs to be redirected to somewhere in the slide. Next, the address of the
library path needs to be known exactly, and so does the address to call the LoadLibrary function. If the starting point of the stack isnt exactly the same at all times, then finding the
buffer with the path is more difficult and so is figuring out where to find the shell code.
Finally, if LoadLibrary cant be counted on to be in the same place at all times, this will pose
additional difficulties.
Heres an application you can use to take a look at how ASLR and stack randomization work
on your system:
#include <windows.h>
#include <stdio.h>
52
The test app was written using the retail shipping version of Visual Studio 2005, Service Pack 1,
and the /dynamicbase linker option, released in Visual Studio 2005, Service Pack 1. Here we
find that LoadLibrary shows up in different places across reboots of the system:
First test:
Kernel32 loaded at 77AC0000
Address of LoadLibrary = 77B01E7D
Second test:
Kernel32 loaded at 77160000
Address of LoadLibrary = 771A1E7D
What is also of interest is that the location of the C runtime library doesnt changeit loads at
address 0x78130000 across both reboots. The C runtime library this application is dynamically
linked with was linked with an older linker that did not have the /dynamicbase capability, and
it wont utilize image randomization. If Id gone to the trouble to load the entire Visual Studio
update with the new C run-time DLL on my system, we wouldnt see this problem, but it does
show us something to be careful ofmany applications are built using third-party components,
and if all of the libraries your vendor supplies arent also rebuilt using the new flag, then they
Chapter 3
53
wont be relocated and will counteract many of the benefits of ASLR. If its a library that comes
with source, be sure to rebuild it yourself to get the benefits of ASLR, although you must also be
careful to test and ensure that the change doesnt break something. Most DLLs wont be disturbed by ASLR, but this opt-in behavior is designed to keep applications from randomly breaking when upgrading to Vista.
Limitations of ASLR
ASLR is performed systemwide, once per reboot. One note is that if all of the processes using a
particular DLL unload ASLR then it would be loaded in a random place on the next load, but
many of the system DLLs are always loaded by several processes and only get randomized at
the next reboot. If youre dealing with a local attack, an attacker could easily determine
addresses, and then launch the attack. This feature helps inhibit network-based attacks, especially worms. If an application has an information disclosure vulnerability (where it reveals
exception details to an attacker) or has a format string vulnerability (which would allow the
values on the stack to be read), it may be possible for an attacker to learn the memory locations
needed to overcome this mitigation. An attacker-controller read might also be used to place
information needed for an attack into either a known location or a location with a known
offset. Another implication is that if the network service restarts itself on failure, the attacker
now has more chances to find where to call the system API, which is why we recommend that
services be configured to restart automatically a small number of times. To reduce virtual
address space fragmentation, the library is only relocated across 256 different possible load
addresses; note that the randomization is in the second-most significant byte of the address.
54
use as much memory as possible. With all the DLLs being loaded into a relatively contiguous
space, theres effectively more memory available for other purposes. Having the memory
addresses relatively close to one another can sometimes help the processor cache perform
more effectively.
As implemented in Windows Vista, the compatibility implications of ASLR arent much of an
issue. Microsoft Office 2007 is a very large code base, and has a wide variety of programming
practices and techniques. When Office enabled ASLR and ran a test pass, we didnt find any substantial performance degradation, and no regressions. Although there may be things a developer could do to break themselves if ASLR were enabled, like hard-code function addresses, if all
of Office can turn this on with remarkably few problems, most apps should be in great shape.
Stack Randomization
Stack randomization changes the base address for every thread, which makes it more difficult
for an attacker to find a place to jump to within an application. For example, assume the
attacker wants to make a call to system, but the C runtime DLL is loaded in a random location
that your code also calls system. If the application code is loaded at predictable addresses, the
attacker can then just jump to where you called system yourself. Obviously, a real attack would
also include setting up arguments and buffers, and some serious mistakes would need to be
made before an attacker could have this much control.
Using the sample program listed in the ASLR section, you get the results shown in Table 3-1.
Table 3-1
Address of main
00281020
00F41020
00281000
00F41000
Address of g_GlobalVar
0028336C
00F4336C
Address of StackBuffer
001EFBC4
002EF8BC
To get this protection, you have to use /dynamicbase in your linker options. Note that even
though the starting address of the executable command is randomized, the offset between the
various code elements remains constant. In this example, the offset between the entry point of
main() and foo() is 32 bytes (0x20). If you have any important information stored in global
variables, these addresses will be randomized as well. While it is generally a bad practice to
store things such as function pointers in global memory, if you do, this mitigation will make
your code more difficult to attack. Encoded pointers, which are discussed in Chapter 9, Miscellaneous Defenses and Security Technologies should also be used.
Even though the address of the stack buffers has always been somewhat unpredictable in multithreaded applications, when using this mitigation we find that the location of the stack for
the main thread is randomized; an interesting aspect to take note of is that the offset between
Chapter 3
55
the stack and the main modules code isnt fixed from one instance of the application to the
next. If weve depriving the attackers of fixed addresses, knowledge of offsets becomes even
more important, and theres no longer a relationship between the stack addresses and the
code addresses.
Heap Defenses
In the last several years, heap exploits have gone from exotic to typical. Countermeasures such
as /GS (explained later in this chapter) and better programming practices have made stack
overruns less frequent, but the heap is actually easier in some ways to exploit. First, the size of
the allocation is typically determined using an arithmetic calculation, and both computers
and programmers can be very bad at mathalthough computers are faster and more predictable. For a more comprehensive look at how computers can mangle integer manipulation,
take a look at Integer Handling with the C++ SafeInt Class (LeBlanc 2004), and Another
Look at the SafeInt Class (LeBlanc 2005).
To make matters worse, common behaviors for a heap make unreliable exploits work more
often. Lets say that you can get execution flow to jump into some spot in the heap, but you
cant control exactly where it will go. One obvious attack is to put an enormous amount of data
into the heap and use a very large NOP slide that causes execution of anything in the NOP slide
to run down into your shell code. It isnt always possible to put very large amounts of data into
the heap, so an alternate approach is a technique known as heap spraying, which is a technique first noted in eEyes advisory about the IDA overflow (eEye 2001), which was well
known because it was developed into the Code Red worm in 2001. Heap spraying involves
writing a large number of copies of your shell code into the heap; depending on the details of
the exploit, you would be much more likely to have the execution jump into the shell code
because large allocations would typically end up in another location. If youre faced with executing legitimate allocations, none of the defenses discussed in the remainder of this section
apply, but NX (No eXecute) will stop these attacks cold. NX will be covered in the next section.
Another problem exploits a very common programming flawdangling pointers and
double-free conditions. Mike Marcelais of the Office Trustworthy Computing team came up
with the following three common double-free conditions that can be dangerous (personal
communication):
The first common double-free problem is when the heap structures in a way that an attacker
can exploit. If the heap manager does not maintain the control data adjacent to the user data,
this attack may not be possible. The base Windows heap in Windows Vista has also been
hardened against this attackmore on this in a moment.
56
There is a pattern of alloc(a), free(a), alloc(b), free(a), alloc(c) all on the same address, as illustrated by the following code:
char* ptrA = new char[64];
// some code here
delete[ ] ptrA;
// now we need some more memory
char* ptrB = new char[64];
// Note that ptrB has the same value as ptrA
// Some more code, just to confuse things
// And now we make a mistake
delete[ ] ptrA;
// Oops! We just freed ptrB!
// Now we need more memory
char* ptrC = new char[64];
// ptrC will now be used to write memory that
// the code dealing with ptrB thinks is validated
The second condition is that efficient heap behavior reallocates recently freed memory that is
the same size. In this case, the function that requested alloc(b) has a pointer to memory controlled by the function that called alloc(c). If the attacker can control the memory written into
alloc(c), this situation is very exploitable, and there isnt anything an allocator can do to prevent this from causing problems. An obvious attack here is that function b validates user
input and copies it to allocated memory. Function c then copies something else over the
validated input, and then a subsequent operation is performed on the data in allocation b.
A third attack is a pattern of alloc(a), free(a), alloc(b), use(a), free(a), as shown here:
char* ptrA = new CFoo;
// Some code, and we then delete allocation A
delete[ ] ptrA;
// Now we need another allocation the same size
// Note that ptrA and ptrB point to the same memory
char* ptrB = new CFoo;
// Copy some data into ptrB
// Do something with ptrA, not knowing that ptrB has changed things
// If ptrA is a class, this includes calling the destructor
delete[ ] ptrA;
Note that if allocation a contains an object with a destructor, thats equivalent to using the
memory. In this case, the code using ptrB is changing the contents of the buffer pointed to by
ptrA, while ptrA is believed to have validated data. There is some potential for the usage of ptrB
to attack ptrA, and in this case, the converse is true as wellthe usage of ptrA could very easily
cause the data kept in ptrB to become invalid.
One good programming practice prevents this type of error from being exploitable: always set
pointers to null when freeing them, although this still wont help if there are multiple copies
of the same pointer. Then the use(a) step will cause a null dereference crash; in the previous
example, freeing or deleting a null pointer is benignnot only does it not crash, but the pattern of alloc(a), free(a), alloc(b), free(a), alloc(c) becomes non-exploitable as well, since calling
Chapter 3
57
delete on a null pointer doesnt do anything, and functions b and c would then have allocations in different places. The following C++ inline functions would help:
template < typename T >
void DeleteT( T*& tPtrRef )
{
assert( tPtrRef != NULL );
delete tPtrRef;
tPtrRef = NULL;
}
// Use when allocation is new T[count]
template < typename T >
void DeleteTArray( T*& tPtrRef )
{
assert( tPtrRef != NULL );
delete[ ] tPtrRef;
tPtrRef = NULL;
}
The reason these functions must be templatized is that for delete or delete[] to properly call
object destructors, the object type must be known. A debugging assert will help catch and fix
these conditions because, at run time, the second delete will be benign and without the assert,
you wouldnt catch the double-free problem, which could be a symptom of other serious
errors. While the heap manager cant protect you against the last two problems listed here,
some of the other countermeasures might help. Our advice is that all double-free bugs should
be fixed. Another good approach is to use smart pointer classes, although the behavior of the
class must be understood before it is used.
In the case of a heap overrun, the effects depend on the heap manager being used. The
default Windows heap places control data immediately before and after every allocation, and
attackers can target both the control data and data kept on the heap in adjacent allocations.
A number of researchers have found ways to attack the default Windows heap, and improvements in the Windows Vista heap have been numerous. Heres a partial list of recent
improvements:
Checking validity of forward and back links A free block has the address of the previ-
ous and next free blocks stored immediately after the block header. Basically, the value
of the forward link turns into the value to write, and the value of the backward link is
where to write the forward link value. This leads to an arbitrary 4 bytes (on a 32-bit system) being written anywhere in memory. The change is to check that the structures at
those locations properly point back to where it started. This improvement was delivered
in Windows XP SP2.
Part of the block header is XORd with a random number, which makes determining the value to overwrite very difficult. The performance
impact is small, but the benefits are large.
Block metadata randomization
58
Heap base randomization This was mentioned earlier in the ASLR section.
Heap function pointer randomization Function pointers used by the heap are
encoded. This technique will be discussed at greater length in Chapter 10.
Basically, we must expect the abilities of the attackers to continue to improve, but we must
also expect the defenders to continue to improve. Any list of attacks and countermeasures we
can give you is probably going to be out of date by the time you read this book. What is important is knowing how to protect yourself and how to leverage the capabilities of the operating
system to help protect your customers.
The first heap countermeasure thats new to Windows Vista is enabling application termination on heap corruption in your application, although it is possible for the exploit to happen
before the heap manager notices corruption. In older versions of the heap, the default behavior when the applications heap became corrupt was to just to leak the corrupted memory
and keep running, even in the face of poorly behaved code. For example, heres something
guaranteed to cause problems:
char* pBuf = (char*)malloc(128);
char* pBuf2 = (char*)malloc(128);
char* pBuf3 = (char*)malloc(128);
memset(pBuf, 'A', 128*3);
printf("Freeing pBuf3\n");
free(pBuf3);
printf("Freeing pBuf2\n");
free(pBuf2);
printf("Freeing pBuf\n");
free(pBuf);
On Windows Vista, even without the heap set to terminate on corruption, this code wont get
any further than the first call to free before it causes the application to abort. On earlier versions of the operating system, including Windows Server 2003 and Windows XP, it executes
all the printf statements and exits normally. Note that this has to be tested with release builds,
because the debug heap does extra checking.
Its always better to crash than to run someone elses shell code, but your customers wont
appreciate crashing either. Enabling terminate on corruption for your processs heap should be
done early in your development cycle to give you time to shake out any previously benign
bugs. Additionally, if your application can host third-party code in some form, such as plugins, you may want to think about getting the third-party code out of your process. A surprisingly large number of the crashes in Microsoft Office and Internet Explorer are due to code
that isnt shipped by Microsoft. To enable the heap to terminate the application on corruption, simply add this code snippet to your applications main or WinMain function:
Chapter 3
59
bool EnableTerminateOnCorrupt()
{
if( HeapSetInformation( GetProcessHeap(),
HeapEnableTerminationOnCorruption,
NULL,
0 ) )
{
printf( "Terminate on corruption enabled\n" );
return true;
}
printf( "Terminate on corruption not enabled - err = %d\n",
GetLastError() );
return false;
}
Obviously, you wouldnt leave diagnostic printf statements in your shipping code, so handle
errors however you like. Wed suggest making an unusual failure such as this an exceptionor
this could be a protection that is only enabled on Vista and just ignore errors when running
an earlier version of Windows.
An additional countermeasure is that the low fragmentation heap (LFH) is historically more
resistant to attack than the standard Windows heap. Why not use the LFH all of the time? The
answer is that heap performance is very dependent on how an application uses the heap, and
in fact, Vista may decide to use the LFH at run time if usage patterns make it beneficial. Before
shipping code with the LFH, use performance benchmarking to see if theres an improvement
or a decrease in performance. If the LFH works well with the application, heres how to enable
it:
bool EnableLowFragHeap( )
{
ULONG ulHeapInfo = 2;
if( HeapSetInformation( GetProcessHeap( ),
HeapCompatibilityInformation,
&ulHeapInfo,
sizeof( ULONG ) ) )
{
printf( "Low fragmentation heap enabled\n" );
return true;
}
printf( "Low fragmentation heap not enabled - err = %d\n",
GetLastError( ) );
return false;
}
NX
NX, short for No eXecute is known by several names. Intel calls it the Execute Disable or
XD-bit, AMD calls it Enhanced Virus Protection, and its also been written as W^X, which
translates to write or execute, but not both. Windows refers to NX as Data Execution Prevention, and the settings can be found using the System Control Panel applet, under Advanced,
60
Performance Options. Non-executable stacks and heaps arent especially newSuns Solaris
operating system had a setting to enable a non-executable stack several years ago, and OpenBSD moved to NX in OpenBSD v3.3 and later. In the Windows line of operating systems, NX
has been available since Windows Server 2003 shipped and then was back ported to
Windows XP in Service Pack 2. An indication of just how far back NX was anticipated is that
one of the flags to the VirtualProtect function, as it originally was documented back in 1993
when Windows NT 3.1 shipped, was a flag to set whether a page was executable. Since the
x86 family of processors didnt support NX on a per-page basis until recently, the flag had no
effect on x86 processors until some additional work was done to detect NX support at the
processor level (David Cutler, personal communication). It didnt have an effect on some of
the other processors originally supported by Windows NT, but it is interesting to note that the
architects of the operating system anticipated this need.
By default the majority of core operating system components support NX. You can see if a component is protected by NX by looking at the process under Vistas Task Manager; its an optional
column named Data Execution Prevention. Although the dialog box that allows you to configure
NX refers to software NX as a fallback, software NX doesnt protect much more than exception
handlers and only handles a small subset of the attacks that real hardware NX will stop.
The concept here is simple: If a page of memory, whether it is on the stack or the heap, is writable, we ought not be executing code from that page. The vast majority of the exploits seen
today will execute code from either the heap or the stack. You might wonder why NX isnt a
sufficient protection against malwaretake a look at Bypassing Windows Hardware-enforced
Data Execution Prevention (Skape and Skywing 2005).
Bypassing NX
Basically, there have to be ways to enable executable memory. One example would be
when we load a DLL after process initialization. The operating system has to allocate
pages and write the instructions into process memory, and the system expects to be able
to execute these instructions once done. If a piece of shell code could first cause VirtualProtect to be called with the correct parameters, NX is then defeated. As it turns out,
calling VirtualProtect, with all the correct parameters in place, is a little difficult due to
needing to write very arbitrary valueseven on systems without ASLR protection. Matt
Miller (aka Skape) of the Metasploit project, and Ken Johnson (aka Skywing) point out
that it is also possible to call NtSetInformationProcess to disable NX for an entire process, unless the application has been compiled with /NXCOMPAT. This functionality
allows for backward compatibility and allows an application to continue to work if it
happens to load a DLL that isnt compatible with NX protection.
As of this writing, the combination of NX and ASLR appears to stop all but the most contrived
attacks, but were not foolish enough to claim that this set of protections is invincible (and
Chapter 3
61
most certainly not unbreakable). Lets take a look at how this feature works in a small snippet
of sample code:
#include <stdio.h>
#include <string.h>
// win32_exec - EXITFUNC=seh CMD=calc.exe Size=164
// Encoder=PexFnstenvSub http://metasploit.com
const unsigned char scode[] =
"\x33\xc9\x83\xe9\xdd\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xc9"
"\xfa\x9b\x8c\x83\xeb\xfc\xe2\xf4\x35\x12\xdf\x8c\xc9\xfa\x10\xc9"
"\xf5\x71\xe7\x89\xb1\xfb\x74\x07\x86\xe2\x10\xd3\xe9\xfb\x70\xc5"
"\x42\xce\x10\x8d\x27\xcb\x5b\x15\x65\x7e\x5b\xf8\xce\x3b\x51\x81"
"\xc8\x38\x70\x78\xf2\xae\xbf\x88\xbc\x1f\x10\xd3\xed\xfb\x70\xea"
"\x42\xf6\xd0\x07\x96\xe6\x9a\x67\x42\xe6\x10\x8d\x22\x73\xc7\xa8"
"\xcd\x39\xaa\x4c\xad\x71\xdb\xbc\x4c\x3a\xe3\x80\x42\xba\x97\x07"
"\xb9\xe6\x36\x07\xa1\xf2\x70\x85\x42\x7a\x2b\x8c\xc9\xfa\x10\xe4"
"\xf5\xa5\xaa\x7a\xa9\xac\x12\x74\x4a\x3a\xe0\xdc\xa1\x0a\x11\x88"
"\x96\x92\x03\x72\x43\xf4\xcc\x73\x2e\x99\xfa\xe0\xaa\xd4\xfe\xf4"
"\xac\xfa\x9b\x8c";
typedef void (*RunShell)( void );
int main( int argc, char* argv[] )
{
char StackBuf[256];
RunShell shell = (RunShell)(void*)StackBuf;
strcpy_s( StackBuf, sizeof( StackBuf ), (const char*)scode );
(*shell)();
return 0;
}
This code sample is an example of just how ridiculously easy it has become for anyone to
obtain completely arbitrary shell code to do whatever they like. All it took to get this string of
shell code was to go to www.metasploit.com, download and install their framework, go to their
Web interface, and then choose from the menu items. Once compiled and run, a running
instance of the Windows calculator appears, and the application will then crash because the
shell code is written to cause an exception once the command line is executed. Another
option is to get the shell code to call ExitProcess, which will cause the application to exit
cleanly.
If /NXCOMPAT is added to the linker options, and if the system supports NX (that is, DEP or
XD), and if the demo code is run again from the development environment, a message is generated that looks like this:
Unhandled exception at 0x0012fe40 in NxTest.exe: 0xC0000005: Access violation.
A quick look at the exception address shows that 0x0012fe40 is the address of StackBuf; note
that there are no extra instances of calc.exe on the desktop. The system may indicate that the
processor doesnt support NX when there is actually a new processor that should support
NX. If thats the case, check the BIOS options. It seems that many BIOS manufacturers are
62
defaulting NX to off. If the computer does have NX disabled in the BIOS, talk to the hardware
vendor and ask that it be enabled by default for all their computers. Even with hardware NX
disabled there may still be the weaker software-based protection, which ironically enough,
wont stop the preceding example from executing because there is no buffer overrun corrupting an exception handler.
Its also interesting to see what Windows Vista does with this application when it is simply
invoked from the command line. It first tells us that NxTest.exe has stopped working. If we
then tell it to close, a help balloon pops up telling us that Data Execution Prevention has
stopped the program, which then explains that weve just stopped a security-related flaw.
You must test your application with NX enabled because your customers are going to do it for
you, sometimes without knowing what the problem could be, which is that NX is enabled
differently on the server line of operating systems and the desktop versions. On the client
versions (Windows XP and Vista), the default is that only operating system components are
opted in; no other applications have NX enabled. On the server, all applications, except those
in the opt-out list, have NX enabled. While thats the default, nervous security administrators
could change the default for clients to the setting for the servers. If this isnt tested, there could
be seemingly random failures as people upgrade to hardware that supports NX and has it
enabled in the BIOS.
Currently, the NXCOMPAT flag in the header is ignored by any Windows operating system
prior to Windows Vista. On Windows Vista, if NXCOMPAT is set in the linker options, then
the application will be running with NX regardless of the settings at OS level (once hardware
NX is available). If NXCOMPAT:NO is set, then NX will not apply to the application. Just like
ASLR and the heap settings, NXCOMPAT is set processwide; if youre hosting third-party
code, this may cause some problems and is another reason to find a way to get third-party
code into its own process.
Chapter 3
63
of the process. Getting other peoples code out of your process may give you some stability
benefits, assuming you write better code than they do!
Heres a handy class that you can use if you do need to execute code on the fly and would like
to do it safely:
class WriteThenExecute
{
public:
WriteThenExecute() : pMemory( NULL ),
fIsExecutable( false ),
cbMemory( 0 )
{}
~WriteThenExecute()
{
if( pMemory != NULL )
VirtualFree( pMemory, 0, MEM_RELEASE );
}
void Allocate(SIZE_T dwSize = 4096 )
{
pMemory = VirtualAlloc( NULL, dwSize,
MEM_COMMIT, PAGE_READWRITE );
if( pMemory == NULL )
throw GetLastError();
cbMemory = dwSize;
}
void CopyBytes( BYTE* pBytes, SIZE_T cbBytes )
{
if( cbBytes <= cbMemory )
{
DWORD dwPrevProtect;
CopyMemory( pMemory, pBytes, cbBytes );
if( VirtualProtect( pMemory, cbMemory,
PAGE_EXECUTE_READ, &dwPrevProtect ) )
{
fIsExecutable = true;
return;
}
throw GetLastError();
}
throw ERROR_INSUFFICIENT_BUFFER;
}
template< typename T >
T GetFunctionPtr()
{
if( fIsExecutable )
return reinterpret_cast< T >( pMemory );
return reinterpret_cast< T >( NULL );
}
private:
64
The small main function demonstrates how you might use this class. Its fairly simplejust
allocate as much memory as youll need, copy in the assembly youd like to run, and then get
a function pointer. There are a couple of issues wed like to bring up before you put this code
into production. First, some asserts ought to be sprinkled liberally through the code. Calling
Allocate more than once will leak memory and corrupt internal state; calling CopyBytes more
than once will cause you to throw an exception; and the default copy constructor and assignment operator ought to be declared private with no implementation, because copying this
class to another instance would cause memory leaks or double-free conditions. Second, its
also a good idea to make a Release, and possibly a MakeWritable method that would reset the
class to its original state. Although we throw DWORDs as exceptions in many places in the
examples, you shouldnt do this in production codeuse std::exception, or another dedicated
exception class instead. Finally, the class isnt thread safe.
/GS
The /GS flag has been around since the release of Visual Studio 7.0. In the original approach,
it was simply a randomly generated cookie placed between the return address and the local
variables on the stack. If the EBP register was pushed onto the stack, the cookie would guard
this as well. It would stop some of the simplest attacks against simple functions, and, although
it was better than nothing, no developer should ever believe that theyre protected against
attacks merely because of /GS. Lets take a look at a sample application:
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <string.h>
void VulnerableFunc( const char* input, char* out )
{
char* pTmp;
char buf[256];
strcpy( buf, "Prefix:" );
strcat( buf, input );
// Transform the input, and write it to the output buffer
for( pTmp = buf; *pTmp != '\0'; pTmp++ )
{
*out++ = *pTmp + 0x20;
Chapter 3
65
}
}
int main( int argc, char* argv[] )
{
char buf2[256];
VulnerableFunc( argv[1], buf2 );
printf( "%s\n", buf2 );
return 0;
}
Starting just after we enter main, heres the commented disassembly (my comments above the
instructions):
int main( int argc, char* argv[] )
{
// Save the previous value of the frame pointer on the stack
00411300 push
ebp
// Put the current stack pointer into the ebp register
00411301 mov
ebp,esp
// Create room for buf2
00411303 sub
esp,140h
// Save three more registers on the stack
00411309 push
ebx
0041130A push
esi
0041130B push
edi
char buf2[256];
VulnerableFunc( argv[1], buf2 );
// Get the address of buf2, and put it on the stack
0041130C lea
eax,[buf2]
00411312 push
eax
// Find the value of argv[1], also place it on the stack
00411313 mov
ecx,dword ptr [argv]
00411316 mov
edx,dword ptr [ecx+4]
00411319 push
edx
// Call VulnerableFunc, and on return, adjust the stack pointer
0041131A call
VulnerableFunc (41102Dh)
0041131F add
esp,8
66
In this example, all optimizations and stack checking are disabled so that you can see what typical applications used to do. In this application, the stack is shown by the diagram in Figure 3-2.
buf
pTmp
previous frame pointer
return address to caller
value of input pointer
value of out pointer
ebx, esi, and edi
registers
buf2
Figure 3-2
Now lets look at what happens, step by step, as buf overflows. The first value to get mangled
is pTmp, but that wont get us anywhereit will just be initialized to zero and incremented as
we process the buf character array. The next value to get overwritten is the previous frame
pointer. This is interesting because on return from the second function (in this case, main),
code execution will jump into the address located here. If we had the classic off-by-one attack,
this is where the fun starts.
Next, things start to get really fun: we can now overwrite the return address and set the input and
out values to anything we like. Setting input isnt especially interestingby the time the overrun
has happened, weve already used that variable and wont write it again. The out parameter is a
real problem, because once that is overwritten the attacker can write nearly anywhere in application memory that theyd like once we fall into the for loop toward the end of VulnerableFunc.
In the original /GS implementation, a cookie was just placed between the local variables and
the stored registers (if any) and the return address. This would leave pTmp open to attack, and
although in this case, it is uninteresting, that may not always be true, especially if this were a
function pointer. In the case of a limited overwrite, it isnt possible to tamper with the previous frame pointer without detection, so /GS in its simplest incarnation would stop a simple
off-by-one (or even off-by-a-few) overflow from being exploitable. In the case of an unchecked
overrun, the attacker has the problem of having to fix up the cookie, but recall that overwriting the out pointer allows the attacker to put nearly any value he or she would like into any
memory location (although in this example, a location with any 0 bytes would be a problem).
Lets take a look at what the most recent version of /GS will do with the same function:
Chapter 3
67
As you can see, this implementation is much safer. The arguments to the function are either
copied into registers or are put on the stack above the buffer. Additionally, as we discuss in the
next section, if an exception were thrown inside this function, the cookie would be checked
prior to executing the exception handler.
Note
While you should always use /GS, you still need to write solid code!
SafeSEH
One of the most significant flaws in the original implementation of the /GS flag was found by
David Litchfield. In any Windows application, there will be at least one, and possibly several,
structured exception handlers (SEH). While we dont have room for a thorough review of how
exception handlers work and when you should be using them, SEH understand three keywords: __try, __except, and __finally. If youd like to learn more about exception handlers, look
up the Structured Exception Handling topic in MSDN, and one of the best explanations is
in Jeffrey Richters Advanced Windows books. Any edition will do, although unfortunately, the
books are currently out of print.
The __try keyword is almost exactly analogous to the C++ try keyword and declares that a
block of code has an exception handler. The __except keyword declares a block that behaves
similarly to a block declared with catch in C++. Unlike C++, you dont catch thrown exceptions
by type, but you can analyze the exception record to determine whether youd like to handle
the exception. You can choose to continue execution after the handler, do something to fix the
problem and resume execution, or tell it to continue to search for an applicable exception
handler. A __finally block is a way for a C program to behave very similarly to how a C++
68
application would use destructors. No matter how you exit the __try block, the __finally block
is guaranteed to be executed.
We cant say strongly enough that you need to be exceptionally careful when using SEH or
C++ exception handling. Weve both seen bad code along these lines:
__try
{
// We're not really sure if this will work
memcpy( dst, src, size );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
// I guess something went wrong. Oh well, exceptions are kewl
}
Do NOT do this in your code! This sort of code is a great way to get hacked, even if you are using
the /SafeSEH flag. So lets take a look at what can go wrong with SEH, and why it became a problem for the /GS flag. When an exception handler is registered for a block of code, an
EXCEPTION_REGISTRATION structure is pushed onto the stack. The EXCEPTION_ REGISTRATION structure contains a pointer to the next EXCEPTION_REGISTRATION structure, and
the address of the current exception handler. Although the amazingly clever Mr. Litchfield has
written a 16-page document detailing it (Litchfield 2003), the problem is essentially that you
have a function pointer sitting on the stack just waiting to get overwritten. If the attacker can
overwrite the buffer far enough, the function pointer to the current exception handler gets overwritten with the address of the attackers choice. The only thing remaining is to cause an exception before the function exits normally. This problem is why attacks that write a very large
amount of information outside of the buffer are frequently exploitable. Once the attacker provokes an exception, the exception handler is called, and because the function hasnt returned,
the security cookie isnt checked, and were now running arbitrary code. Where some of the previously reported flaws in /GS depended on contrived code with multiple problems to bypass
stack protection, this attack works regardless of the code internal to the function, as long as the
overwrite extends far enough to hit the exception handler, or is an arbitrary DWORD overwrite,
and if an exception can be caused prior to the function exiting normally. As mentioned in the
previous section, the Visual Studio 2005 compiler treats calling an exception handler as if the
function had exited (which is often the result of calling an exception handler) and checks the
security cookie prior to executing the handler. While Litchfields attack is thwarted, an arbitrary
DWORD written on top of an exception handler will still cause problems unless we use SafeSEH.
Heres an application we can use to demonstrate both the problem and the various solutions:
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <stdio.h>
win32_exec - EXITFUNC=process CMD=calc.exe Size=164
Encoder=PexFnstenvSub http://metasploit.com
unsigned char scode[] =
"\x33\xc9\x83\xe9\xdd\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x46"
Chapter 3
"\x12\x29\x79\x83\xeb\xfc\xe2\xf4\xba\xfa\x6d\x79\x46\x12\xa2\x3c"
"\x7a\x99\x55\x7c\x3e\x13\xc6\xf2\x09\x0a\xa2\x26\x66\x13\xc2\x30"
"\xcd\x26\xa2\x78\xa8\x23\xe9\xe0\xea\x96\xe9\x0d\x41\xd3\xe3\x74"
"\x47\xd0\xc2\x8d\x7d\x46\x0d\x7d\x33\xf7\xa2\x26\x62\x13\xc2\x1f"
"\xcd\x1e\x62\xf2\x19\x0e\x28\x92\xcd\x0e\xa2\x78\xad\x9b\x75\x5d"
"\x42\xd1\x18\xb9\x22\x99\x69\x49\xc3\xd2\x51\x75\xcd\x52\x25\xf2"
"\x36\x0e\x84\xf2\x2e\x1a\xc2\x70\xcd\x92\x99\x79\x46\x12\xa2\x11"
"\x7a\x4d\x18\x8f\x26\x44\xa0\x81\xc5\xd2\x52\x29\x2e\x6c\xf1\x9b"
"\x35\x7a\xb1\x87\xcc\x1c\x7e\x86\xa1\x71\x48\x15\x25\x3c\x4c\x01"
"\x23\x12\x29\x79";
char* pHeapShell;
// Function to demonstrate overwriting a structured exception
// handler.
void foo( int elements )
{
char* ptr = NULL;
char buf[ 32 ];
int i;
// For loop to cause the overwrite.
// The value written into each byte is
// incremented to make it easier to determine where
// the exception handler address is located
for( i = 0; i < elements; i++ )
{
buf[i] = 0x20 + i;
// In this particular piece of code, the
// exception pointer starts at an offset of 0x30
// bytes from the start of the buffer
// Note if this is compiled with a newer
// compiler, the offset might be different
// on my pre-release version, the test should be
// for buf[i] == 0x58
if( buf[i] == 0x50 )
{
DWORD* pdw = (DWORD*)( buf + i );
// To try and execute an exception handler
// that isn't on the heap, change this to:
// *pdw = (DWORD)scode;
*pdw = (DWORD)pHeapShell;
i += 3;
}
}
buf[elements] = 0;
printf( "%s\n", buf );
//
//
//
//
69
70
that prevents
whole block away
all exceptions
)
);
This program makes no attempt to pretend to be an actual application someone might write
intentionally, but which happens to make a few mistakes. Some of the errors shown here are
unfortunately all too commonthe most egregious example is that were taking user-supplied
data and using it as a count. If we compile this application using Visual Studio .NET, using
default settings and /GS enabled, we get several different behaviors. With a small count, the
output will look like this:
c:\ projects> SafeSehTest.exe 1
A
Address of shell code is 00408040
Chapter 3
71
Now lets say we raise the count to be large enough to trip the exception handler within the
try-except block:
c:\ projects>SafeSehTest.exe 32
!"#$%&'()*+,-./0123456789:;<=>?
Caught an exception!
This will be followed shortly by Windows Vista telling us that the application has a buffer
overrun and cannot safely continue. So far, we havent given the application enough elements
to overwrite the exception handler; lets try a larger number:
c:\ projects>SafeSehTest.exe 92
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN
Were now looking at an extra instance of calc.exe running on the desktop. The shell code
nicely called exit on the process once it ran. Note that we used EXITFUNC = process, not
EXITFUNC = seh as in the previous example because if we continue to throw exceptions into
a process with a corrupted exception handler, well just keep recursively calling the shell code
until the process overflows the stackit runs out of room for the stack, not an overwrite condition. This will result in about 100 instances of calc.exe runningor attempting to run and
failingon the desktop. If we keep increasing the size of the overwrite, the application will
cause an exception before it hits our contrived exception; it doesnt print anything, but we do
have calc.exe running.
To test how the changes in Visual Studio 2005 help protect things in this case, lets modify the
code a bit. First, get rid of the straightforward stack overwrite by commenting out the first line
inside the for loop, and change the problem to a far more serious case where an arbitrary
DWORD is written to the address of our choice. With this sort of attack, well find that /GS
wont help us because the cookie isnt tampered with. With the compiler used for this example, we find that the address of the exception handler pointer is located at buf + 0x38, not buf
+ 0x30, like it was in the version compiled with Visual Studio .NET. Note that we have two
mitigations on by default: /GS and SafeSEH. We can also enable NX. If we enable only
SafeSEH, were told that SafeSehTest.exe has stopped working. This is to be expected because
the shell code isnt a registered exception handler.
Now, restore the overwrite by enabling the first statement in the for loop, re-enable /GS, and
disable SafeSEH. Now the app crashes, just as it did with only SafeSEH enabled. This is interesting, because /GS on the older compiler was unable to save us from this attack. The change
were seeing is that if /GS is enabled, and we try to jump to an exception handler, the stack
cookie is checked, and the clever attack noted by Mr. Litchfield is foiled.
For a third approach, disable SafeSEH and /GS, then enable NX. If we try the code in this case,
we find that the attack is foiled again. Its interesting how three different sets of protections
will each individually stop the original attack. Even with the far more dangerous arbitrary
4-byte overwrite, two of the three protections will take effect to prevent an attack against the
exception handler. Please note that were not claiming arbitrary 4-byte overwrites are no
72
longer dangerousjust those that are only able to overwrite one exception handler pointer.
Even with all these countermeasures in place, an arbitrary 4-byte overrun is considered very
dangerous and presumed exploitable.
If compiling 64-bit code, the exception records are compiled into the binary and arent kept
on the stack, which makes 64-bit executables much saferat least from SEH attacks.
Summary
Weve covered ASLR, stack and heap randomization, heap defenses, NX, /GS, and SafeSEH.
Unless there are compelling reasons, you need to be using all of these in your application.
Heres what you should be doing:
ASLR
Heap defenses
NX
/GS
SafeSEH
At the very least, Visual Studio 2005 with Service Pack 1and
ensure all of the DLLs the process links with are also compiled with the same compiler
and C runtime library.
So, whats still exploitable with all these overlapping countermeasures in place? Note that there
are ways to overcome every one of these mitigations. Overcoming them all at once is a lot
harder, but it is still possible. Data-driven attacks should also be considered. As a quick example, say an application would call LoadLibrary on a DLL, and an attacker was able to overwrite
the DLL path or nameyoud load the wrong DLL, and call the attackers DllMain function.
Some of the most prevalent attacks against Web servers dont involve arbitrary code but attack
the logic of the underlying application, and these attacks wont be thwarted by compiler or
linker options. At the end of the day, the only way to get secure code is to write solid code.
Call to Action
You should link your code with /dynamicbase, run a full test run, and ship with this
option enabled
References
Howard, Michael, and David LeBlanc. Writing Secure Code, 2nd
Edition. Redmond, WA: Microsoft Press, 2003.
Chapter 3
73
watts_new/1999/March/watts-mar99.htm.
(LeBlanc 2004) LeBlanc, David. Integer Handling with the C++ SafeInt Class, http://
(Litchfield 2003) Litchfield, David. Defeating the Stack Based Buffer Overflow Prevention
Chapter 4
Networking Defenses
In this chapter:
IPv6 Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .76
Network List Manager. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .81
The Windows Vista RSS Platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .82
Winsock Secure Socket Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .83
Windows Firewall with Advanced Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .85
Call to Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .95
References. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .95
Windows Vista has so many improvements and new features in the networking area, wed
really need a whole book to properly cover the topic in any real depth. While there are
improvements in nearly every area, and because any time you say network the phrase security implications naturally follows, were going to focus on the areas most likely to have a
strong security impact on ordinary applications. The following areas include a great deal of
new functionality:
Network List ManagerThis is a new interface available only on Windows Vista that
allows you to determine programmatically what type of network youre connected to.
Well be discussing this interface later in the chapter.
76
IPv6Its here, its on by default, and you ought to start supporting it in your networked
applications. IPv6 still has a way to go before youll see many purely IPv6 networks,
mostly because of older operating systems and some infrastructure issues, but youll
need it to do peer-to-peer. Go learn more about it, and get ready to use it!
IP Helper APIThe IP helper APIs have been updated to give a lot more support for IPv6,
and theres too many helpful new functions to list here. If you do a lot of low-level sockets programming, check these out.
Windows RSS (Really Simple Syndication) PlatformWell cover this later in this chapter.
Windows SocketsThere are a lot of changes here, primarily around IPv6 support.
Theres also a really nice new API that allows you to connect to a remote server using a
list of addresses. Another major change is that support has been dropped for obsolete
protocols; only IPv4 and IPv6 are supported.
Windows Filtering PlatformIf you build firewall applications for use on Windows, you
need to check out the Windows SDK. If you just want your application to work well with
the Windows Advanced Firewall, read onwell cover that in detail. Hidden away in the
WFP documentation are some great new extensions to Winsock that allow you to
enforce security in your application and make it easy to impersonate socket clients.
Windows Firewall with Advanced SecurityWindows is getting a much more robust and
versatile firewall. The version available in Windows Vista can block outbound connections as well as inbound connections, can be applied to services, supports IPv6, and
much more. For a great user experience, youll want your application to register firewall
rules on set up.
TeredoThis is a transitional technology to allow IPv6 applications to work while transiting IPv4 networks, especially NAT (network address translation) devices. Teredo is
needed to enable peer-to-peer networking when running over the Internet.
IPv6 Overview
Although IPv6 isnt technically a security technology, this new network transport plays a
critical role in many new technologies that do have security implications. Depending on the
type of application youre writing, IPv6 support might be critical; for example, you might
choose to implement a game or a collaboration application using the peer-to-peer networking
facilities. Even if youre not creating applications that require IPv6 immediately, its always a
good thing to keep current.
An obvious reason to create IPv6 is that the original IP (Internet Protocol), known as IPv4
only uses 32 bits for network addresses. Once we subtract out the ranges used for multicast
and private address spaces (10.0.0.0/8 and 192.168.0.0/16 are two examples) and then
account for wasted address space, were clearly going to run out of address space relatively
soon. Using NATs helps stave off the inevitable, at the cost of end-to-end connectivity.
Chapter 4
Networking Defenses
77
As I write this, my home network is behind two NAT devicesa real proxy firewall and a cheap
router that accommodates the fact that my ISP regularly changes my address with DHCPand
the ISP has a NAT of its own. To make matters worse, this configuration would be difficult for
anyone who didnt have experience as a professional system administrator. As we move
forward, we can foresee needing several network addresses per person and per household,
and all of this needs to be easily configured. IPv6 solves most of these problems by design.
One of the most significant changes between IPv4 and IPv6 is that instead of the host portion
of an address using a variable number of bits, IPv6 is evenly divided between a 64-bit network
address and a 64-bit host portion. This allows for more devices than were likely to use on this
planet; it works out to several billion available addresses per person. An IPv6 address looks
like this:
fe80::e93a:50e5:6301:aa03
Note that the :: means some number of sequential zeros. Everything after that sequence is
the host portion of this systems address, and the network portion of the address is
fe80:0000:0000:0000, which happens to be the reserved network identifier for link local
addresses.
On a typical IPv6 system, there might be a number of assigned addresses; the first would be
the link local address, which is only good for the local network segment. When writing applications for IPv4, it can take a good bit of tricky code to restrict your application to just the local
network, although the new Windows Vista firewall APIs can help as well see later in this
chapter. When youre writing for IPv6, its trivial to just bind your application to the local
address, and you wont need to worry about traffic from outside the network. The second IPv6
address that could be encountered is the site address; it would be used for communications
within your enterprise network, but it wont be passed along by external routers. Finally, if
you have a connection to the global IPv6 network, youd also have a global IPv6 address thats
fully routable. As you can see, we now have several tools available to determine just what the
service scope of our applications should be. A fourth type of address you will find on a default
installation of Windows Vista is the Teredo Tunnel Adapter address; well cover this in more
detail shortly.
IPv6 also has a some great features; for example, IPSec is built into the IPv6 requirements;
extensibility allows for multiple, arbitrary sized headers; and best yet, autoconfiguration is
standard. The current state of running a home network is similar to the bad old days of configuring the interrupts needed by your hardware; eventually, IPv6 will resemble dealing with
USB devicesyou plug in the network cable and it just works.
If this short overview has sparked your interest, heres some references that can come in
handy:
IPv6: The New Internet Protocol, 2nd edition, by Christian Huitema (Prentice Hall 1998).
78
Internetworking with TCP/IP Vol.1: Principles, Protocols and Architecture, 4th edition, by
Douglas E. Comer (Prentice Hall 2000).
There are currently many books on this topic, and were not asserting these are the best references, just those that weve found helpful. Wikipedia has the advantage of staying current,
and it has a number of helpful links.
Teredo
Teredo has a somewhat colorful history. Originally, Teredo was known as Shipworm, and the
name was created with the idea of enabling applications to transit networks behind NAT
devices, but it also gave an entirely different image to people charged with maintaining operational security. Having worms eating holes in the firewalls you put there to protect your network is worthy of horror movies! Possibly due to the connotations of the name Shipworm,
the protocol was renamed to Teredo, which is really the same thingthe most common type of
shipworm has a Latin name of Teredo navalis! Despite the colorful name, Teredo has evolved
into one of the more secure and firewall-friendly ways for applications to transit across NAT
devices and firewalls.
There are two fundamental problems Teredo is designed to solve. First, there are too many
systems behind devices that only support IPv4. We need a way to be able to get IPv6 to a
useful state without everyone running out and replacing their home router/firewall devices,
not to mention all the Internet service providers updating their networks. Second, most
people are behind at least one NAT device, often two. For applications that need end-to-end
connectivity (such as peer-to-peer networking, games, and collaboration scenarios), NAT
devices are preventing effective solutions from being created.
Teredo works by encapsulating IPv6 on top of IPv4 UDP packets. Although there are other
approaches that can be used when we can assume clear end-to-end connectivity, such as on a
large enterprise network, Teredo is assuming that NAT devices need to be traversed.
The first thing a client needs to do is send an initial packet to a properly configured Teredo
server. The Teredo server is responsible for being able to refer clients to a Teredo relay, and it
doesnt do much else: the goal is for a Teredo server to consume very little network bandwidth. The initial packet consists of an IPv6 ping with a large random number as the payload
carried on top of a UDP packet sent to port 3544. The IPv6 portion of the initial packet is then
forwarded to the IPv6 address the client is trying to reach, which would then send the reply
to the nearest Teredo relay. Thus, the relay can be thought of as a somewhat specialized
router.
The relay now needs to be able to put the response back into an IPv4 UDP packet and send it
back to the client. It does this by cleverly using portions of the IPv6 address to encapsulate the
information needed.
Chapter 4
Networking Defenses
IPv
6p
ing
resp
79
ons
ing
6p
IPv6 Server
IPv
Teredo Relay
e
ns
po
Teredo Server
Ter
e
do
ng
Re
6
Pv
qu
est
qu
e
oR
Figure 4-1
pi
est
e
Ter
s
re
Home Router
The first 32 bits of the Teredo IPv6 address is the Teredo prefix: 2001:0000. The next 32 bits
contain the IPv4 address of the primary Teredo server that sent the packet, which is followed
by 16 bits of flags and some information used to inhibit random scanning attacks. Finally, the
actual IPv4 address and the port used by the NAT is obfuscated within the final 48 bits. We
now see that the relay has all the information needed to reconstruct the UDP reply packet and
send it to the client.
You may be wondering how UDP packets sent to one host will get back to you if another host
replies. The answer is that you have 4 different types of NAT devices in general use:
Full coneA full-cone NAT establishes an external UDP port when sending an outbound packet and will forward traffic sent to that port from any IP address and any port
back to the originating port on the internal system. This doesnt sound very secure, and
80
it isntthis feature is why you should never depend on an NAT for security. It does
conserve resources on the NAT device, which is the primary design goal of this type of
system.
Restricted coneThis type of NAT maintains some level of state and requires that replies
come from the same IP address as the initial request was sent to.
Port-restricted coneReplies must come from the same IP address and port as the
request.
SymmetricIn addition to the requirements for a port-restricted cone NAT, the symmetric NAT will create a new mapping of internal IP address and port to external IP address
and port for traffic sent to every individual external host. This type of NAT is somewhat
more secure and is typically only seen in fairly expensive higher-end firewalls.
Some newer NAT devices can also appear to be port restricted under some conditions and
symmetric under others:
In particular, we found out that many NAT have a 5th strategy, port conservation.
Basically, they will try to keep the same port number inside and outside, unless it is already
used for another connection, in which case they pick a different one either sequentially (from
a global variable) or randombly. These NATs appear typically port restricted during the
tests, but behave as symmetric under load. (Huitema, personal communication)
If youre interested in the details, consult RFC 3489 (Rosenberg et al. 2003).
Back to our Teredo relay! It will send the reply back to the Teredo client. If the reply reaches
the client, the client then knows it is behind a cone NAT. There are a number of scenarios that
can come into play, but the primary thing is that the Teredo relay will attempt to get out of the
relay business entirely and just let the clients exchange packets directly, which can be done
when two IPv6 systems are behind different Teredo-compatable NAT devices.
What all of this means to you is that it is now possible to create applications that can deal
directly with one another, even if they are behind NAT devices. You get end-to-end connectivity, and, as IPv6 becomes ubiquitous, it will continue to work, just better.
There are some security implications of Teredo. If youre operating a firewall, the protocol is
designed to be completely transparent. If you dont want to allow Teredo, just block one UDP
port; if you wanted to selectively manage Teredo traffic, all the information you need is available
so that it should be easy to write a Teredo-specific packet handlerand it would be very efficient.
Out of all the tunneling approaches currently in use, this one is one of the easiest for the network administrators to deal withwhich is smart because happy and secure network admins
are less likely to just block your traffic. On the client side, a socket has to specifically request that
it would like to accept Teredo traffic on Windows XP, and on Windows Vista, Teredo traffic is
controlled by the firewall settings, so the only applications on your system exposed by Teredo
would be those that expect to deal directly with the Internet. In later sections, well show you
how to create host-based firewall rules that explicitly allow or deny Teredo.
Chapter 4
Networking Defenses
81
The system as a whole has connectivity to a local subnet, a local network, or the Internet.
All of these parameters can be found for both IPv4 and IPv6. This information is
obtained using the GetConnectivity method.
An enumerator for the list of network connections on this system can be obtained. You
can find only connected, only disconnected, or all networks using the GetNetworks
method. You can also find all the connections using the GetNetworkConnections method.
A related interface is the INetworkListManagerEvents, which exposes one sink method, ConnectivityChanged. The ConnectivityChanged method allows your application to be notified if any of
the properties INetworkListManager::GetConnectivity returns have changed. At a more granular
level, you can also register sinks exposed by the INetworkEvents interface to determine if a
network has been added or dropped. Unless youre writing a network monitoring or management tool, its rare for an application to need to drop to this level.
Although most of the properties for individual networks arent especially interesting from a security perspective, the INetwork::GetDomainType method tell us whether a domain controller is
present on your network and, if one is present, whether your connection has authenticated to the
domain controller. Some applications should change functionality when domain status changes.
82
For example, Outlook 2007 knows to connect to the internal Exchange server when my laptop is
connected to the Microsoft corporate network, but it isnt confused by my home domain controller when connected to my home network and instead connects to Outlook Web Access.
Another interesting property of a network is obtained from INetwork::GetCategory, and this tells
you if youre on a domain, public, or private network. The firewall included with Windows Vista
determines which policy to use on the basis of the return value for this method. If your application changes firewall rules depending on which network category your customers system is
connected toand please note that this is a property of a network, which means that you could
have different rules in effect for different networksthen you may want to monitor the system for
changes so that your application can gracefully handle changes instead of presenting unintelligible errors to the user. Additionally, you may want your application to behave more conservatively and expose less attack surface when running on a public network.
Of course, we cant expect you to rewrite your product to use the RSS platform
in Windows Vista, but if you are building a product that includes RSS support, then you
should seriously consider using the RSS platform in Windows Vista.
The Windows RSS platform offers two COM interfaces: a vtable version for C/C++ developers
starting with the IXFeedsManager interface, and a late-bound IDispatch-derived interface
named IFeedsManager. You must include <msfeeds.h> in your code to access these interface
definitions.
A full treatise of the Windows RSS platform is beyond the scope of this book, and you can find
more information about it at MSDN (Microsoft 2006b), but to give you a feel for how to use
the RSS infrastructure, the following code will enumerate all feeds in the root folder, and
display each feeds name, URL, and item count.
HRESULT hr = CoInitialize (NULL);
if (FAILED(hr))
return hr;
Chapter 4
Networking Defenses
83
CComPtr<IXFeedsManager> spIXFeedsManager;
hr = spIXFeedsManager.CoCreateInstance(CLSID_XFeedsManager, NULL, CLSCTX_INPROC_SERVER);
if (FAILED(hr))
return hr;
CComPtr<IXFeedFolder> spIXRoot;
hr = spIXFeedsManager->RootFolder(IID_PPV_ARGS(&spIXRoot));
if(SUCCEEDED(hr)) {
CComPtr<IXFeedsEnum> spIXFeedsEnum;
hr = spIXRoot->Feeds(&spIXFeedsEnum);
if (SUCCEEDED(hr)) {
UINT uiFeedCount = 0;
hr = spIXFeedsEnum->Count(&uiFeedCount);
while (uiFeedCount && (SUCCEEDED(hr))) {
CComPtr<IXFeed> spIXFeed;
hr = spIXFeedsEnum->Item(--uiFeedCount, IID_PPV_ARGS(&spIXFeed));
if (SUCCEEDED(hr)) {
LPWSTR wszName = NULL;
if (SUCCEEDED(spIXFeed->Name(&wszName))) {
wprintf(L"%s",wszName);
CoTaskMemFree(wszName);
}
LPWSTR wszUrl = NULL;
if (SUCCEEDED(spIXFeed->Url(&wszUrl))) {
wprintf(L" from %s",wszUrl);
CoTaskMemFree(wszUrl);
}
UINT cItems;
if (SUCCEEDED(spIXFeed->ItemCount(&cItems))) {
wprintf(L" has %d items.\n",cItems);
}
spIXFeed = NULL;
}
spIXFeedsEnum = NULL;
if (uiFeedCount)
hr = spIXRoot->Feeds(&spIXFeedsEnum);
}
}
}
spIXRoot = NULL;
spIXFeedsManager = NULL;
CoUninitialize();
84
WSASetSocketSecurity before either calling bind (assuming youre writing a server) or connect
(assuming youre creating a client), and if youre writing a kernel mode application, youll
need to use WSAIoctl to set the security settings. To use this API, youll need to set up a
SOCKET_SECURITY_SETTINGS structure, which is defined here:
typedef struct _SOCKET_SECURITY_SETTINGS
{
SOCKET_SECURITY_PROTOCOL securityProtocol;
ULONG securityFlags;
} SOCKET_SECURITY_SETTINGS;
The securityProtocol member has two valid values currently defined: SOCKET_SECURITY_
PROTOCOL_DEFAULT, which is what youd get using a default socket, and SOCKET_
SECURITY_PROTOCOL_IPSEC, which actually enforces that IPSec be used for this socket.
Unfortunately, it isnt quite that simple. Once you set the SecurityProtocol member to
SOCKET_SECURITY_PROTOCOL_IPSEC, WSASetSocketSecurity expects to be passed a
SOCKET_SECURITY_SETTINGS_IPSEC structure. The first option you can use determines
whether IPSec is required to allow connections, or well just use IPSec when available, which is
done by setting the SecurityFlags member to either SOCKET_SETTINGS_GUARANTEE_
ENCRYPTION or SOCKET_SETTINGS_ALLOW_INSECURE. If you choose to allow insecure
connections, you may want to call WSAQuerySocketSecurity on connections and branch your
code depending on whether the connection is secure or not. There is one flag in the
IpsecFlags member definedSOCKET_SETTINGS_IPSEC_SKIP_FILTER_INSTANTIATION
which is used when you know that the IPSec policy has already been configured for your application. Note that if you supply NULL as the pointer to the SOCKET_SECURITY_SETTINGS
structure, default IPSec configuration for that system will be applied to your application.
Although weve been able to do server authentication and impersonation of socket clients in
the past, it involved writing quite a lot of fairly messy Security Support Provider Interface
(SSPI) code. Among the improvements in Windows Vista is WSASetSocketPeerTargetName,
which allows the client to specify the service principal name (SPN) of the server and application (Microsoft 2007) they want to connect to. If this option is used, once connect returns success, we know weve connected to the server we intended to connect to. Its important to know
what server youre connecting to, because the next interesting API is WSAImpersonateSocketPeer, which is what the server is going to call to identify the user thats just connected and to
make access control decisions. If youre writing a server application, be sure to check whether
the impersonation succeeded, and remember to call WSARevertImpersonation as soon as
youre done checking access. You should also remember to carefully take into account exceptions (whether C++ or SEH exceptions) thrown while inside a block thats impersonating. If
youre able to leverage C++, a good approach is to create a class that reverts the impersonation
in the destructorthen youre guaranteed to revert as the stack unwinds. If youre using C, or
cant use C++ exception handlers, try-finally can achieve the same effect.
Overall, this is a great set of useful new API calls. The alternative is to write a lot of messy code,
and weve seen ISVs go as far as using embedded Web servers with SSL/TLS to accomplish
Chapter 4
Networking Defenses
85
the same thingtoo bad they also forgot to set up certificates correctly for the SSL/TLS connection and accomplished little more than security by obscurity. Once the platform support
for the new socket options is widespread, these new socket options will make getting real
security a lot easier.
A helpful hint is to search for CLSID in the NetFw.h header; this shows you the COM classes
you can instantiate directly with CoCreateInstance, as opposed to having to acquire an interface
pointer through a class method. The currently defined classes are:
NetFwRule (Advanced Firewall)
NetFwOpenPort
NetFwAuthorizedApplication
NetFwPolicy2 (Advanced Firewall)
NetFwMgr
Now that you have an instance of the NetFwPolicy2 object, we can get information about the
current status of the firewall, and which policy is in effect with the following:
long CurrentProfileType;
printf( "Checking current profile type:\n" );
hr = pFwPolicy2->get_CurrentProfileTypes( &CurrentProfileType );
if( SUCCEEDED( hr ) )
{
// The profile type can currently be treated as exclusive,
// but this may not be true in the future, so check it like this:
86
If the system is joined to a domain and is able to locate a domain controller for its forest, the
domain policy will take effectwhich could be pushed down from the domain controller. If
the system is exposed to a public network, the public policy would be used, and if youre running on a private network, the private policy would take effect. When you write rules for your
application, which policy to enable is a primary consideration; for example, we might not
want something like Universal Plug and Play (UPnP) running on a corporate networkits
unlikely to be a great idea on a public network, but its probably very useful on a private network. Think through your choices about which profiles to support with your rules in terms of
principle of least privilege. If you use our GetFirewallSettings sample to dump all of the rules on
your Windows Vista system, youll see some applications that have different rules for each
profile and some that only support some of the profiles. You can also check whether modifications can succeed with this code:
printf( "Checking whether modifications will succeed:\n" );
NET_FW_MODIFY_STATE ModifyState;
hr = pFwPolicy2->get_LocalPolicyModifyState( &ModifyState );
if( SUCCEEDED( hr ) )
{
switch( CurrentProfileType )
{
case NET_FW_MODIFY_STATE_OK:
printf( "Modifications are OK\n" );
break;
case NET_FW_MODIFY_STATE_GP_OVERRIDE:
printf( "Firewall state set by group policy\n" );
break;
case NET_FW_MODIFY_STATE_INBOUND_BLOCKED:
// This means your rule will be applied, but
// wont work because of blocked inbound connections.
printf( "Inbound connections are blocked\n" );
break;
}
}
For each policy level, the following properties are configurable on an individual basis:
BlockAllInboundTraffic
Chapter 4
Networking Defenses
87
FirewallEnabled
In general, applications should not modify systemwide settings, but it can often be useful to
determine whether a setting might be in place that would cause your application to either
malfunction or have reduced functionality and then notify the user. If it is important to the
security of your application for the firewall rules to be in effect, you may want to warn the user
if the firewall is disabled. Note that applications need to be fully elevated to administrator to
change firewall settings.
Creating Rules
There are two main tasks to creating good firewall rules. The first is understanding how to use
the APIs to create rules and put them into groups. Due to some documentation errors, this
isnt as easy as it looks, but weve sorted out these issues so you can avoid them. In all likelihood, unless you are one of the first ones to buy this book, the documentation errors will have
already been fixed, and a couple of functional issues are scheduled to be fixed in Windows
Vista service pack 1.
The second part of creating good firewall rules is much harderknowing both how your application needs to work with the network and knowing the types of attacks that someone might
launch against your application. First, well show you what can be done with the Advanced
Windows Firewall, and then well show you the rules we wrote for our sample application and
why each of these rules is important. One note, although the Advanced Windows Firewall is
indeed very advanced compared to the previous Windows Firewall shipping in Windows XP,
it isnt going to allow you to do some of the things you could do with a fully featured,
enterprise-class firewall. For the most part, these limitations arent a significant problem, but
if they are, you can do nearly anything you like by writing filters for the Windows Filtering
Platform, which is also documented in the Windows SDK. To be completely fair, its also true
that the Advanced Windows Firewall allows you to create rules that might be difficult using
other approaches, such as applying rules only to wireless interfaces.
Using the Advanced Windows Firewall rules, a firewall rule can have the following
properties:
ApplicationNameThis property takes a full path to the binary youre running. Wed
suggest copying the executables into a staging directory or creating two sets of rules, one
for debug and another for release.
DescriptionThe description is just a user aid and documents what youre trying to
accomplish with this rule.
88
DirectionThis is the direction traffic will flow, and is one of two enum values: NET_FW_
RULE_DIR_IN or NET_FW_RULE_DIR_OUT. Rules regulating what can go from your
application to other points on the network are outbound rules, and rules regulating
what can come from the network to your application are inbound rules. Note that some
protocols, such as TCP, are considered directional, and enterprise-class firewalls can
write rules that allow connections to be made in only one direction. This version of the
firewall rules doesnt support this level of functionality, although it isnt as important for
a host-based application-level firewall to be able to regulate the TCP connections at this
level.
GroupingThe grouping property is important but tricky. If your rules have a grouping
property, you can manage them all at once, which well show you later in the chapter.
The documentation tends to expect that you can feed a simple BSTR into this property,
such as My Apps Rule Group, and it ought to work, but unfortunately, this wont work
until service pack 1. You can give it the path and string ID to a string stored in a resource
DLL, and well show you how this is done.
IcmpTypesAndCodesThe ICMP types and codes allows you to regulate what type of
ICMP traffic is allowed to be passed along to your application. The types and codes are
passed in as strings, so if your application sent pings (ICMP echo request) and expected
replies, your rule would need to allow 0:0,8:0where type 8 is an IPv4 echo request,
code 0 is the only documented code for that type, and type 0 is an echo reply type, again
with a corresponding code of 0. If your application needed to deal with all of the ICMP
unreachable packets, the way to write the rule is to specify 3:*which is interpreted as
type 3, all codes. If you use the sample code provided, you can dump the rules that come
with Windows Vista and see more examples. Hopefully, the details of how to actually
use this property will be documented in the Windows SDK soon.
InterfacesThis specifies which interfaces the rule applies to. The default is all interfaces. Well present a sample showing how to do this shortly.
InterfaceTypesThis is a string specifying the type of interface to apply the rules on.
Currently allowed values are RemoteAccess, Wireless, Lan, and All.
LocalAddressesThis specifies the local addresses a rule applies to. In most cases, youd
set this to *meaning all interfaces. You can specify a list of comma-separated IP
addresses (no spaces), a range of IP addresses (for example, 192.168.0.1-192.168.0.255),
or you can use CIDR notation (192.168.0.0/24 in CIDR means the same thing as the previous range example). The documentation states that you can use netmask notation,
which would specify the 192.168.0.x network as 192.168.0.0 mask 255.255.255.0, but
in reality, this isnt supported and wont work. Although it is annoying that the Windows
Chapter 4
Networking Defenses
89
SDK doesnt correspond to what works, it isnt a serious problem because CIDR notation
accomplishes exactly the same thing, is more compact, and is easier to understand.
Ranges could be used as well. Strings designating IPv6 addresses are also supported.
LocalPortsThe local ports tell us what ports are affected by the rule. You can specify
* for all ports, or you can specify an individual port. If you want to specify more than
one port, a comma-separated list will do it (such as 7, 8, 9). Unfortunately, ranges
arent available for ports in this version. Another issue is that while its tempting to
copy and paste the list of properties into your code and then set them one-by-one so
you wont forget anything, if you try to set the local or remote ports prior to setting the
protocol to either TCP or UDP (6 or 17), the set_LocalPorts method will fail with an
error of invalid parameter. Although we havent tried it, the same may be true of the
ICMP types and codes. Just remember to set the protocol first, and things will work as
expected. The following keywords are supported (Yariv and Mohan, personal
communication):
RPC: a special keyword designating a port used by the RPC run-time DLL. When
the RPC run-time DLL is using a dynamically assigned port to receive traffic for a
given interface (RPC/TCP), it marks the socket in a special way that is detected by
Windows Firewall. Specifying this keyword in the LocalPort attribute means the
port is dynamically assigned and used for RPC purposes.
RPC-EPMap: a special keyword used to designate the RPC end-point mapper port.
This is used by the RPCSS service when it uses TCP/135 for RPC/TCP end-point
mapping and TCP/593 for RPC/HTTP end-point mapping. Instead of specifying
this fixed port, by using this keyword the firewall is being notified by the RPCSS
service of the right port it needs and dynamically opens/closes these ports on the
fly as RPC interfaces are being registered/unregistered. This port keyword is used
in the Windows Firewall out-of-box policy and should also be used for third-party
cases where dynamic RPC is being used.
Teredo: a special keyword used to designate the dynamic ports the Teredo service
is using to perform its NAT-traversal magic. By using this keyword, the firewall is
being notified by the Teredo service of the right port(s) it needs and dynamically
opens/closes these ports on the fly as Teredo is being utilized. This port keyword
is used in the Windows Firewall out-of-box policy and should probably not be
used in third-party scenarios.
ProfilesThe profiles property determines whether the rule applies to public, private, or
domain profiles. This same set of properties shows up in the NLM APIs discussed earlier
in this chapter.
ProtocolThis is the protocol for your traffic. Typically, this would be 1 for ICMP,
6 for TCP, or 17 for UDP. IPv6 is specified by 41. Out of the 255 possible values,
close to 140 are currently defined. You can find a complete and current list at
90
RemoteAddressesThe remote addresses follow the same rules as the local addresses,
but theres also a predefined string that is very useful: LocalSubnet means that we only
want to accept local traffic.
RemotePortsLike the LocalPorts property, this specfies which remote ports your application can access. For most server applications, this would be any port (or *), and for
client applications, youd specify the port the server listens on.
ServiceNameThis specifies the name of the service the rule applies to. Many services
run under the same application name, and this setting is needed to distinguish between
the large number of services running under svchost.exe, to give the most common example. Once you understand how to create rules, well show you how to apply this setting
in Chapter 6, which covers services. If your service isnt a sockets application, the default
rule blocks everything, which is both easy and does exactly what you want.
Unfortunately, it wont work that way prior to service pack 1. What you actually need to
do is this:
BSTR Group =
SysAllocString( L"@c:\\scratch\\RuleSetResource.dll,-101" );
hr = pFwRuleIn->put_Grouping( Group );
So just what is this DLL, and whats the -101 for? Just follow these steps, and it isnt all
that onerous. First, create a new project under your solution. Make it a Win32 project,
tell it you want a DLL, and just take all the defaults. None of the code is actually important. Next, right-click at the top of the new project, and tell it youd like to add a new
resource. It will ask you what type of resource; select String Table. Drill down into the
new string table, and add a string for your group name. Take note of the string IDthe
first will be 101, which explains the -101 after the DLL name. What this also gives you,
nearly for free, is an easy way to localize your applications firewall rules for different
languages. Finally, remember at setup time to place the DLL on the users system, and
make sure the application setting your firewall rules has the full path to your resource
DLL. If youd like to learn more about the string syntax, see the Windows SDK documentation for SHLoadIndirectString.
Chapter 4
Networking Defenses
91
Now that weve documented all the foibles of the NetFwRule object, using them is fairly simple. You call CoCreateInstance on as many NetFwRule objects as you need, set all the properties
where the default isnt good enough, get an interface to the rules collection, and add your
rule(s). The code looks like this:
hr = CoCreateInstance( __uuidof(NetFwRule),
NULL,
CLSCTX_INPROC_SERVER,
IID_INetFwRule,
(void**)&pFwRuleIn );
// Go set up all the rule properties
// Now let's get a pointer to the rules
INetFwRules* pFwRules;
hr = pFwPolicy2->get_Rules( &pFwRules );
// Handle errors
hr = pFwRules->Add( pFwRuleIn );
if(SUCCEEDED(hr))
printf( "Added inbound rule\n" );
else
printf( "Could not add inbound rule - err = %x\n", hr );
The last remaining task is really the hardest part: how to make a rule set for your application.
For our example server, we created a simple TCP-based echo server. Although you wont often
see echo servers these days, they used to be very common and were used to establish network
connectivity and a quick check of latency and available bandwidth. An echo server listens on
a well-known, designated port (7) and sends back any ASCII string it was sent by the client.
Lets look at what we need to do. For inbound connections, we have to allow traffic from any
remote port to our local port 7. If the feature were available, wed prefer to be able to say that
we wanted to accept connections from dynamic portsport numbers greater than 1023 (ports
below that are reserved ports, although on Windows any application can use them). Unfortunately, port ranges arent available, so well just have to take all ports. For a silly example like
92
echo that can be used to eat up all our expensive bandwidth, it would be best to set the
RemoteAddresses to LocalSubnet. Your choice of RemoteAddresses could be different, and
is one of the more important decisions to make about your application.
Now that we can get inbound connections, we need another rule to specify where the replies
can go. Our first outbound rule is to allow traffic from local port 7 to all remote ports on the
local subnet, which is a straightforward reversal of the inbound rule. The second outbound
rule illustrates a more subtle problemwe certainly dont want to allow a situation where our
echo server can be tricked into having an endless conversation with another echo server. To
prevent this, well add an outbound rule that disallows traffic from our port 7 to any remote
port 7. If youre well educated about networks, you might point out that spoofing a connection
isnt so easy with TCP, and this is mostly a problem with UDPand youd be right! However,
this is just an example, and the point is that just because you need to reply to legitimate
inbound connections, there might be some things you ought not reply to.
If youre the inquisitive type, you might also be wondering why wed go to all this trouble to
restrict traffic to port 7 on our server. After all, the server only listens on port 7, and any connections attempted to other ports will just get met with a reset, so why not just allow all ports
to all ports? The answer to this is that if an attacker did take over your application, one of the
things they might do is bind a command shell to a port you might not noticesay, 54321. Your
echo service obviously has no business accepting connections on port 54321, but if you
allowed traffic to all ports, this is exactly what could happen. If you restrict the traffic to
port 7, the attackers attempt to bind a command shell to port 54321 will end up failing the
first time they call accept, and not knowing just what it is that made things break, they may
just move along to a less resistant victim. Another common ploy would be to make an outbound connection to another port, but in this case, our outbound rule has restricted them to
starting from port 7. If the attacker knows what is stopping them, only then could they bind
their source port to port 7 and make the outbound connection. You could stop them from
rebinding to port 7 by setting SO_EXCLUSIVEADDR, but you could get into issues trying to
restart on failure. The bottom line here is that principle of least privilege is always a good
thing, and this goes double if youre dealing with networked applications.
If youre writing a client application, you would just apply the same rules in reverse. Allow
connections from any local port to the servers port, and traffic from the servers port back to
any local port. If youve read (and followed) our advice on how to make firewall-friendly applications in the second edition of Writing Secure Code, writing firewall rules for your application
should be easy. If you didnt follow our advice and find yourself writing more than a dozen
rules to cover a myriad of connections between a large number of ports on both ends, or if you
forgot that sockets do go both ways and used two when you needed one, it might be time to
refresh your knowledge of that topic and perhaps write your application to play more nicely
with firewalls.
Chapter 4
Networking Defenses
93
You have methods available to determine if the rule group is currently enabled and whether the
rule group is enabled for each of the three profiles (domain, public, and private), and you can
enable or disable your rule group for each or all of the three profiles with one call. Take a look
at the CheckFirewallRuleSet sample application to see how to use the APIs. As with the rules,
be mindful that a VARIANT_BOOL isnt a bool, nor is it a BOOL. For example, this works:
if( SUCCEEDED( pFwPolicy2->EnableRuleGroup( profileTypesBitmask,
group, VARIANT_TRUE ) ) )
{
printf( "Rule group now enabled\n" );
}
If you had passed true or TRUE to EnableRuleGroup, it would not have worked.
And expect it to work. The compiler may complain, and it will certainly complain if we try
to assign TRUE to a bool, but it will indeed work as we expect. Unfortunately, if we mix
and match bool, BOOL, and VARIANT_BOOL, it wont always work, and if you write:
VARIANT_BOOL fEnabled = true;
It will compile cleanly, and if you pass fEnabled to any of the set methods for the preceding rules, youll find that it wont work. Despite having done a lot of work with integer
manipulation and overflows, this caused me significant amounts of programmer astonishment! To save you time, trouble, and headaches, heres whats really going on:
Type
Integer Representation
BOOL
signed int
bool
unsigned char
VARIANT_BOOL
signed short
-1
94
When armed with this information, we quickly see that if( VARIANT_TRUE == true) is
false, since -1 != 1! While were on the topic of bools, you may often run into annoying
warnings when you do something like assign the results of a Windows API that returns
BOOL to your C++ bool. The right way to do this is:
bool fRet = !!OpenProcessToken( ... );
What happens here is that the first unary not (!) performs an operator cast to type bool,
and then the second unary not changes the result to what you expected. A less elegant
way to do the same thing is:
bool fRet = OpenProcessToken( ... ) ? true : false;
If youve been programming for Windows for a long time, you may recall back in the 16bit days when TRUE was a 16-bit integer and was defined as -1. Because
VARIANT_BOOL is descended from Visual Basic, it has kept its 16-bit heritage, and
unfortunately VARIANT_TRUE is -1.
What should you do about all this? First, be aware of these differences. If something asks
you for a VARIANT_BOOL, pass it either VARIANT_TRUE, or VARIANT_FALSE. If
youre writing code that deals with VARIANT_BOOL, and youd like to be robust in the
face of unsuspecting C++ programmers thinking that true might be 1, write your code
like this:
if( fEnabled != VARIANT_FALSE )
or better yet,
if( fEnabled )
You can always count on false being 0, but you cannot count on anything else, although
it is a happy side effect that I can cast a bool to a BOOL and back again without ever
changing the value.
A documentation problem we uncovered is that IsRuleGroupCurrentlyEnabled is documented,
whereas get_IsRuleGroupCurrentlyEnabled is what actually exists in the header and will compile. Hopefully, by the time youre reading this, the documentation will have been updated
and will be correct.
Chapter 4
Networking Defenses
95
Call to Action
Use the NLM APIs to get information about network changes, and make your application handle changes gracefully.
Learn how to create correct firewall rules for your application and enable them.
References
(Huitema, personal communication)
February, 2007.
(RFC 3489) Internet Engineering Taskforce, Network Working Group (Rosenberg
(CVE-2006-4660)
(CVE-2006-4712)
(Microsoft 2006a) Microsoft Windows Software Development Kit for Windows Vista and
(Microsoft 2006b)
ms677949.aspx.
(Yariv and Mohan, personal communication) Yariv, Eran, and Mahesh Mohan, Microsoft
Chapter 5
Services Overview
Windows services are used to run programs that need to operate independently of whether a
user is logged on at a console. If youre familiar with Unix or Linux, services are very analogous to daemons. Although were not going to go into detail about just how services work, it
would be helpful for you to have an overview of how services function. For a full description,
97
98
look at the Services topic in the Windows SDK documentation, especially the Service
Program Tasks topic.
Services typically run under one of three specially defined service accounts, and can be configured to run as a user account. Service accounts will be covered in the next section. A service
is just a normal executable program, except that it will make a number of special API calls to
register entry points with the service control manager (SCM). Its this authors practice to
write services that will install and remove themselves if invoked with the correct switches and
that run as a service otherwise. This is because you can end up installing and removing your
service many times during the development process, and the CreateService API takes 13 arguments, not counting the handle to the SCM. Its also easier, since well need the service name
in multiple places in our service code.
The first task a service performs in its main function is to register the ServiceMain function
with the service control dispatcher. This is almost all that the main entry point for the application will do, and if multiple services need to be supported within one process, multiple service names and entry points are registered. Whether to run multiple services in one process is
a key decision from the standpoint of security, performance, and reliability. Regardless of how
many services run in the same process, theyll share the same address space, run under the
same service account, and have the same privileges enabled.
ServiceMain calls yet more boilerplate code: the first thing it does is register the control handler for the service. If the service needs to be started, stopped, paused, or process newer controls (like logon events or hardware changes), the control handler function is where this will
happen. The control handler function is called by the service control dispatcher and operates
asynchronously with your services worker threads, so youll need a way for the control handler function to communicate with the thread or threads doing the work. Many implementations use events, but this example uses I/O completion ports, which can be a lot more efficient
than events, largely because wed need a lot of events to properly handle a named pipe and the
control mechanisms, but we can do it all with one I/O completion port.
There are two control handlers defined, and in new code, you should use RegisterServiceCtrlHandlerEx, which allows you to use the newer controls. Once the control handler has been
registered, the service must update its control status to SERVICE_START_PENDING. If you
need to do lengthy initialization, you need to repeatedly call SetServiceStatus with an updated
wait hint to let the SCM know that your service hasnt hung during start-up. Be careful to only
send updates when youre really making progressjust sending updates in a loop can have
some nasty side effects on the operating system. Speaking of start-up, many services start on
boot, and there are new options that allow your service to start just after boot, which tends to
improve the users overall experience. Check the most recent Windows SDK for details on
this. If the initialization doesnt take much time, go ahead and set the status to
SERVICE_RUNNING. Once the service status has been set to running, you should then go
ahead and call the function in which youre going to do the bulk of your work. Be sure and
implement any code needed to communicate correctly with your control handler in this
Chapter 5
99
function, and make sure that the last thing the function does is to set the service status to
SERVICE_STOPPED, with the appropriate exit code. Once ServiceMain returns and the status
has been set to SERVICE_STOPPED, the service will exit, and if it is the only (or last) service
running in that process, the process will exit as well.
Service Accounts
Services normally run under one of three specially defined service accounts or as an
ordinary user:
Local system
Local service This account has few privileges locally and only amounts to a normal
user for the most part, except for the right to create global objects (SE_CREATE_
GLOBAL_NAME), which can have some interesting implications, and the right to impersonate clients (SE_IMPERSONATE_NAME). Note that if youre only identifying clients,
SE_IMPERSONATE_NAME isnt needed. Across the network, services running under the
local service account will authenticate as the anonymous account, which could be a
problem or a feature, depending on what youre trying to do.
Network service This is the account to use when your service has to authenticate
across the network as the computer account. As of Windows 2000, computer accounts
are just variants on normal user accounts and can be added to ACLs and groups. Computer accounts are in the form of Domain\ComputerName$. Other than network
authentication, network service has the same rights as local service.
Domain or local user account You can run services under any user account that can
log on to the system. Although this is sometimes the right choice, password management can be a problem. The best thing to do if youre going to take this approach is to
make two accounts; put them in an appropriate group; and when password changes are
required, cycle all the services from account A to account B.
100
Even though the local service and network service accounts are a huge improvement over running all services as local system, there are still a number of problems. If your service needs
access to some specific resource, that resource ends up getting an ACL entry added for local
service or network service. If any other service running under that account is compromised,
your resource is now available to the attacker. The various services running under these
accounts are also vulnerable to attack by each other. If the attacker is clever (and many are)
and having a good day, they may even find ways to escalate privilege to a local system.
The solution to the problem of setting ACLs for specific service resources is to use a new type
of SID in your access control listthe service SID. The code to set the service SID is simple:
DWORD SetServiceSid( SC_HANDLE hSvc, DWORD dwServiceSidType )
{
SERVICE_SID_INFO SvcSidInfo;
DWORD dwErr = ERROR_SUCCESS;
SvcSidInfo.dwServiceSidType = dwServiceSidType;
if( !ChangeServiceConfig2( hSvc,
SERVICE_CONFIG_SERVICE_SID_INFO,
&SvcSidInfo ) )
{
dwErr = GetLastError();
}
return dwErr;
}
When a process or thread token contains restricted SIDs, all the access checks must go
through two passes. The first is to see if the normal SIDs enabled in the token grant the
access requested. If access is granted, then a second check is performed to see if access is
granted on the basis of the restricted SIDs. Note that different user accounts could satisfy
both checksfor example, the regular check might be granted because user David has access,
and the second check might be granted because the write-restricted SID also has access.
What this implies is that the SERVICE_SID_TYPE_RESTRICTED option means that your
service can only open resources ACLd to everyone, the specific service SID, and the
write-restricted SID.
Chapter 5
Restricted SIDs
Resource ACL
Default Groups
Restricted
Network Service
------------------
-----------------------------
------------------
Service SID
Users
------------------
-----------------------------
------------------
Write-Restricted
Everyone: Read
Everyone
------------------
-----------------------------
------------------
Everyone
Users: Read
Auth. Users
------------------
------------------
Logon ID
Logon ID
101
-----------------Service SID
Figure 5-1
As an example, the sample service has a debug log file. Because having clients connect to a
named pipe implies that youre implementing a state machine, state transitions can be complex and hard to debug, especially when considering error states. Having a log file gives you
an easy way to print out verbose information about whats happening inside the sample service. When we install and run the service, if we fail to provide a full path to the file, we find
that it gets created in %systemroot%\system32, which requires administrator-level access to
add files, and which the process would have if it were running as local system. A better solution is to put the log file in a handy directory used for temporary filessay, for example,
c:\scratch. Heres how you can give the sample service access to the directory in order to
create the log file:
c:\scratch>cacls . /e /g "NT Service\VistaService":c
If youre not familiar with cacls, what this translates to is add an ACE that grants change
access to this directory (.) to the VistaService service accountassuming youve already
installed the service, which is why we install the service first in our example. Note that while
cacls is still supported on Windows Vista, icacls is a more versatile tool, and should be used
for most ACL editing tasks, although it isnt currently available for older operating systems.
As it turns out, if youre running with SERVICE_SID_TYPE_RESTRICTED, then you can end
up creating named objects that you dont have enough access to. When you create persistent
objects, the ACL will depend on the inherited permissions from the parent directory or registry key. When creating named objects, the permissions are still inherited, but are inherited
from the BaseNamedObjects, and this varies depending on whether it is an event, a named
pipe, or some other object. BaseNamedObjects can be thought of as the root directory for
non-persistent named objects. With most of the objects you can create in Windows, you can
specify a security descriptor at creation time, and if youre using SERVICE_SID_TYPE_
RESTRICTED, you may need to do so. In our sample application, when we set
102
Reducing Privileges
An ordinary process has a couple of options in terms of reducing unneeded privileges; for
example, the process could call AdjustTokenPrivileges on initialization, with a parameter of
SE_PRIVILEGE_REMOVED. Unfortunately, AdjustTokenPrivileges is one of the least usable API
calls you could ever encounter. The first problem is that it can succeed without doing anything. Only if it succeeds and GetLastError returns ERROR_SUCCESS did it successfully
adjust all the privileges you asked it to manipulatethis is even true if you only asked it to
change one privilege. If it fails to adjust all the privileges, trying to figure out which privileges
werent adjusted will mean some tricky code: the privileges that worked will end up being
written into the parameter for the previous state. Another annoying aspect of AdjustTokenPrivileges, at least when used to disable privileges, is that it works backward from how a good security operation should behave: it disables known bad and doesnt just enable known good. If
youre clever (and determined), you can write code to obtain the list of privileges available to
the process, then remove only the privileges youd like to keep from your list, and then remove
the remaining privileges.
A second approach would be to try and run the service using a restricted token. Unfortunately, you dont control the SCM code that starts the service process and trying to do this by
hand would be difficult and error-prone. Then theres the fact that CreateRestrictedToken is difficult to use; youre likely to encounter problems, such as being able to create objects that your
process doesnt have enough access to; and you could have problems with COM objects. Additionally, you have the same problem as with AdjustTokenPrivileges in that the API asks for the
Chapter 5
103
privileges youd like to disable, not the privileges youd like to keep. We strongly recommend
not taking the CreateRestrictedToken approach in a service, although it may be useful if your
service has to launch helper processes, which is what we did with the Windows Search feature
shipping in Vista. Even so, weve had a number of learning experiences along the way.
Fortunately, a new information level was created for the ChangeServiceConfig2 API, and we
have a relatively easy way to specify the privileges the service needs. Even better, the API takes
the list of privileges you needenable known good, never try to disable known bad. Youll
always forget something, or theyll invent a new, dangerous privilege. It even gets better! The
way that this works in the operating system is that the privileges are kept as a multistring in
the registry under the RequiredPrivileges value, so you could tinker with these on the fly without recompiling your application by using the sc privs command. Our only complaint is that
multistrings are a little tricky, and wed have preferred just passing in a TOKEN_PRIVILEGES
structure, but since you can just use our sample code, this isnt really a problem. Heres the
sample:
DWORD SetServicePrivs( SC_HANDLE hSvc, const WCHAR* wzPrivs[], DWORD cPrivs )
{
// The call to set the privileges for a service requires you to pack
// them into a multi-string, which is a series of strings in the same
// buffer that have a double-null terminator
size_t cchTotal = 0;
DWORD i;
for( i = 0; i < cPrivs; i++ )
{
// Add up the length of all the input strings, including the null
cchTotal += wcslen( wzPrivs[i] ) + 1;
}
// Add one more for the final null
cchTotal++;
SERVICE_REQUIRED_PRIVILEGES_INFO ReqPrivs;
const WCHAR wzMultiStringNull[2] = { '\0', '\0' };
if( cchTotal > 1 )
ReqPrivs.pmszRequiredPrivileges = new WCHAR[ cchTotal ];
else
ReqPrivs.pmszRequiredPrivileges = wzMultiStringNull;
WCHAR* wzTmp = ReqPrivs.pmszRequiredPrivileges;
if( wzTmp != NULL )
{
DWORD dwErr = ERROR_SUCCESS;
if( cchTotal > 1 )
{
for( i = 0; i < cPrivs; i++ )
{
size_t cchPriv = wcslen( wzPrivs[i] ) + 1;
wcscpy_s( wzTmp, cchTotal, wzPrivs[i] );
wzTmp += cchPriv;
cchTotal -= cchPriv;
}
104
Note that this service didnt really require the right to create named objects in the global
namespace, but it was a benign privilege we could add to prove that the code actually works
as advertised with multiple privileges. If you had given an empty list of privileges, youd still
end up with SE_CHANGE_NOTIFY_NAME (right to bypass traverse checking) enabled,
because it is a benign right, and a lot of things will break if you dont have it enabled. Heres a
list of privileges currently defined, along with some commentary specific to services.
High-Level Privileges
Dont enable any of these privileges unless you have a need to do so, and understand why
youre doing it. If you have to have any high-level privileges, try to use them during initialization, and then call AdjustTokenPrivileges to drop them once youve completed initialization.
SeAuditPrivilege, which is needed to generate entries in the security log. The security log
now has configurable ACLs, which may be a better approach than assigning this privilege. The user-friendly test is, Generate security audits.
SeBackupPrivilege, which essentially disables access checking on files for the entire
process. Unless youre writing backup software, dont ever enable this privilege. The
user-friendly text is, Back up files and directories.
Chapter 5
105
SeDebugPrivilege, which allows a process to open any other process for debugging. Any
process with this privilege can compromise the operating system and should never be
enabled in any service, except an actual debugger. The user-friendly string is, Debug
programs.
SeLoadDriverPrivilege, which is needed to load and unload device drivers. A device driver
can completely compromise the operating system, and this privilege shouldnt be enabled
for any typical service. The user-friendly name is, Load and unload device drivers.
SeSecurityPrivilege, which is needed to set an SACL (system audit access check), and
enables manipulation of system auditing parameters. Because this privilege has strong
security implications, it is dangerous and should only be used when absolutely required.
In general, it is preferable to use the configurable ACLs on the event logs to delegate the
right to read events than by using this privilege. The user-friendly string is, Manage
auditing and security log.
SeSystemEnvironment, which allows the process to manipulate system environment variables. A process with the ability to manipulate the system environment variables, notably the path settings, can have strong effects on the rest of the system. The user-friendly
string is, Modify firmware environment values.
106
SeSystemtimePrivilege, which is required to modify the system time of day, although not
the time zone, which is a change for Windows Vista. The system time has implications
for Kerberos tickets, and unless youre replacing the Windows Time service, dont enable
this privilege.
SeTcbPrivilege, which allows a process to act as part of the operating system. Although the
details are somewhat arcane, a process with this right can compromise the operating system and should never be granted to a typical service. Under Windows 2000, this privilege was required to call LogonUser, which led to several escalation of privilege points.
On later version of Windows, you dont need this privilege if you know the users password. The user-friendly string is, Act as part of the operating system.
Benign Privileges
The following privileges typically wont allow someone to compromise the operating system,
but only enable these if you need them. Many of these illustrate our point that disabling
privileges you think may be bad can get you into trouble if you start running on a domain
controller and find your service has privileges you hadnt even known about.
SeChangeNotifyPrivilege, which is always enabled, even if you try to disable it. The userfriendly string is, Bypass traverse checking.
Chapter 5
107
SeIncreaseQuotaPrivilege, which is needed in some cases when calling CreateProcessAsUser and is needed by SetSystemFileCacheSize. The user-friendly name is, Adjust memory quotas for a process.
SeProfileSingleProcessPrivilege, which is needed to use some of the performance monitoring features. This privilege would typically have few security implications, but as always,
dont enable it unless required. The user-friendly string is, Profile single process.
SeShutdownPrivilege, which is required to shut down the system. Your service typically
wont need to do this, but the implications are obvious. The user-friendly string is, Shut
down the system.
108
lead to excessive permissionson my system there are 11 svchost.exe processes running, and
some of them are hosting as many as 20 services.
If youre familiar with COM, the firewalling API is relatively easy to use. There are only three
methods exposed by the INetFwServiceRestriction interface, defined in NetFw.h. There are a
few differences that are worth pointing out, especially considering that all of the examples in
the SDK are in VB script. First of all, in the older firewalling API, you first create a NetFwMgr
object, and call get_LocalPolicy to obtain a INetFwPolicy interface. One might (erroneously)
think that you could obtain an INetFwPolicy2 interface from the NetFwMgr object, or perhaps
call QueryInterface on an INetFwPolicy object to get the interface pointer. Because the SDK
doesnt currently document just how you might get an INetFwPolicy2 pointer, you could try a
number of approachesthis author did. As it turns out, the critical clue is in the header, where
youll find a CLSID defined for INetFwPolicy2. Heres what the code looks like:
INetFwPolicy2* pFwPolicy2 = NULL;
hr = CoCreateInstance( __uuidof(NetFwPolicy2),
NULL, CLSCTX_INPROC_SERVER,
IID_INetFwPolicy2,
(void**)&pFwPolicy2 );
Once you have an INetFwPolicy2 pointer, the next step is to get an INetFwServiceRestriction
pointer that will give access to the following methods:
Chapter 5
109
Remember that both SvcName and AppPath are BSTR types and will need to be initialized with
SysAllocString and freed with SysFreeString. If your application isnt running with the administrator group enabled, RestrictService will fail with access denied, but the system does nicely
allow you read access when running as an ordinary user.
If you happen to already be familiar with the IEnumVARIANT type, dealing with the firewall
rules is no problem, but if you arent, heres how it works:
INetFwRules* pFwRules = NULL;
hr = pSvcRestrict->get_Rules( &pFwRules );
if( SUCCEEDED( hr ) )
{
long iCount = 0;
hr = pFwRules->get_Count( &iCount );
if( SUCCEEDED( hr ) )
printf( "There are %d rules\n", iCount );
IEnumVARIANT* pEnum = NULL;
hr = pFwRules->get__NewEnum( ((IUnknown**)&pEnum) );
if( SUCCEEDED( hr ) )
{
VARIANT Var;
long i;
for( i = 0; i < iCount; i++ )
{
hr = pEnum->Next( 1, &Var, NULL );
if( SUCCEEDED( hr ) )
{
INetFwRule* pRule = NULL;
// Some error checking omitted for clarity
IUnknown* pIu = (IUnknown*)Var.ppunkVal;
hr = pIu->QueryInterface( __uuidof( INetFwRule ),
(void**)&pRule );
BSTR desc;
hr = pRule->get_Description( &desc );
printf("Rule %d: %S\n", i, (WCHAR*)desc );
pRule->Release();
}
}
}
pEnum->Release();
}
This starts out simply: you just call get_Rules to get an INetFwRules interface, and we can then
straightforwardly get the number of rules. Once you have the count, you can then acquire
your IEnumVARIANT pointer. The tricky bit is remembering that you have to use the ppunkVal
member of the VARIANT structure and then call QueryInterface to get access to the firewall rule
itself. Heres what some sample output looks like:
Service is restricted
There are 2 rules
Rule 0: Block all outbound traffic from service VistaService
Rule 1: Block all inbound traffic to service VistaService
110
Our sample service has no business wandering around opening random sockets and doing
silly things, so the default rule set works quite nicely. If your service needs to do something
more complex, we covered managing rule sets in Chapter 4.
At the time this book was written, the original source URL for Pagets paper
was not working. For a more comprehensive article, see the Wikipedia reference at http://
en.wikipedia.org/wiki/Shatter_attack.)
A second side effect of sharing console 0 between the console user and services is that named
objects can have namespace collisions. If youre familiar with the Saltzer and Schroeder design
principles (Saltzer and Schroeder 1975), this is a failure of the principle of least common
mechanism, which implies that functionality and services provided to multiple users should
have a proper level of separation. Namespace collisions have two consequences: the first is
that you can end up with access controls that are much too open than would be best, and the
second is that name squatting attacks can be launched. For our sample service, if someone
could pre-create the WSC_Vista_Pipe, then instead of getting ridiculous developer excuses,
you could have different rude messages delivered, and worse yet, until impersonation became
a privilege, the process running the imposter pipe could have impersonated the client.
The first step toward resolving the problems with console 0 was to institute a separation of
local versus global namespaces. On Windows XP in fast user switching mode, secondary
logons will create objects in local namespaces; on Windows Server 2003 with terminal services in application server mode, all remote logons create named objects in local namespaces,
although the console user still shares the global namespace with services. Windows Vista
extends this by placing all console logons outside of session 0, even though you could rightly
argue that there is still an incomplete enforcement of least common mechanism. Services do
Chapter 5
111
run under a number of different user contexts, and even though services can only be installed
by administrators, scheduled tasks (created only by administrators by default) also run in session 0. Ideally, we should break namespaces into per-user directories, but since all of the applications with potential collisions are controlled by administrators by default, theres a
reasonable performance-security trade-off.
Windows XPnot domain joined, if youre using fast user switching, and youre
not the first user to log on
Windows Vista and laterwill always have a local namespace, unless youre running as a service
We patched Office 2003 to create a much more restrictive ACL on the shared-memory
sections when an available local namespace is found.
Consequently, weve been telling developers for some time not to allow services to interact
with the desktop in an unsafe manner, and these services are now broken. If your service
depends on having the interactive flag set, placing windows on the desktop, and receiving or
112
sending window messages to and from the console user, then youre going to need to find a
safer way to do these tasks.
Here are some of the options you havethere is no single right answer, and the method you
use depends on what youre trying to do and your performance constraints. A common tradeoff youll see as we present the various options is that the higher the performance of the mechanism, the more difficult it is to secure; conversely, the easier it is to provide security, the lower
the performance will tend to be.
Shared Memory
You create a shared memory section by using CreateFileMapping with a first argument of
INVALID_HANDLE_VALUE, which creates a memory section that isnt backed by an actual
file. You can use shared-memory sections as an extremely high performance way to transfer
large amounts of data, and once the section is open, you just read and write memory. The
problems youll face are that synchronizing access is difficult; theres no way built into shared
memory to ensure atomicity. You can utilize events or mutexes to try to coordinate access, but
this depends on well-behaved clientsand thats rarely a good option for security. A second
problem is that you will also have problems setting a correct ACL unless youre essentially
using shared memory as a local broadcast mechanism. Most importantly, theres nothing that
enforces the format of the data, and youll need to ensure that any parsers are extremely
robust. Although we havent tested this, you may also run into the same problem we did with
our sample when we set SERVICE_SID_TYPE_RESTRICTED, and youll need to create an
explicit ACL for the section on creation. Unless you have some unusual performance and
application requirements, wed tend to discourage this approach.
Named Pipes
Pipes are a good choice for service-client communication, especially if your service needs to
perform actions on behalf of the user. Impersonating a client is as easy as calling ImpersonateNamedPipeClient and being sure to always check the return. Once a connection is established,
named pipes also allow the client and server to have privacy for local connections. If you want
to enforce privacy and integrity across the network, your options are to configure IPSec or to
Chapter 5
113
use cryptography to protect the data. For reasons well discuss in a moment, named pipes
arent an ideal mechanism for network communications, so you may want to avoid using
named pipes on the network.
Using named pipes is fairly easy: on the server side, you call CreateNamedPipe to create as
many instances of the pipe as the number of clients youd like to support, and then call ConnectNamedPipe to wait on clients to connect. Once connected, you call ReadFile and/or WriteFile to copy data to and from the pipe. The usual set of nonblocking capabilities is present
you can either use overlapped I/O and wait on events, or you can use I/O completion ports.
Using named pipes with events can be a little tricky, especially if you dont want to have one
thread per pipe, which is somewhat wasteful. With a service, wed also need a few events to
deal with control messages. Thus, by the time you get around to dealing with unexpected state
transitions, the whole thing is fairly messy. With an I/O completion port, we can use one port
to deal with all the pipes and control messages at once and serve all of the clients with one
thread. Heres the control handler within the worker thread for our sample service:
if( GetQueuedCompletionStatus( g_hPort, &dwBytes, &CompletionKey,
&pOverlapped, INFINITE ) )
{
if( CompletionKey == ControlKey )
{
// Handle control messages here
switch( dwBytes )
{
case SERVICE_CONTROL_STOP:
goto CleanUp;
case SERVICE_CONTROL_PAUSE:
g_CurrentStatus.dwCurrentState = SERVICE_PAUSED;
if( SetServiceStatus( hStatus, &g_CurrentStatus ) )
{
fPaused = true;
continue;
}
goto CleanUp;
case SERVICE_CONTROL_CONTINUE:
g_CurrentStatus.dwCurrentState = SERVICE_RUNNING;
if( SetServiceStatus( hStatus, &g_CurrentStatus ) )
{
fPaused = false;
continue;
}
goto CleanUp;
default:
WriteLogFile( "Unexpected control", dwBytes );
continue;
}
}
...
As you can see, this is a very simple piece of code. One gotcha to remember is that if you set
the service status to SERVICE_PAUSE_PENDING in the control handler routine, and if you
114
forget to subsequently set the status to SERVICE_PAUSED, the service control manager will
time out your service and refuse to pass any further control messages to it. Youll end up
restarting the system or killing the process with Task Manager. Note that when you use PostQueuedCompletionStatus to a completion port, all three of the arguments passed in are completely arbitrary: dwBytes, the completion key, and the OVERLAPPED pointer could be
anything that fits into the size being passed. In this case, we used a completion key that was
an index into the arrays used to maintain state for the pipes, and a key of ControlKey
(0xffffffff) could be used to mean we had a special event posted to the port. The pipe handling
code is nearly as simple; the bulk of it follows:
switch( g_PipeState[CompletionKey] )
{
case PipeStateStart:
assert( false );
break;
case PipeStateWaitConnect:
// With a completion port design,
// it's hard to be truly paused
// We could do it with an event, but it's
// more polite to the client to just reject them
if( fPaused )
{
DebugState( g_PipeState[CompletionKey], PipeStateReset );
g_PipeState[CompletionKey] = PipeStateReset;
break;
}
// Let's log the connection info
// This consists of the remote computer name,
// client process ID, and client session ID
// If we wanted to maintain state, we could use these to limit
// the session to one pipe instance.
LogConnectionInfo( g_hServicePipe[CompletionKey] );
if( !WritePipe( g_hServicePipe[CompletionKey],
g_PipeState[CompletionKey],
g_Overlapped[CompletionKey] ) )
{
DebugState( g_PipeState[CompletionKey], PipeStateReset );
g_PipeState[CompletionKey] = PipeStateReset;
}
break;
case PipeStateWritePending:
// To do - add more error checking here
// On success, write completed
DebugState( g_PipeState[CompletionKey], PipeStateWaitDisconnect );
g_PipeState[CompletionKey] = PipeStateWaitDisconnect;
break;
case PipeStateWrite:
DebugState( g_PipeState[CompletionKey], PipeStateWaitDisconnect );
g_PipeState[CompletionKey] = PipeStateWaitDisconnect;
continue;
case PipeStateWaitDisconnect:
continue;
case PipeStateError:
Chapter 5
115
fDone = true;
break;
}
The one feature pipes are missing that could make them difficult to use across a network is
that theres no way to know when the client has read all of the data. Once we call WriteFile to
write the data to the pipe, we might assume that when this has returned then the client would
have read the data out of the pipe; thus we ought to be able to call DisconnectNamedPipe to disconnect the client from the pipe. Unfortunately, just because we were able to write data into
the pipe only means that the pipe buffer had room to hold a few bytes. It says nothing about
whether the client has read data and cleared the pipe. What we can do is call ConnectNamedPipe, and the return will tell us if the client has closed the connection on its end. If we wanted
to make our server a little more complex, we could let clients write an ACK into the pipe once
the client was ready for more data, but this still doesnt help us against a misbehaved client
that just opens the pipe and then hangs. If youre familiar with sockets programming, what
wed like to be able to do is to send the client a shutdown message that tells them to get the
data now, or lose it, but thats not available to named pipes.
What can we do to avoid denial of service attacks against our pipe server? There are some
really cool new APIs available that can help with this. Weve demonstrated these in the following code snippet:
void LogConnectionInfo( HANDLE hPipe )
{
char tmp[1024];
WCHAR wzComputerName[256];
ULONG ClientProcessId, ClientSessionId;
if( !GetNamedPipeClientComputerName( hPipe,
wzComputerName,
sizeof( wzComputerName ) ) )
{
wcscpy_s( wzComputerName, _countof( wzComputerName ), L"Unknown" );
}
if( !GetNamedPipeClientProcessId( hPipe, &ClientProcessId ) )
ClientProcessId = 0;
if( !GetNamedPipeClientSessionId( hPipe, &ClientSessionId ) )
ClientSessionId = 0;
}
In this case, we just write the information to the debugging log, largely as an example of how
to use these new functions. GetNamedPipeClientComputerName will fail if the client is local and
will otherwise tell you the name of the remote system. Please remember that all of the naming
systems available are prone to corruption and can be unreliable even when youre not under
attack, but its better than nothingand it will reliably distinguish local and remote.
The next two APIs are more intriguing and allow you to do some interesting things. If you call
GetNamedPipeClientProcessId, then you could do some checking up on that process. For example, say you wanted your desktop client to be the only code to connect to your server. Most of
the current code that were aware of to do this depends on unreliable security by obscurity to
116
perform some sort of handshake or, worse yet, uses an encrypted stream with a fixed key in
the binaries. This is not encryption, nor is it security. Now that we can reliably get the process
ID on the other end, we can see the path to the process; check the signature on the file given
as the process binary; and, if we wanted to be complete, check the modules loaded in the process. In the case of the sample application, we could have guarded against denial of service
attacks by calling GetNamedPipeClientSessionId and enforcing a rule that each session can only
have one pipe instance running at any time. Now the user could create a denial of service
conditionon only themselves! All three of the previously mentioned APIs also have a server
variant that allows a client to find out the same information about the server end of the pipe.
There are two other ways that we could have guarded against denial of service attacks. The first
would be to impersonate the pipe client long enough to identify the user and then maintain a
per-user pool of pipes. A user could deny itself services, but not other users. The lsass named
pipe uses exactly this scheme, which came about after this author demonstrated an annoying
attack tool against beta versions of Windows 2000. The second approach that could be used if
the pipe accepted anonymous clients would be to maintain a timer on a pipe instance once it
had written its final data into the pipe and then just forcefully drop the client once the time out
expired. If you wanted to get a little more sophisticated, you could only drop timed-out clients
once you ran out of available pipe instances, which might save you from creating problems
with clients on very slow or congested networks or with local clients on a very busy system.
The SDK recommends using FlushFileBuffers to cause the pipe server to block until the client
has read all the data, but were not in favor of servers (or even clients) that make blocking calls.
Named pipes have very good performance characteristics and allow for synchronization, as
well as a solid separation of the various clients, but it places no restrictions on the format of
data passed between the client and the server. You will need to carefully validate any parsers
that deal with data being read from the pipe. As an example, our sample client makes the following mistake:
if( ReadFile( hPipe, wzMsgBuf, 1024, &dwRead, &ol ) && dwRead > 0 )
{
printf( "%S\n", wzMsgBuf );
}
The problem here is that this code makes the assumption that the incoming message is null terminated. If the client connected to an evil server, the printf statement could crash with a read
access violation (AV). A more robust piece of code would call wcslen_s to check and see if a null
was encountered within wzMsgBuf, or simply check that wzMsgBuf[ dwRead-1] was a null character. Being able to validate the data types passed to your service is one of the primary advantages of RPC over named pipes, although at a cost of some additional development time.
Sockets
Sockets are meant to be used in hostile environments and have a full set of protections against
denial of service conditionsnetworks can do some nasty things, even when there arent any
Chapter 5
117
attackers causing trouble. We could have easily written the preceding application to work
with a socket listening on the localhost address (127.0.0.1 in IPv4 or 0:0:0:0:0:0:0:1 in IPv6),
and it would not have been prone to denial of service attacks. Although I/O completion ports
and sockets arent overly well documented, theyre a great technique for writing high-performance servers.
What you lose with sockets is the ability to easily authenticate or impersonate clients. This can
be done with the Security Support Provider Interface (SSPI) set of API calls, but SSPI isnt one
of the easier API sets to use. Sockets also have no idea of the process or session that originated
a connection, although the remote server can be determined. We can also do this with some
of the new socket extensions, but it still takes a lot more work than just using a pipe. Sockets,
like named pipes, do nothing to validate whether the incoming data stream is well formed,
and the same advice about making sure your parsers are well hardened applies.
RPC/COM
Remote procedure calls are an extremely robust form of interprocess communication. With
RPC, you get it allbuilt-in authentication, the ability to identify and impersonate clients, builtin functionality to maintain privacy and integrity of network communications, and a rich way
to define the data types being passed. All this and asynchronous capabilities too!
The catch is that RPC programming is difficult, and a specialty area. Its also complex enough
that you can make a lot of mistakes, security and otherwise, when using RPC. We spent
30 pages discussing RPC security issues in the second edition of Writing Secure Code. Not only
is it difficult to program, but RPC is also going to have more overhead than any of the techniques mentioned so far. If all you need to do is something simple, RPC is tremendous overkill.
COM (or DCOM) is just a programmer-friendly wrapper over RPC, and you could quite easily
create a COM server, perhaps with ATL, and then a COM client to talk to the service. You get
all of the features of RPC with much less work on your part. The downside is that you get a lot
more overhead, and you get to worry about random applications trying to instantiate your
server. If you choose this approach, you might want to go back and review our RPC/DCOM
chapter in the second edition of Writing Secure Code. DCOM has a number of security implications and decisions, and you may also need to worry about the client impersonating the server
if you choose to use callbacks (also known as sources and sinks). Lastly, be sure you implement all of your interfaces exactly to specifications; if you dont, an application that can load
arbitrary COM objects may get into trouble if your component misbehaves.
118
We couldnt find a simple way to map the \Device paths to drive letters, so one solution is to
assume that the services current directory is where the binary exists. You can then first call
GetCurrentDirectory, followed by GetModuleBaseName to append the binary name to the path.
Note that simply using argv[0] will only give you what the user typed, not the full path.
Another issue that caused some consternation was that because wed written the service to
install and delete itself from the command line, and because both of these actions require
administrator rights, it would seem to make sense to embed a manifest such as the one we
documented in Chapter 2 and make the application always run as administrator. This works
great as long as youre running the service as local system, but as soon as you try and drop it
down to local service, starting the service returns ERROR_ACCESS_DENIED. Neither sc.exe
nor the Control Panels services applet bothers to tell you just where this access denied error
happensit could be accessing the service control manager or the service itself, or it could
even be that the service fails and sets ERROR_ACCESS_DENIED as the error code. After writing some custom code to step through controlling the service, we observed that it was StartService that failed and that the debugging log was never createdwhich tells us ServiceMain
was never executed. The conclusion is that you should create a Visual Studio command
prompt running as administrator to install and remove your service and not embed a manifest
in a service executable that causes it to always run as administrator.
The third problem we encountered was mentioned previously, but its worth mentioning
again. If you restrict the service processs token, your process may not have enough access to
the objects it creates, and youll need to supply an ACL at creation time. This can be difficult
to debug, because the handle returned when you create something will have full control
access; you can do anything you like with that handle, but if you go to reopen the object to get
a second handle, you can be denied access. Once youve seen the problem, its relatively easy
to fix. If you suspect something like this is the problem, create a SECURITY_ATTRIBUTES
structure, attach an initialized SECURITY_DESCRIPTOR to it, and then call SetSecurityDescriptorDacl to set a NULL DACL with bDaclPresent set to true. If whatever it is now works, you
know it is an ACL problem. You must not leave the code this waythis is for debugging purposes only! You should then set a proper ACL on the object just as we did in the CreatePipeDacl function in the sample. We hope that our documenting these issues will save you some
time and frustration.
Call to Action
Run your service as Local Server or Network Service, not Local System.
Restrict the token for your service. Be sure to work out the ACLs needed for your objects
first!
Use the service account SID in the access controls for the services resources.
Chapter 5
Think about how your service will communicate with the desktop, and use correct
mechanisms.
119
References
(Humphrey 2006) Software Engineering Institute: Biography, Watts S. Humphrey. http://
www.sei.cmu.edu/tsp/watts-bio.html.
(Paget 2002) Paget, Chris (aka Foon).Exploiting design flaws in the Win32 API for
Service, http://support.microsoft.com/default.aspx?scid=kb;en-us;115825.
Saltzer, Jerome H, and Michael D. Schroeder.The Protection
of Information in Computer Systems, http://web.mit.edu/Saltzer/www/publications/
protection/.
Chapter 6
122
was removed: your online bank would not work; itd be much harder to make an online hotel
reservation or make an online purchase. Users have learned to expect a certain level of functionality, and taking that away would incite riots.
At Microsoft, we recognize that browsers will have security bugs, and attackers want to exploit
them. Hand-to-hand combat with individual browser security bugs is a losing proposition. For
Windows Vista, then, we decided to continue, and substantially enhance, the Internet Explorer
defensive work started in Windows XP SP2. Some of these defenses and changes can be used
by developers building on top of Internet Explorer 7, and others pervade the browser and have
an impact on the way developers build software and how code runs within the browser.
Important
In the previous paragraph we used the words, browsers will have security
bugs. But the problem extends beyond bugs in browsers; if you create any form of browser
extensibility, then your code can be attacked also using the same conduit of attack: the Web.
This chapter is divided in two large sections. The first explains the major Internet Explorer
defenses and how they affect developers. The second portion explains the security features in
Internet Explorer that developers can take full advantage of.
Finally, a third, smaller section that covers some Internet Explorer 7 changes that may pose
issues with your application.
Pervasive Defenses
On Windows Vista, Internet Explorer 7 has many defenses designed to protect the user from
malicious software and malicious Web sites. Some of these defenses also exist in Internet
Explorer 7 on down-level Windows platforms.
User Account Control (UAC) aside, the three prime defenses in Internet Explorer 7 in
Windows Vista are:
ActiveX opt-in
Protected Mode
ActiveX Opt-in
A critical defense in Internet Explorer 7 is ActiveX opt-in. By default, most previously installed
ActiveX controls will not run inside Internet Explorer until the user opts to use that control the
first time the control is referenced. By disabling most ActiveX controls on the system by default,
Internet Explorer reduces the amount of code that can come under attack while still letting a
user have the powerful experiences you can get with ActiveX content like Adobe Flash.
Users often have controls on their machines they simply dont know they have, so the ActiveX
opt-in capability brings first use to the attention of the user. The net effect to you as a
Chapter 6
123
developer is that when users upgrade their machines to Windows Vista and attempt to use a
control your Web application uses, it will fail until they opt to use the control.
Scenario
IE6
IE6 Windows XP
SP2
IE7 Windows XP
SP2
IE7 Windows
Vista
Unsigned ActiveX
downloads
Blocked silently
Blocked silently
Blocked silently
Blocked silently
Signed ActiveX
downloads
Prompt
Preinstalled
ActiveX controls
Run silently
124
Protected Mode
Of all the defenses in Internet Explorer 7 in Windows Vista, Protected Mode is probably the
biggest architectural changeits the newest too. Protected Mode relies on Integrity Levels in
Windows Vista, which we covered in depth in Chapter 2, User Account Control, Tokens, and
Integrity Levels. In short, the browser process facing the Internet and rendering all that complex and potentially dangerous markup and code runs at low integrity because the content
can never be trusted.
Note
In general, HTML content should not behave any differently when Protected Mode is enabled.
Things get a little trickier for extensibility products such as ActiveX controls, toolbars, and
browser helper objects that may need to write to the file system or registry. Most, if not all,
write operations to the operating system will behave differently when Protected Mode is
enabled because Internet Explorer is running at low integrity, and the majority of Windows
Vista objects are labeled at medium or higher integrity.
Note
Table 6-2 outlines some of the new function calls in Internet Explorer that help extensibility
components behave correctly when Protected Mode is enabled. To use these functions you
must include <iepmapi.h> and link with iepmapi.lib.
Table 6-2
Function
Comments
IEIsProtectedModeProcess
IEShowSaveFileDialog
Displays the Save File dialog box. The process that displays
the dialog box is not the low-integrity Internet Explorer, it
is a sister process running at medium integrity. If the
dialog operation succeeds, your code must free the path
buffer (lppwstrDestinationFilePath) with CoTaskMemFree.
IEGetWriteableFolderPath
SHGetKnownFolderPath
(FOLDERID_LocalAppDataLow, . . .)
IESaveFile
IEGetWriteableHKCU
Chapter 6
125
Comments
IEIsProtectedModeURL
IELaunchURL
The IEShowSaveFileDialog function is interesting because the dialog is created by a mediumintegrity helper process named IEUser.exe rather than by the low-integrity IExplore.exe rendering content from the Internet. The reason for launching the dialog from a higher-integrity
process is to let users get their jobs donea browser that does not allow users to save files
would be frustrating to use for most users, and a low-integrity process cannot save files to anywhere useful or convenient. Note the user must initiate the file save operation.
The following code shows how to use some of the new APIs to write data from an application
hosted in the browser to a location in the users registry that can be written to by low-integrity
Internet Explorer.
HRESULT WriteHKCUSetting(__in_z wchar_t *pwszKey,
__in_z wchar_t *pwszValue,
__in_z wchar_t *pwszData) {
BOOL bIsProtected = FALSE;
HRESULT hr = IEIsProtectedModeProcess(&bIsProtected);
if (SUCCEEDED(hr) && bIsProtected) {
HKEY hLowKey = NULL;
hr = IEGetWriteableHKCU(&hLowKey);
if (SUCCEEDED(hr)) {
HKEY hMyKey = NULL;
DWORD dwDisposition = 0;
LONG lRes = RegCreateKeyEx(
hLowKey,
pwszKey,
0L,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE,
NULL,
&hMyKey,
&dwDisposition);
if (ERROR_SUCCESS == lRes) {
lRes = RegSetValueEx(hMyKey,
pwszValue,
NULL,
REG_SZ,
(CONST BYTE*)pwszData,
wcslen(pwszData + 1));
hr = HRESULT_FROM_WIN32(lRes);
RegCloseKey(hMyKey);
126
And the following code will launch IE in the correct mode for the URL.
HRESULT LaunchIE(__in_z LPCWSTR pszURL) {
PROCESS_INFORMATION pProcInfo;
HRESULT hr = IELaunchURL(pszURL, &pProcInfo, NULL);
if (SUCCEEDED(hr)) {
CloseHandle(pProcInfo.hProcess);
CloseHandle(pProcInfo.hThread);
}
return hr;
}
Important
Chapter 6
127
applications is Internet Explorer. The reason DEP is not enabled by default is that Internet
Explorer, like all modern graphical browsers, can host other software such as Java applets and
various plug-ins and objects, and many of these applications fail when DEP is enabled
because they may have self-modifying code or use just-in-time (JIT) compilation.
Figure 6-1
Using the Internet Explorer Compatibility Test Tool to analyze Protected Mode failures.
Note
Applications that use older versions of the Active Type Library (ATL) also fail to run
correctly when DEP is enabled because an ATL-generated function thunks on the fly. You
should make sure you update your code to use ATL version 8.0 or higher. You can enforce
this by adding this line early in your code: assert(AtlGetVersion(NULL) >= 0x0800);
128
Figure 6-2
At the time of writing, two of the major non-Microsoft tools used in Internet Explorer, Adobe
Flash Player v9.0.28.0 and Adobe Acrobat Reader v8.0.0, had just been released, and they
both support DEP and run correctly in Internet Explorer when DEP is enabled.
Chapter 6
129
if (pBuff) {
memcpy(pBuff,scode,sizeof scode);
// Well uncomment this code later
// DWORD dwOldProtect = 0;
// VirtualProtect(pBuff,sizeof scode,PAGE_EXECUTE_READ, &dwOldProtect);
RunShell shell = (RunShell)(void*)pBuff;
(*shell)();
VirtualFree(pBuff,0,MEM_RELEASE);
}
return 0;
}
When you run it, youll see an instance of calc.exe pop-up. As you can probably guess, thats
what the shellcode defined in scode does. There is no buffer overrun in this code; the code is
simply calling into a buffer copied onto the heap.
Next, link the code with the /NXCOMPAT linker option. If youre using Visual Studio 2005,
then you need to perform the following steps:
1. Right-click the project name, and then click Properties.
2. Expand Configuration Properties.
3. Expand Linker.
4. Click Command Line.
5. In the Additional options text box, type /NXCOMPAT.
6. Click OK.
Now rerun the code, and youll get an access violation on the line that calls the shellcode,
(*shell)(). This is DEP working, assuming that your processor supports DEP, and has it
enabled in the BIOS.
But lets say your code has a valid reason to run data even though you have linked with
/NXCOMPAT. You can mark the virtual memory as readable and executable by uncommenting the two commented lines of code in the previous sample code. Recompile the code and
run it, and youll see calc.exe appear. Your application is still protected by DEP, but now your
code has a little more flexibility, and can run correctly even with DEP enabled. The call to
VirtualProtect marks the virtual memory used to launch calc.exe as readable and executable,
so the code runs correctly. Of course, you should use VirtualProtect sparingly and not for all
memory allocations (only for those allocations must run code), otherwise DEP will offer little,
if any protection.
Important
At some point in the future DEP will be enabled by default in 32-bit Internet
Explorer. Its enabled by default for 64-bit Internet Explorer today, but you should prepare for
the 32-bit eventuality now.
130
Although a few exceptions exist for compatibility purposes, cURL is generally aggressive
at rejecting URIs that are invalid under the latest standard covering the subject (RFC 3986).
To use cURL in your browser component, you should include <urlmon.h>, link with
urlmon.lib, and make sure you have the following line in your code before you load urlmon.h:
#define_WIN32_IE_WIN32_IE_IE70
The following code shows a basic way to use the cURL interface to create a URI, and break it
down into its requisite components.
int wmain(int argc, wchar_t* argv[]) {
if (argc != 2) {
wprintf(L "Please enter a URL");
return -1;
}
wchar_t *pwszUri = argv[1];
// Create an IUri object from the URI
IUri *pIUri = NULL;
HRESULT hr = CreateUri(
pwszUri,
Uri_CREATE_ALLOW_RELATIVE,
0,
&pIUri);
if (SUCCEEDED(hr)) {
BSTR bstrSchemeName;
if (SUCCEEDED(pIUri->GetSchemeName(&bstrSchemeName))) {
wprintf(L "Scheme: %s\n",bstrSchemeName);
SysFreeString(bstrSchemeName);
}
BSTR bstrHost = NULL;
if (SUCCEEDED(pIUri->GetHost(&bstrHost))) {
wprintf(L "Host : %s\n",bstrHost);
SysFreeString(bstrHost);
}
Chapter 6
131
DWORD dwPort=0;
if (SUCCEEDED(pIUri->GetPort(&dwPort))) {
wprintf(L "Port : %d\n ",dwPort);
}
}
if (pIUri)
pIUri->Release();
return 0;
}
Note also there is a new COM interface named IInternetSecurityManagerEx2 that accepts IUri
objects; use it instead of IInternetSecurityManagerEx and IInternetSecurityManager.
Various URL parsing functions, such as the following are now considered deprecated in favor
of cURL if you are using cURL:
InternetCrackUrl
ParseURL
UrlIsOpaque
UrlGetPart
UrlGetLocation
InternetCanonicalizeUrl
UrlCanonicalize
UrlUnescape
UrlEscape
InternetCreateUrl
CreateURLMoniker
The performance impact of cURL is generally no different from using other canonicalization
functions, but fewer function calls and therefore fewer redundant escape operations can lead
to performance benefits.
There are potential application compatibility side effects because Internet Explorer 7 uses
cURL internally and has much stricter URL parsing; some nonvalid URL sequences that used
to work may no longer work in script or browser add-ons.
132
get the originating URL, and pass it to cURL or one of the legacy URL cracking APIs and then
do a string comparison to determine if the control is activated from your Web site or not.
Script URLs
Embedding script into a URL attribute works differently in Internet Explorer 7. For example,
the following snippet:
<html>
<a href= "javascript:alert('click');">click</a>
</html>
Notably, the following use of a script URI no longer executes at all because JavaScript is not a
valid source for IMG tags:
<html>
<IMG SRC= "javascript:alert('bang');">
</html>
Chapter 6
133
and no longer supported in Windows Vista. And because SSL2 is so weak, it is disabled by
default in Internet Explorer 7. If your Web site uses only SSL2 or PCT, it will fail to work correctly with Internet Explorer 7, and in fact it wont work in Mozilla FireFox v2 either. If your
Web site requires SSL2, its time to upgrade the configuration! Windows Vista also disables
the weak 40- and 56-bit ciphers while adding new ciphers, including 256-bit Advanced
Encryption Standard (AES).
Window Origin
This change will have little effect on most Web applications. Any dialog box that pops up from
Internet Explorer 7 will automatically have an address bar with the originating address, which
helps the user determine if the Web page has come from a trusted Web site or not.
Tip
Do you want a good tool to view or manipulate HTTP headers and related information? Then use Fiddler from www.fiddlertool.com.
Call to Action
If theres only one thing you take away from this chapter, its this: if you build an extensibility
feature for a Web browser, such as an ActiveX control, a toolbar, or a browser helper object,
you need to realize that your code is as at risk as the browser itself. With that in mind you
should perform all the action items in Chapter 3, including these:
If your code generates code on the fly, or attempts to execute code from data, then you should
still link with /NXCOMPAT but call VirtualProtect(. . .,PAGE_EXECUTE_READ, . . .) on the
memory region you want to execute. You may also need to call the new Internet Explorer 7
Protected Mode file and registry function calls if you want to save data to the operating system
without virtualization.
Do not expect usersand do not force usersto disable Protected Mode to run code from your
Web site. If your code does not run in Protected Mode, even after making updates to the code,
then suggest that users add your site to the Trusted Sites zone. This will turn off Protected
Mode for your site.
Consider migrating URI canonicalization or parsing code to use cURL. Microsoft does not
expect developers to migrate code to use cURL overnight, however. But you should plan to do
so over time.
If your Web server still uses SSL2 or PCT, you should upgrade to use SSL3; otherwise your
Web applications will not work correctly with Internet Explorer 7 or FireFox 2.0.
134
As discussed in Chapter 2, you should code sign your ActiveX controls before they are
released to customers.
If your control is intended to be used only from your site, consider sitelocking the implementation so that it cannot be used from other Web sites. The best tool for the job is SiteLock.
Some functionality is removed or changed in Internet Explorer 7 in the name of security, such
as clipboard access and script URLs, so you should perform a complete application compatibility test of your Web application.
References
WC3 Recommendation. 13 Objects, Images, and Applets, http://www.w3.org/
TR/html4/struct/objects.html.
(W3C 1999)
(Microsoft 2006a) Microsoft Help and Support. How to Stop an ActiveX Control from Run-
(Microsoft 2006b)
(Howard and LeBlanc 2003) Howard, Michael, and David LeBlanc. Writing Secure Code,
2nd ed., Chapter 11, Canonical Representation Issues. Redmond, WA: Microsoft Press,
2003.
IUri Interface. http://msdn.microsoft.com/workshop/networking/moniker/
reference/ifaces/IUri/IUri.asp. MSDN.
(Microsoft 2006c)
(RFC 3986) Internet Engineering Task Force, Network Working Group. RFC 3986: Uniform
en-us/samples/internet/components/sitelock/default.asp. MSDN.
(Murray 2000) Murray, Eric. SSL Server Security Survey, http://www.megasecurity.org/
Chapter 7
Cryptographic Enhancements
In this chapter:
Kernel Mode and User Mode Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .136
Crypto-Agility. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .136
New Algorithms in CNG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .139
Using CNG. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .140
CNG and FIPS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .142
Improved Auditing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .143
Something Missing from CNG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .144
SSL/TLS Improvements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .144
Root Certificates in Windows Vista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .148
Deprecated Crypto Features in Windows Vista. . . . . . . . . . . . . . . . . . . . . . . . . . . . .148
Call to Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .148
References. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .149
All versions of Windows add new cryptographic capabilities, but in most cases its a few new
APIs or new algorithms. Windows Vista is different because Microsoft added a modern cryptographic infrastructure, called Cryptography API: Next Generation (CNG), that supports
new APIs and offers kernel and user mode support, better support for crypto-agility, new
cipher suites [most notably Suite B (NSA 2005)], and improved auditing.
Microsoft also improved Secure Sockets Layer and Transport Layer Security (SSL/TLS) with
new cryptographic algorithms and support for Suite B. In this chapter we will explain all these
subjects and more.
Important
We want to point out that this chapter will not explain how cryptographic
algorithms work, and this chapter will most certainly not turn you into a cryptographer!
CNG
136
New cryptographic innovation will occur in CNG and .NET, and CAPI 1.0 will eventually be
phased out. CAPI 2.0 will be supported because it is not a superset of CAPI 1.0. One could
argue that CAPI 2.0 is named badly. It is! CAPI 2.0 is a different functionality than CAPI 1.0,
CAPI 2.0 exists to manage and generate X.509 certificates and related standards; it does not
support low-level cryptographic primitives like CAPI 1.0 and CNG do.
Crypto-Agility
Cryptographic algorithms are constantly under attack on two fronts. The first is the ceaseless
increase in processor speed, making it more feasible that algorithms can be cracked in reasonable times, and the second is from ongoing cryptographic research that continually finds
weaknesses in algorithms. A good example of this research is weaknesses found in some
major hash functions; MD4, MD5 are now considered utterly insecure because of poor collision resistance, and SHA-1 is showing signs of serious weaknesses. In fact, Microsoft has
banned the use of MD4 and MD5, and SHA-1 in new code except for backward compatibility or if the algorithm is used in an industry standard, or in applications where the minimum Windows platform is anything older than Windows Vista.
More Info NIST has some excellent guidance about the use of SHA-1: Federal agencies
should stop using SHA-1 for digital signatures, digital time stamping and other applications
that require collision resistance as soon as practical, and must use the SHA-2 family of hash
functions for these applications after 2010. After 2010, Federal agencies may use SHA-1 only
for the following applications: hash-based message authentication codes (HMACs), key derivation functions (KDFs), and random number generators (RNGs). Regardless of use, NIST
encourages application and protocol designers to use the SHA-2 family of hash functions for
all new applications and protocols. (NIST 2006).
Chapter 7
Cryptographic Enhancements
137
Sometimes policy dictates the need for new algorithms; a great example would be the U.S.
federal Suite B requirements. If your application does not understand Suite B, but your
competitors do, then your lack of crypto-agility could mean lost sales.
The biggest mistake developers make is hardcoding the algorithm name in the code; if the
algorithm is weak and needs to be replaced, then the code has to be fixed to use the new
algorithm, data structures need to be updated, and finally a product update needs to be issued
to customers. In some cases, this kind of work can cause incompatibilities with older file
formats. The following code snippet, using Cryptographic API (CAPI), is an example of code
that is not crypto-agile because it has an embedded algorithm identifier:
if(CryptDeriveKey(hProv, CALG_DES, hHash, CRYPT_EXPORTABLE, &hKey)) {
// Encrypt data
}
A good example of crypto-agility is SSL and TLS, which securely negotiate cryptographic
algorithms between the server and client. If an algorithm is deemed weak, a simple algorithm
policy setting at the server or client will prevent that algorithm from being used. We discuss
these settings later in the chapter.
Note
It could be argued that SSL and TLS really dont have good crypto-agility because
the underlying random number generators are hardcoded to use SHA-1 and MD5!
Crypto-Agility in CNG
There are three major areas of improvement in CNG that make agility easier. First, all
cryptographic constants are strings rather than numeric constants. In CAPI, all cryptographic
algorithms are predefined in wincrypt.h. This makes it very hard to extend cryptographic functionality to suit your applications needs. Adding a custom symmetric encryption algorithm to
CAPIfor example, Serpent (Anderson 1999)is not easy. But this all changes in CNG
because you can use any string constant you want to define your algorithm, and when your
application attempts to use your algorithm, CNG will load the crypto-provider that registered
that name.
Crypto-agility in CNG extends beyond cryptographic primitives; you can also plug in custom
cipher-suites for SSL and TLS or customer envelopes for CMS and custom certificate elements.
Documentation outlining the steps to install and register a CNG add-in can be found in the
CNG SDK. The core function that adds a new add-in is BCryptAddContextFunctionProvider:
#define BCRYPT_SERPENT_ALGORITHM L"SERPENT"
status = BCryptAddContextFunctionProvider(
CRYPT_LOCAL,
NULL, // Default context
BCRYPT_CIPHER_INTERFACE,
138
The second area of agile improvement is that unlike CAPI, CNG does not require Microsoft to
sign the implementation. A cryptographer can create a CNG cryptographic provider. When
implementing a CNG provider, you need only implement the necessary functionality. For
example, if you implement a Serpent provider, then the provider does not need to implement
signing or hashing functions because Serpent is only a symmetric encryption algorithm.
Finally, it is possible for an application to query CNG for supported algorithms, based on certain criteria, if needed. The following code shows how to dump all primitive cryptographic
algorithms:
PROVIDER_REFS pProviders = NULL;
DWORD dwBufSize = 0;
const DWORD dwFlags = CRYPT_ALL_FUNCTIONS | CRYPT_ALL_PROVIDERS;
for (DWORD dwInterface = BCRYPT_CIPHER_INTERFACE;
dwInterface <= BCRYPT_RNG_INTERFACE;
dwInterface ++) {
NTSTATUS ret = BCryptResolveProviders(
NULL,
dwInterface,
NULL,
NULL,
CRYPT_UM,
dwFlags,
&dwBufSize,
&pProviders);
if (NT_SUCCESS(ret) && pProviders) {
printf("dwInterface = %d\n", dwInterface);
for (DWORD k=0; k < pProviders->cProviders; k++) {
PCRYPT_PROVIDER_REF pProv = pProviders->rgpProviders[k];
printf("\tFunction = %S\n", pProv->pszFunction);
printf("\tProvider = %S\n", pProv->pszProvider);
// dump property names
for ( DWORD j = 0; j < pProv->cProperties; j++)
printf("\tProperty %d = %S\n",
j,
pProv->rgpProperties[j]->pszProperty);
printf("\n");
}
BCryptFreeBuffer(pProviders);
pProviders = NULL;
}
}
Important
Chapter 7
139
Cryptographic Enhancements
Algorithm
#define
Standard
RC2
BCRYPT_RC2_ALGORITHM
RFC2288
RC4
BCRYPT_RC4_ALGORITHM
AES
BCRYPT_AES_ALGORITHM
FIPS 197
DES
BCRYPT_DES_ALGORITHM
DESX
BCRYPT_DESX_ALGORITHM
3DES
BCRYPT_3DES_ALGORITHM
3DES-112
BCRYPT_3DES_112_ALGORITHM
MD2
BCRYPT_MD2_ALGORITHM
RFC 1319
MD4
BCRYPT_MD4_ALGORITHM
RFC 1320
MD5
BCRYPT_MD5_ALGORITHM
FC 132
SHA-1
BCRYPT_SHA1_ALGORITHM
SHA-256
BCRYPT_SHA256_ALGORITHM
SHA-384
SHA-512
Allowed Suite
by SDL? B?
Yes*
Yes
Yes
Yes
Yes
BCRYPT_SHA384_ALGORITHM
Yes
Yes
BCRYPT_SHA512_ALGORITHM
Yes
Yes
RSA (encryption)
BCRYPT_RSA_ALGORITHM
Yes
RSA (signing)
BCRYPT_RSA_SIGN_ALGORITHM
Yes
Diffie-Hellman
BCRYPT_DH_ALGORITHM
PKCS#3
Digital
Signature
Algorithm
BCRYPT_DSA_ALGORITHM
FIPS 186-2
SHA-256, SHA-384, and SHA-512 are collectively referred to as SHA-2 and are available on Windows Vista (in CAPI and CNG) and Windows Server 2003 (in CAPI), and all supported Windows platforms via the .NET Framework.
140
Table 7-2
Algorithm
#define
Standard
BCRYPT_ECDSA_P256_ALGORITHM
BCRYPT_ECDSA_P384_ALGORITHM
BCRYPT_ECDSA_P521_ALGORITHM
BCRYPT_ECDH_P256_ALGORITHM
SP800-56A
BCRYPT_ECDH_P384_ALGORITHM
SP800-56A
BCRYPT_ECDH_P521_ALGORITHM
SP800-56A
Note
All of the above are approved for use in the SDL, are Suite B compliant, and are new
to CNG.
CNG also supports two kinds of random number generators (RNG), and both are allowed
under SDL: BCRYPT_RNG_ALGORITHM and BCRYPT_RNG_FIPS186_DSA_ALGORITHM.
Most applications should use the former, but if you are using DSA, then you should use the
latter. Both RNGs conform to FIPS 186-2 and FIPS 140-2.
Using CNG
What follows are a series of small code function outlines that show how to use CNG to perform
various cryptographic tasks. Consider the examples as pseudo-code using real API names. The
intent is not to demonstrate every possible algorithm or cryptographic operation, nor do we
want to show huge swaths of code; rather, we want to show the general API call order.
Note
CAPI1 APIs dont have access to CNG providers and keys, but CNG has access to
CAPI1 keys used by Microsoft Cryptographic Service Providers.
The Windows Vista Software Development includes complete CNG samples in the samples/
security/CNG folder. There is also a separate CNG SDK available that includes samples and
documentation relating to CNG configuration and installing CNG plug-ins (Microsoft 2006a).
In all cases you must include <bcrypt.h> and link your code with bcryt.dll. Also, CNG returns
various status values defined in ntstatus.h. You may also need to add this macro to your code:
#ifndef NT_SUCCESS
#
define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
Chapter 7
Cryptographic Enhancements
141
Encrypting Data
BCryptOpenAlgorithmProvider(&hAlg,...)
BCryptGetProperty(hAlg,BCRYPT_BLOCK_LENGTH,&dwBlockSize,...)
Allocate buffer, rounding up to next block size.
BCryptGetProperty(hAlg,BCRYPT_OBJECT_LENGTH,&cbKeyObjectLen,...)
Allocate buffer for key object.
BCryptGenerateSymmetricKey(hAlg,&hKey,...)
BCryptEncrypt(hKey,...)
Data is now encrypted
BCryptDestroyKey(hKey)
BCryptCloseAlgorithmProvider(hAlg,0)
Deallocate buffers
Hashing Data
BCryptOpenAlgorithmProvider(&hAlg,...)
BCryptGetProperty(hAlg,BCRYPT_OBJECT_LENGTH,&cbHash,...)
Allocate buffer for hash
BCryptCreateHash(hAlg,&hHash,...)
BCryptHashData(hHash,...)
BCryptFinishHash(hHash,...)
Use the hash data
BCryptDestroyHash(hHash)
BCryptCloseAlgorithmProvider(hAlg,0)
Deallocate buffers
MACing Data
Creating a message authentication code is exactly the same as creating a hash, but there are
two differences.
1. The last argument to BCryptOpenAlgorithmProvider should be BCRYPT_ALG_HANDLE_
HMAC_FLAG.
2. The fifth and sixth arguments to BCryptCreateHash are the secret MAC key and the length
of the MAC key. So the function call looks like this:
BCRYPT_ALG_HANDLE hAlg = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
status = BCryptOpenAlgorithmProvider(&hAlg,
GetPreferredHmacAlg(),
NULL,
BCRYPT_ALG_HANDLE_HMAC_FLAG)))
The call to GetPreferredHmacAlg is not a CNG function; its a function you would provide to get
the preferred HMAC base algorithm, perhaps from a configuration setting.
142
Chapter 7
Cryptographic Enhancements
143
Now heres the caveat. This setting only affects the protocol suites used by SSL/TLS and .NET
code. The following C# will fail with a System.InvalidOperationException exception because it is
using a non-FIPS compliant algorithm, MD5.
MD5CryptoServiceProvider hash = new MD5CryptoServiceProvider();
byte[] result = hash.ComputeHash(ASCIIEncoding.UTF8.GetBytes(message));
From a CNG application, it is possible to determine if the FIPS requirement is enabled or not
by using the BCryptGetFipsAlgorithmMode function.
Improved Auditing
To help comply with certain Common Criteria requirements, various key operations are
audited. Enter the following command from an elevated command-prompt to configure key
auditing in Windows Vista.
auditpol /set /subcategory:"other system events" /success:enable /failure:enable
Note
The U.S. Government Protection Profile for Single-level Operating Systems in Environments Requiring Medium Robustness v1.67, 5.1, defines security audit requirements,
including those related to cryptographic key use (NSA 2003).
Various keys operations, such as creation, deletion, and key access will yield events such as
those shown in Figure 7-1.
Figure 7-1
You can view these entries in the Security log of the Windows Event Viewer.
144
CNG does include a very flexible key derivation function, BCryptDeriveKey, that can be used
to derive keys the same way SSL3, TLS1, and CMS do, but its not a password-to-key function.
It is hoped this will be addressed in a future version of CNG.
SSL/TLS Improvements
There are two major improvements to SSL/TLS in Windows Vista. The first is newer cipher
suites, including support for the Suite B algorithms: elliptic-curve encryption, SHA-2, and
AES. Windows Vista supports the cipher suites shown in Table 7-3.
SSL/TLS in Windows Vista also supports the following cipher suites, but they can only be used
from code by calling the SSL/TLS Security Support Provider (such as TLS1SP_NAME) directly.
TLS_RSA_WITH_NULL_MD5
TLS_RSA_WITH_NULL_SHA
As mentioned earlier in this chapter, you can also add a custom cipher suite by a writing an
SSL/TLS provider plug-in.
Second, Windows Vista makes it easier to configure the cipher suites you are willing to support for SSL/TLS; its simply a policy setting. You can set the policy by performing the
following steps:
1. Open mmc.exe.
2. Click File and then Add/Remove Snap-in.
3. Scroll down to and select the Group Policy Object Editor.
Chapter 7
Cryptographic Enhancements
145
4. Click Add.
5. Select remote computer by clicking Browse, or Finish, to access the local computer.
6. Click OK and then OK again.
7. Open the following nodes: Local Computer Policy, Computer Configuration, Administrative Templates, and then Network.
8. Click the SSL Configuration Settings node.
9. Double-click the SSL Cipher Suite Order setting.
10. Click Explain to learn how to modify this setting.
Table 7-3
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_RC4_128_SHA
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
TLS_RSA_WITH_3DES_EDE_CBC_SHA
TLS_RSA_WITH_RC4_128_MD5
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA_P256 SSL_CK_RC4_128_WITH_MD5
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA_P384 SSL_CK_DES_192_EDE3_CBC_WITH_MD5
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA_P521 TLS_RSA_WITH_NULL_MD5
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA_P256 TLS_RSA_WITH_NULL_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA_P384 TLS_RSA_WITH_DES_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA_P521 TLS_DHE_DSS_WITH_DES_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA_P256
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA_P384
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA_P521
TLS_DHE_DSS_EXPORT1024_WITH_
DES_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P256
TLS_RSA_EXPORT_WITH_RC4_40_MD5
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P384
SSL_CK_DES_64_CBC_WITH_MD5
T LS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P521
SSL_CK_RC4_128_EXPORT40_WITH_MD
146
can get large, but an OCSP lookup is small. The major disadvantage is that for OCSP to work,
the application must be online, but CRLs can be cached, which means a CRL lookup may not
necessarily be remote.
Note
The really good news is OCSP support is transparent in Windows Vista if the certificate being
checked includes an appropriate Authority Information Access (AIA) URL. Like a CDP, an AIA
can include a URL or URLs to access to determine if the certificate has been revoked or not.
Figure 7-2 shows a valid AIA in a Web server certificate.
Figure 7-2
Chapter 7
Cryptographic Enhancements
147
Figure 7-3
An application may fail to access a CDP or the OCSP URL, in which case functions like CertGetCertificateChain will fail and return a CRYPT_E_REVOCATION_OFFLINE error. If your
application is in a bind, what should it do if it cannot verify that a certificate is revoked or not?
We cant give a 100 percent applicable answer because it depends on the nature of your application. If it is a sensitive application, or an application running in a sensitive environment,
then you should fail operations related to the certificate use.
The certutil -url command-line tool can be used to interactively verify a certificate using a CRL
or OCSP.
Note
You can look at important certificate-related events in the Event Viewer. Open the
Event Viewer, and expand Applications and Services Logs, then expand Microsoft, then
expand Windows and finally expand CAPI2.
148
The XEnroll and SCdEnrl COM objects are no longer included with Windows Vista; you
should use the CertEnroll API instead (Microsoft 2006b).
WinVerifyTrust should no longer be used for certificate verification; rather you should use
the CertGetCertificateChain function call instead.
Call to Action
Determine how much of your code uses hard-coded algorithm names, start to migrate
the code to read from a configuration store, and then write the cryptographic settings
information to the document meta-data.
Chapter 7
Cryptographic Enhancements
149
Evaluate which weak algorithms should be removed from your code and replaced with
strong algorithms, or better yet stronger algorithms, making your code crypto-agile at
the same time.
Some cryptographic functionality is now deprecated in Windows Vista, and you should
start to migrate to user the newer, more robust functionality instead.
References
National Security Agency. Fact Sheet NSA Suite B Cryptography, http://www.
nsa.gov/ia/industry/crypto_suite_b.cfm.
(NSA 2005)
(NIST 2006)
(Anderson 1999)
details.aspx?FamilyID=1ef399e9-b018-49db-a98b-0ced7cb8ff6f&DisplayLang=en.
National Security Agency. U.S. Government Protection Profile Single Level
Operating Systems for Medium Robustness Environments, http://www.niapccevs.org/
pp/draft_pps/pp_draft_slos_mr_v1.67.cfm October 2003.
(NSA 2003)
(RFC 2898)
(RFC 2560) Entrust Technologies. Network Working Group, Request for Comments 2560.
aa374863.aspx.
Chapter 8
All these protocols are covered by Microsofts Open Specification Policy (Microsoft 2006a),
which means that Microsoft will not enforce any patent or intellectual property rights for the
use of these industry standards, even though Microsoft was instrumental in designing some
of the standards.
151
152
The benefits of Information Cards for service providers (online merchants, banks, etc.) are
many, including:
Stronger client authenticationEven personal cards are more secure than a simple username and password combination.
You might think user-issued Information Cards are of little use. In fact, they are useful
because its quite common for a user to create his or her own identity when visiting a Web
site for the first time and creating an account at that Web site.
Server authentication
Chapter 8
User
Windows
CardSpace
Relying Party
(Woodgrove
Back)
153
Identity
Provider
Figure 8-1
Server Authentication
One of the biggest enablers of phishing attacks is poor or nonexistent server authentication;
requiring SSL/TLS server authentication goes a long way to remedying the server authentication problem. All communication between the Windows CardSpace client and the server is
performed using SSL/TLS, but more importantly, Windows CardSpace requires the SSL/TLS
server authenticate correctly to the Windows CardSpace client. In other words, all the appropriate checks are made by Windows CardSpace to make sure the server is who it claims to be.
The checks performed by default are as follows:
Server name the client requests matches the common name in the server certificate.
Server certificate is trusted (that is, you trust the certificate issuer).
154
The certificate has not been revoked (may be a local cached lookup).
No Password to Steal!
When a user provides his Information Cardbased identity to a server, the users password is
never provided to the server. Information Cards do not include a password or any other type
of secret user credential data. This is absolutely critical, because one of the reasons attackers
mount phishing attacks is to harvest unwitting users passwords. Rather, a trusted entity, the
Identity Provider, attests to the users identity.
Also, if the user is using a self-issued Information Card, the key presented to one site is different from the key presented to another site, because the key is derived, in part, from the
servers server authentication certificate.
Chapter 8
155
Lets assume the worst, and the user clicks the bank link in the email. The Web browser
opens, and the user is confronted with a Web site that looks just like the real Woodgrove Bank
Web site. The site probably includes trademark, copyright, and privacy notices, too! In short,
it looks real. But its actually a compromised Web serversomewhere on the Internetthat is
made to look like Woodgrove Bank, and its sole purpose is to harvest account numbers and
passwords of gullible Woodgrove Bank customers.
Again, Information Cards can do nothing here, but perhaps the users browser can. Both Internet Explorer 7 and Mozilla FireFox 2 have support for detecting and notifying users of many
known phishing sites, as does the Netcraft Toolbar for older browsers (Netcraft 2004).
The connection between Windows CardSpace and the Web server must be over SSL/TLS, and
all appropriate SSL/TLS checks are made; otherwise, CardSpace will reject the connection. In
other words, the name of the Web site in the browsers address bar must equal the name in
the certificate, and the certificate must be trusted. This will foil most phishing attacks. This
has a side effect of breaking some Web sites if they choose to use an SSL/TLS certificate that
does not reflect the Web server DNS name. If you want to use Information Cards with your
Web site, you should use a valid X.509 certificate, which includes the sites DNS name in the
certificate common name (CN).
Lets again assume the worst, and the user clicks the option to sign in to the bank, regardless
of any SSL/TLS warnings issued by the browser. If the user enters her credentials, the rogue
Web site now has the users logon information. But things are difficult for the phisher. First,
for the user to enter her credentials, the logon page must look just like the Windows CardSpace login because Woodgrove Bank uses CardSpace. It is, of course, possible to spoof a user
interface, but how will the attacker know what cards the user has and display the users cards
correctly? Also, how will the attacker know the visual secrets chosen by the user? By visual
secrets we mean the image and text associated with each Information Card (see Figure 8-2).
Again assuming the worst, the attack site does not spoof the users CardSpace environment but
instead invokes the users CardSpace environment; the user clicks on an Information Card
and enters a PIN to unlock the card. The attacker does not get the users credential, even if the
credential is a password. Information Cards rely on a trusted third party to assert the users
identity. The importance of this last point cannot be overstated. The real bank and the
attacker never get the users password. Ever.
The phishing site will simply get, at best, a token that asserts the user is who the user claims
to be. The password is provided to the trusted identity provider, not the phishing site, or the
valid site for that matter.
Even if the attacker gets the key from a self-issued card, he cannot replay the key against the
real Woodgrove Bank because when a self-issued card is used, the key is derived (in part) from
the certificate used by the Web site. Given that the site is different (phisher versus Woodgrove), the generated key is not the same.
156
Figure 8-2
Tip
If you want to see a good technical overview of CardSpace in action, watch the video,
Vittorio Bertocci: WS-TrustUnder the Hood, on Channel9 (Channel9 2006). Vittorio also
has an excellent CardSpace blog (Bertocci 2006) that includes sample code.
Chapter 8
157
chant wants a user to identify itself, the merchant can request certain assertions, and the Windows CardSpace Identity Selector will allow the user to select an Information Card that
supports the required claims.
We had a couple of problems installing the code on Windows Vista. If you get an
exception like this, Relying Party Certificate thumbprint not specified, then you should
explicitly register capicom by typing regsvr32 capicom.dll in the sample code bin directory.
A Web site can trigger the Windows CardSpace identity selector with HTML code like this:
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="logonform" method="post" action="login.aspx">
<button type="submit">Sign in with your Information Card</button>
<object type="application/x-informationcard" name="xmlToken">
<param name="tokenType" value="urn:oasis:names:tc:SAML:1.0:assertion" />
<param name="issuer"
value="http://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self" />
<param name="requiredClaims"
value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
</object>
</form>
</body>
</html>
Note the object tags type is application/x-informationcard, and the parameters define
what claims the Web site requires from the user, as well as the valid issuer(s). In this case, the
Web site will accept self-issued Information Cards that assert the users given name and
surname.
158
The login page can then get the data from the users Information Card using code like this:
string xmlToken = Request.Params["xmlToken"];
if (xmlToken == null || xmlToken.Equals("")) {
// Handle Error there is no token
} else {
Token token= new Token(xmlToken);
string givenname = token.Claims[ClaimTypes.GivenName];
string surname = token.Claims[ClaimTypes.Surname];
string email = token.Claims[ClaimTypes.Email];
}
Chapter 8
159
CardSpace Summary
Nearly two-thirds of this chapter is dedicated to Windows CardSpace and its implementation
of the Information Card identity technology, so we want to wrap this section up with a short
summary. We feel Information Cards are a very real phishing defense, but we need more
people to adopt the technology in both clients and servers. Today, there is Information Card
support for many non-Microsoft platforms and technologies, and the list of supported is
growing quickly. We highly recommend that you acquaint yourself with Kim Camerons blog
(Cameron 2006) and the Windows CardSpace resource Web site (Microsoft 2006a) and
consider adding Information Card support alongside other identity technologies your system
presently supports.
160
If you want to learn more about this area, you should review Windows Vista Credential
Providers (Microsoft 2006c) and become familiar with the ICredentialProvider interface
through the available sample code (Microsoft 2006d).
If youre sure that your code will only run on Windows XP and later, a more robust and nearly
as efficient way to accomplish the same thing is to use CreateWellKnownSid with an argument
of WinCreatorOwnerRightsSid.
When the DACL for the securable object is evaluated during an access operation in Windows
Vista, a check is made for ACEs containing the Owner SID to determine the rights granted to
the objects owner. If the DACL contains no Owner SIDs, the objects owner is granted the
READ_CONTROL and WRITE_DAC rights, as in previous versions of Windows. However, if
the DACL contains one or more Owner ACEs, the access check will use those entries to
determine the owner's rights.
For some customers, this is a very useful feature. For example, an administrator can ensure
that users who create files and folders will not change the intended access control policy on
those resources or that users are unable to change settings on an object they created after they
have been removed from a particular group.
Another example is related to the ability for services to be assigned a service SID in Windows
Vista, as we covered in Chapter 5. Some resource isolation can be achieved by ACLing objects
with a service's SID, but not resources created by the service at run time: they will be owned
by LocalSystem, NetworkService, or LocalService, and hence will be ultimately modifiable by
other instances of such services. By adding an Owner ACE to the ACLs of those resources, we
can prevent other services from accessing resources belonging to our service, because the
other service cant change the access controls.
As a final example, an Active Directory administrator can allow users in a specific group to
create objects under an Organizational Unit (OU) but can then restrict their ability to modify
Chapter 8
161
or create group objects when removed from that group. This change allows the emulation of
a standard UNIX access control trick: a user can create a file and then not be able to access it
once it has been closed. This happens because the handle returned from CreateFile allows all
access (except WRITE_DAC when the inherited ACL from the containing folder contains
OWNER ACEs that deny WRITE_DAC), so the program can do whatever it needs to create the
file. The inherited ACL can be set to have an OWNER ACE that does not contain WRITE_DAC
and whatever other rights that it decided not to grant once the file is closed. However, if the
user could write code that directly sets an ACL at the time the file is created, it need not inherit
anything from the parent, so this measure isnt completely foolproof. This issue could also
apply to Active Directory objects.
Such a change does not affect TakeOwnership semanticsa user with SeTakeOwnership privilege will still be able to take ownership and acquire READ_CONTROL and WRITE_DAC rights.
When an administrator takes ownership of a file (which changes the owner SID), then the
DACL is examined for ACEs that contain the Owner SID. If an allow-Owner ACE is found,
then READ_CONTROL | WRITE_DAC are added to the access mask. If a deny-Owner ACE is
found, then READ_CONTROL | WRITE_DAC are removed from the mask.
Finally, this change does not affect quota either. Quota is charged to the owner of an object,
even if that owner does not have WRITE_DAC rights.
Call to Action
You should seriously consider using CardSpace in your Web-based application whether
its a Web site or a smart client using Web services. CardSpace helps mitigate one of the
most serious Internet crimes todayphishing. At the very least, you should use CardSpace as a user and then download the sample code and start experimenting.
If you built a GINA in the past, it will no longer work on Windows Vista, so it is important to understand the new Credential Provider model and replace your current code
with the new COM interfaces.
References
(XMLSOAP 2002) WS-Security. http://schemas.xmlsoap.org/ws/2002/04/secext/. April 2002.
(XMLSOAP 2005a) Web Services Trust Language (WS-Trust). http://schemas.xmlsoap.org/
162
(XMLSOAP 2005b)
(Microsoft 2006b)
(Bertocci 2006)
(OASIS 2006)
(Microsoft 2006c)
(Microsoft 2006d)
Chapter 9
Pointer encoding
164
Tip
If you need to know on which version of Windows Vista your application is running,
you can call the new Windows Vista API GetProductInfo.
Also note that Parental Controls feature is not enabled by default for domain-joined computers, but you can enable it by following these steps:
1. Open MMC.exe.
2. Click File and then Add or Remove Snap-ins.
3. Double-click Group Policy Object Editor.
4. Click Finish and then click OK.
5. Expand the following nodes: Local Computer Policy, Computer Configuration,
Administrative Templates, Windows Components.
6. Click Parental Controls.
7. Double-click the Make Parental Controls control panel visible in a domain setting.
8. Click Enabled.
9. Click OK.
Another important point is that Parental Controls are only effective when the users under
Parental Control policy are not administrators.
Important
The Parent Controls feature is not 100 percent foolproof, and parents or
guardians should not abdicate the protection of their children to the Parental Controls
technology. Parents should make sure kids are aware of online dangers, and monitor the
Parental Controls activity reports regularly.
Chapter 9
165
The Code
To add Windows Parental Controls (WPC) functionality to your application, you must
include <wpc.h>, and because WPC is implemented as a COM object, there is no need to link
with a library.
You will find the following code at the start of all applications that support WPC:
CComPtr<IWindowsParentalControls> piWPC = NULL;
HRESULT hr = piWPC.CoCreateInstance(__uuidof(WindowsParentalControls));
if (SUCCEEDED(hr)) {
// cool!
}
Tip
As a general rule, we like to use the Abstract Type Library (ATL) when writing COM
code. ATL makes COM palatable.
Next, get the user settings; note the first argument to GetUserSettings can either be NULL to get
the settings for the current user, or a textual SID (of the S-1-5-21-xxx ilk) for a specific user.
Two functions, WpcuSidStringForCurrentUser and WpcuSidStringFromUserName in the
Windows Software Development Kit (Microsoft 2006b) sample code contained in a file
named Security\ParentalControls\Utilities\Utilities.cpp show how to create textual SIDs.
IWPCSettings* piWPCUserSettings = NULL;
hr = piWPC->GetUserSettings(pwszSid,&piWPCUserSettings);
if (E_ACCESSDENIED == hr) {
// user is probably an admin
} else if (SUCCEEDED(hr)){
// Cool!
}
Accessing user settings will fail with an E_ACCESSDENIED error when calling the IWPCSettings:: GetUserSettings method if the user is an administrator because parental controls do
not apply to administrators.
Time Limits
Windows Parental Controls includes time limits that allow a user access to the computer only
at predetermined times. If a users time is up, they are logged off and cannot log back on until
the time limit policy determined by the parent or guardian allows them to log back on. Use
the following code to determine if the user has time restrictions:
// Determine if time limits are in effect for this user
DWORD dwRestrictions = 0;
hr = piWPCUserSettings->GetRestrictions(&dwRestrictions);
if (SUCCEEDED(hr)) {
if (dwRestrictions & WPCFLAG_HOURS_RESTRICTED)
wprintf(L"Hour Restriction");
}
166
Fifteen minutes before a forced logoff, appropriately registered applications will receive a
WMI event. There is sample code in the Samples\Security\ParentalControls\ComplianceApp
folder of the Windows SDK that demonstrates how to register and handle these WMI events.
When the application receives this WMI event, your application should probably inform the
user that they will soon be logged off and had better save their work soon.
Chapter 9
167
Note that this code must run with full administrator privileges to add an exemption. Also note
the WMI object used is unfortunately named HTTPExemptionList; take our word for it that this
is the object that holds a list of exempted applications! If you want to add a URL to the list,
then use the URLExemptionList object. If you look at the WPC WMI schema, youll notice two
other similarly named classes: WinHTTPExemptionList and WinURLExemptionList. These are
read-only lists of exemptions that apply to all users.
Logging Events
Microsoft cannot make computers safer to use online alone; we need the help of Independent
Software Vendors (ISVs) too. One way you can help is by logging interesting online events in
the WPC activity reports. For example, if you build some form of instant messaging system,
you should log the time and date of the communication, as well as the IP address or DNS
name of the remote computer. David Bennett of the Parental Controls team at Microsoft has a
good logging code sample on his blog (Bennett 2006).
168
Windows Vista exposes two functions to enable and disable Windows Defender and another
to determine the current status of Windows Defender. Only administrators can enable or disable Windows Defender.
The following code detects if Windows Defender is disabled and if it is, turns it on.
typedef HRESULT (WINAPI *WDSTATUS)(BOOL*);
typedef HRESULT (WINAPI *WDENABLE)(BOOL);
// Get the path to Program Files, it might be localized
wchar_t wszPath[MAX_PATH];
HRESULT hr = SHGetFolderPathAndSubDir(
NULL,
CSIDL_PROGRAM_FILES,
NULL,
SHGFP_TYPE_CURRENT,
L"Windows Defender",
wszPath);
if (FAILED(hr))
return hr;
wcscat_s(wszPath, MAX_PATH, L"\\MpClient.dll");
HMODULE h = LoadLibrary(wszPath);
if (!h) {
DWORD dwErr = GetLastError();
return dwErr;
}
WDSTATUS pfnWDStatus = (WDSTATUS)GetProcAddress(h,"WDStatus");
WDENABLE pfnWDEnable = (WDENABLE)GetProcAddress(h,"WDEnable");
if (pfnWDStatus && pfnWDEnable) {
BOOL fEnabled = FALSE;
HRESULT hr = pfnWDStatus(&fEnabled);
if (SUCCEEDED(hr)) {
if (fEnabled) {
wprintf(L"Windows Defender is already enabled.");
} else {
hr = pfnWDEnable(TRUE);
if (SUCCEEDED(hr)) {
wprintf(L"Windows Defender is now enabled.");
} else {
wprintf(L"Could not enable Windows Defender, err = %X.",hr);
}
}
}
}
FreeLibrary(h);
Chapter 9
169
170
In short, credentials are always encrypted until needed. The following code is an example of
how to use the API:
CREDUI_INFO ci = {0};
ULONG
ulAuthPkg = 0;
void
*pAuthBuff = NULL;
ULONG
cbAuthBuff = 0;
void
*pOutAuthBuff = NULL;
ULONG
cbOutAuthBuff = 0;
BOOL
fSave = FALSE;
DWORD
dwFlags = CREDUIWIN_CHECKBOX;
ci.cbSize = sizeof ci;
ci.hbmBanner = NULL;
ci.hwndParent = NULL;
ci.pszCaptionText = L"Writing Secure Code for Windows Vista";
ci.pszMessageText = L"Please enter your password";
DWORD dwErr = CredUIPromptForWindowsCredentials(
&ci,
0,
&ulAuthPkg,
pAuthBuff,
cbAuthBuff,
&pOutAuthBuff,
&cbOutAuthBuff,
&fSave,
dwFlags);
if (dwErr != 0) {
wprintf(L"CredUI failed, err = %d\n",dwErr);
return dwErr;
}
WCHAR
DWORD
WCHAR
DWORD
WCHAR
DWORD
wszUsername[CREDUI_MAX_USERNAME_LENGTH + 1];
cchUsername = _countof(wszUsername);
wszPassword[CREDUI_MAX_PASSWORD_LENGTH + 1];
cchPassword = _countof(wszPassword);
wszDomain[CRED_MAX_DOMAIN_TARGET_NAME_LENGTH + 1];
cchDomain = 0;
Chapter 9
171
The most important reason to use this function over the older credential user interface API is
the ability to display the credential dialog on the Windows logon desktop, therefore reducing
the chance an attacker can spoof the dialog and steal a users credentials. You can enable this
setting in Group Policy by navigating to Computer Configuration | Administrative Templates |
Windows Components | Credential User Interface, and then enable Require trusted path for credential entry. From now on, when an application calls CredUIPromptForWindowsCredentials
she will see the series of dialogs shown in Figures 9-1 and 9-2.
Figure 9-1
First stage of CredUI prompting for user credentials on the Windows logon desktop.
Figure 9-2
Second stage of CredUI prompting for user credentials on the Windows logon desktop.
Once the user presses Ctrl-Alt-Del, the credential dialog appears on the Windows logon desktop.
Change in a policy or setting that might affect the outcome of security decisions (including old and new value of a security setting)
172
Change in state that might affect the outcome of security decisions (load module, start
module, stop module, etc.)
Application-specific suspicious behavior or integrity violation (this is application specific, but for example, if a Web application detects an SQL injection attempt, it might
choose to log that, or an anti-virus program might log that it detected a virus, etc.)
Microsoft added a new API, AuthzReportSecurityEvent, to Windows Server 2003 and Windows
Vista to log such data easily. The Windows security log is Common Criteria compliant at EAL4+;
and although this doesnt guarantee Common Criteria compliance for your application, it may
reduce the amount of work required to achieve Common Criteria for your application.
Windows authentication, authorization, and account management functions all generate
appropriate audit trails in the Windows security event log. If you use these functions of Windows rather than implement your own, then auditing will be done for you. One final point,
dont flood logs with irrelevant information, just log critical data that could help administrators.
Pointer Encoding
Pointer encoding is neither an SDL requirement nor a Windows Vista security quality gate
requirement, but we worked very closely with Windows Vista developers to make sure they
were educated about the value of pointer encoding. Many long-lived pointers in Windows
Vista are encoded to help protect them from becoming buffer-overrun victims.
Technically, pointer encoding isnt a code quality benefit; its a defensive measure only. Its very
common to use pointers in C and C++. In fact, one of the major benefits of C and C++ is that the
languages expose pointers to arbitrary memory locations and code can read from and write to
arbitrary computer memory. Accessing memory directly is a double-edged sword: its powerful,
but failure can be catastrophic because this can be a source of buffer-overrun vulnerabilities.
Introduced in Windows Server 2003 SP1 and Windows XP SP2, pointer encoding allows
developers to make it harder for an attacker to successfully overwrite a pointer with a valid
value. Take a look at the following contrived code:
class Stuff {
public:
Stuff( ) {
m_dest = new char[32];
m_data = new char[1];
}
~Stuff() {
delete [] m_dest;
delete [] m_data;
Chapter 9
173
}
const char *WriteData(const char *src) {
if (!src) return NULL;
if (m_dest) strcpy(m_dest,src);
if (m_data) *m_data = src[0];
return src;
}
private:
char *m_dest, *m_data;
};
Assuming the attacker controls src when calling the WriteData method, she can overflow
m_dest, and in doing so overwrite the m_data pointer (because its a few bytes higher in memory) with whatever value she wants. Because the attacker controls src, and therefore m_dest,
and because the vulnerable code calls strcpy and overflows m_dest, she controls m_data. The
code then writes the first byte of src to m_data, and now the attacker can very effectively write
one byte anywhere in memory. This is a classic heap-based buffer overrun (see Figure 9-3). Of
course, the code is bad because it uses strcpy, which is banned as we pointed out in Chapter 1,
Code Quality.
m_dest
m_data
src overflows the first buffer,
corrupting the second
src
Heap-based buffers
Figure 9-3
Now take a look at the same contrived C++ class code using pointer encoding.
class Stuff {
public:
Stuff() {
m_dest = (char*)EncodePointer(new char[32]);
m_data = (char*)EncodePointer(new char[1]);
}
~Stuff() {
delete [] DecodePointer(m_dest);
delete [] DecodePointer(m_data);
m_data = m_dest = NULL;
}
const char *WriteData(const char *src) {
if (!src) return NULL;
char *dest = (char*)DecodePointer(m_dest);
if (dest) strcpy(dest,src);
char *data = (char*)DecodePointer(m_data);
if (data) *data = src[0];
return src;
}
174
This code is very similar to the original code, except that the pointers are deemed long-lived
and are encoded as soon as they are created and then decoded prior to use. You will also
notice that the class destructor sets the pointers to NULL after the memory is deleted; Im in
the habit of setting freed pointers to NULL; it can help mitigate exploitable double-free bugs
as described in Chapter 3, Buffer Overrun Defenses.
Follow these steps when dealing with long-lived pointers:
1. Allocate memory or initialize the pointer, and assign the pointer to the address.
2. Encode the pointer.
3. In the line above the code where the pointer is used, decode the pointer into a temporary
variable.
4. When the pointer is no longer needed, decode the pointer and free it.
5. Set the pointer to NULL.
Note that DecodePointer wont fail; it will just give you back a bad pointer. If youd like to make
this really safe, wrap this in a class and keep a reference copy of the pointer. Heres an
example:
class EncodedPointer {
public:
EncodedPointer() : m_pv( NULL ), m_pReference( NULL ){}
void Set( void* pIn ) {
m_pv = EncodePointer( pIn );
m_pReference = MakeRefPtr( pIn );
}
void* Get() {
if( m_pv == NULL )
return NULL;
void* pRet = DecodePointer( m_pv );
// Now double-check for tampering
if( MakeRefPtr( pRet ) != m_pReference )
return NULL;
return pRet;
}
#ifdef _DEBUG
// testing only
void Corrupt() {
m_pv = (void*)((char*)m_pv + 1);
}
#endif
private:
void* MakeRefPtr( void* pIn ){
Chapter 9
175
Before you use this class a great deal, remember that you ought to do some work to implement
the copy constructor and assignment operator. If you assigned one of these classes to another
instance of the class, it would be invalid because the C++ this pointer changes. What the class
does is to make it very unlikely that a corrupted pointer would get returned as anything other
than NULL, and dereferencing a null pointer wont typically be exploitable. This also gives
you a way to check for success or failure, or even throw an exception if it does fail.
or
bcdedit -deletevalue debug
The change does not take effect until the next system reboot.
176
cryptographic operations into hardware. Windows Vista supports TPM 1.2. The most wellknown feature that uses the TPM, if one is available, is BitLocker Drive Encryption and its
secure start-up capability (Microsoft 2006e).
Tip
BitLocker can be used without a TPM, but it only offers drive encryption, not secure
start-up.
You can only successfully call TPM functionality from elevated processes.
Chapter 9
177
What is exposed today in Windows Vista are WMI interfaces (Microsoft 2006f) to build
TPM and BitLocker management tools. The following C# code shows how to query a TPM
using WMI:
using System.Management;
static private ManagementObject m_wmiObject = null;
static Boolean BooleanMethod(String method)
{
ManagementBaseObject outParams = m_wmiObject.InvokeMethod(method, null, null);
return (0 == (UInt32)outParams["ReturnValue"]) ? false : true;
}
static void Main(string[] args)
{
try
{
string tpm = "root\\cimv2\\security\\microsofttpm:Win32_Tpm=@";
m_wmiObject = new ManagementObject(tpm);
String version = m_wmiObject.GetPropertyValue("SpecVersion").ToString();
Boolean fEnabled = BooleanMethod("IsEnabled");
UInt32 id = (UInt32)m_wmiObject.GetPropertyValue("ManufacturerId");
}
catch (ManagementException me)
{
Console.WriteLine(me.Message);
}
}
These WMI providers sit atop the Windows Vista TPM Base Services (TBS), which runs as a
system service and exposes a small number of APIs (Microsoft 2006g) to software developers
wanting to build TPM-aware applications.
178
time-consuming, as you will see in the next code sample. A TSS package helps by reducing the
complexity of TPM commands and provides a much simpler API for a TPM application developer to use.
The following C++ example shows how to write to the TBS directly. This sample code gets the
TPM version information and 32 random bytes.
// returned data starts at byte 10
#define TPM_DATA_OFFSET 10
#ifdef _DEBUG
// Display TPM data hex dump
void TpmDisplayRawResult(
__in_bcount(cbResult) BYTE *pbResult,
UINT32 cbResult) {
if (!cbResult || !pbResult)
return;
if (cbResult >= TPM_DATA_OFFSET) {
wprintf(L"Tag : %02X %02X\n",
pbResult[0], pbResult[1]);
wprintf(L"Len : %02X %02X %02X %02X\n",
pbResult[2], pbResult[3], pbResult[4], pbResult[5]);
wprintf(L"Err : %02X %02X %02X %02X\n",
pbResult[6], pbResult[7], pbResult[8], pbResult[9]);
if (cbResult > TPM_DATA_OFFSET) {
wprintf(L"Data: ");
for (UINT32 i=TPM_DATA_OFFSET; i < cbResult-TPM_DATA_OFFSET; i++)
wprintf(L"%02X ",pbResult[i]);
wprintf(L"\n");
}
}
}
#endif
#define MAX_RNG_BUFF 64
#define TPM_RNG_OFFSET 14
HRESULT TpmGetRandomData(
TBS_HCONTEXT hContext,
__inout_bcount(cData) BYTE *pData,
UINT32 cData) {
if (!hContext || !pData || !cData || cData > MAX_RNG_BUFF)
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
BYTE bCmd[] = {0x00, 0xc1,
// TPM_TAG_RQU_COMMAND
0x00, 0x00, 0x00, 0x0e,
// blob length in bytes
0x00, 0x00, 0x00, 0x46,
// TPM API code (TPM_ORD_GetRandom)
0x00, 0x00, 0x00, (BYTE)cData};// # bytes
UINT32 cbCmd = sizeof bCmd;
BYTE bResult[128] = {0};
UINT32 cbResult = sizeof bResult;
HRESULT hr = Tbsip_Submit_Command(hContext,
TBS_COMMAND_LOCALITY_ZERO,
TBS_COMMAND_PRIORITY_NORMAL,
Chapter 9
bCmd,
cbCmd,
bResult,
&cbResult);
#ifdef _DEBUG
if (FAILED(hr))
wprintf(L"Tbsip_Submit_Command failed %X",hr);
else
TpmDisplayRawResult(bResult,cbResult);
#endif
if (SUCCEEDED(hr))
memcpy(pData,TPM_RNG_OFFSET+bResult,cData);
return hr;
}
HRESULT TpmGetVersion(
TBS_HCONTEXT hContext,
__in_ecount(cVersion) wchar_t *wszVersion,
UINT32 cVersion) {
if (!hContext || !wszVersion || !cVersion)
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
BYTE bCmd[] = {0x00, 0xc1, // TPM_TAG_RQU_COMMAND
0x00, 0x00, 0x00, 0x12, // blob length in bytes
0x00, 0x00, 0x00, 0x65, // TPM API code (TPM_ORD_GetCapability)
0x00, 0x00, 0x00, 0x06, // TCPA_CAP_VERSION
0x00, 0x00, 0x00, 0x00};// no sub capability
UINT32 cbCmd = sizeof bCmd;
BYTE bResult[128] = {0};
UINT32 cbResult = sizeof bResult;
HRESULT hr = Tbsip_Submit_Command(hContext
TBS_COMMAND_LOCALITY_ZERO,
TBS_COMMAND_PRIORITY_NORMAL,
bCmd,
cbCmd,
bResult,
&cbResult);
#ifdef _DEBUG
if (FAILED(hr))
wprintf(L"Tbsip_Submit_Command failed %X",hr);
else
TpmDisplayRawResult(bResult,cbResult);
#endif
if (SUCCEEDED(hr))
swprintf_s(wszVersion,cVersion,L"TCG v%d.%d, Firmware v%d.%d",
(UINT32)bResult[14],
(UINT32)bResult[15],
(UINT32)bResult[16],
(UINT32)bResult[17]);
return hr;
}
int main(int argc, char* argv[]) {
179
180
When calling into the TBS, you must set up an input buffer that contains function arguments,
pass that to the Tbsip_Submit_Command function, and then query the response buffer for status and results.
Note
TBS functions that start with Tbsip result in a direct TPM call, and functions that start
with Tbsi do not directly call the TPM.
Comment
TPM_TAG
UINT32
TPM_CMD_CODE
Variable
Comment
TPM_TAG
UINT32
Chapter 9
181
Comment
TPM_RESULT
Error value.
Variable
Often BYTE[]
or UINT32
The sample code shown earlier issues a TPM command to get TPM chip version information.
To learn more about the different TPM commands and the in and out buffers, you should refer
to the TCG documentation (TCG 2006c).
To summarize this TPM material, most software developers will not need to write any TPMaware software because its very specialized; for those who need to create TPM-aware applications, though, you can use high-level WMI interfaces, a TCG Software Stack (TSS) ported to
work with the TBS, or the low-level TPM Base Service function calls in Windows Vista.
As a defense-in-depth mechanism, encode all untrusted data with code such as the following
sample, which will perform HTML encoding and neuter any script.
function sanitizeHTML(s) {
s = s.replace(/'/g,"'");
s = s.replace(/"/g,""");
var d = document.createElement('div');
d.appendChild(document.createTextNode(s));
return d.innerHTML;
}
You should always enclose untrusted data in quotes when used as an attribute's value. Also
validate untrusted data used as URL values beginning with a supported protocol like http://,
https://, etc. (This will prevent attacks where a URL allows script, such as <A HREF=
javascript:alert(document.cookie)>Link1</A>
182
References
Windows Vista Parental Controls. http://www.microsoft.com/windows/
products/windowsvista/features/details/parentalcontrols.mspx.
(Microsoft 2006a)
(Microsoft 2006b)
Bennett, David. How to Log a Crimson Event to the Custom Log (for
Parental Controls), http://blogs.technet.com/david_bennett/archive/2006/08/28/
452296.aspx. August 2006.
(Bennett 2006)
(Microsoft 2006c)
(Microsoft 2006e)
(Microsoft 2006f)
(Microsoft 2006g)
(TCG 2006b) Trusted Computing Group. TPM Software Stack (TSS) Specifications,
https://www.trustedcomputinggroup.org/specs/TSS.
Trusted Computing Group. Trusted Platform Module (TPM) Specifications,
https://www.trustedcomputinggroup.org/specs/TPM.
(TCG 2006c)
Index
Symbols and Numbers
*p, 6
**p, 6
4-byte overwrites, and exception handling, 7172
40- and 56-bit ciphers, 133
64-bit code, and exception handling, 72
256-bit AES, 133
450 error, 166
A
Abstract Type Library (ATL), COM code and, 165
access checking, disabling, 104
access control, 1314, 107110
Access Control Entries (ACEs), 1920
Owner Rights, 160
access control lists (ACLs), 14, 17
DACLs, 32, 160
namespaces, global and local, 111
service accounts, 99102
shared memory, 112
Active Directory, owner SIDs, 160161
Active Type Library (ATL), and DEP, 127
ActiveX opt-in, 122123
locking, 131132
address space, 76
Address Space Layout Randomization (ASLR),
5153
limitations of, 53
link with /dynamicbase, 72
performance and capability implications, 5354
addresses, application, and stack randomization, 54
AdjustTokenPrivileges, 16, 102104
administrator user accounts, 1314
administrator with approval mode, 1820
applications requiring, building, 2224
elevated code, starting, 27
elevating to, 24
local system service accounts, 99
Adobe Acrobat Reader v8.0.0, 128
Adobe Flash, 121, 122
Adobe Flash Player v9.0.28.0, 128
Advanced Encryption Standard (AES), 132133
Advanced Windows (Richter), 67
Advanced Windows Firewall, 8792
AES-256 encryption key, 154
AIA (Authority Information Access) URLs, 146
algorithms
CNG, elliptic curve, 140
CNG, new, 139140
cryptographic, 9
hash, 9
hardcoded, 137, 148
Suite B, 144
allocation attack patterns, 5556
AMD CPUs, 11
Enhanced Virus Protection, 59
analysis tools, 9
/analyze, 5, 7, 9
warnings, 10
annotation, of functions, 3
anti-malware, 167168
anti-virus protection, 167168
APIs (application programming interfaces)
ASLR and, 51
AuthzReportSecurityEvent, 172
banned. See APIs, banned
CertEnroll, 148
ChangeServiceConfig2, 103
CreateService, 98
credential user interface, 163
GetProductInfo, 164
impersonation, 17
IP Helper, 76
kernel mode, 136
Network Diagnostics
Framework, 75
Network List Manager (NLM), 75, 8182
peer-to-peer collaboration, 75
pipe server attacks, 115116
secure socket extensions, 76, 8385
TBS, 177
user-mode, 136
Windows Defender, 163, 167168
APIs, banned, 3
bug prevention, 2
list of, 89
not replaced, 8
removing from codebase, 89
Application Compatibility toolset, 31
application manifest, side-by-side, creating, 31
application programming interfaces (APIs). See APIs
(application programming interfaces)
Application Verifier (AppVerif), 9
warnings, 11
applications
accessibility, 24
administrator-only, creating, 2224
compatibility of, debugging, 4244
high- vs. low-priority, 24
legacy, 28
183
184
AppPath
prompt for credentials or consent, creating, 2425
virtualization and, 28
AppPath, 109
asInvoker manifest option, 23, 24
ASLR. See Address Space Layout Randomization
(ASLR)
assertions, 156
assumption flaws, 3
ATL (Abstract Type Library), COM code and, 165
attacks
allocation, 5556
denial of service, 115117
malware, 60, 121, 154
name squatting, 110
phishing, 152, 153156
pipe server, 115116
screen-scraping, 158
shatter, 24, 110
spoofing, 158, 171
spyware, 167168
Web browser, 121122
attribute syntax, 3
auditing, 143, 172
authentication and authorization, 117, 151
authentication modules, 159
CardSpace and Information Cards, 151159
Graphical Identification and Authorization (GINA),
159
owner SID, 159161
Authenticode signatures, 44
Authority Information Access (AIA) URLs, 146
AuthzReportSecurityEvent, 172
Background Intelligent Transfer Service (BITS), 75
B
Backup Operators user group, 21
backward compatibility, 9
bank, online, phishing example, 154156
base functions, banned, 8
base process priority, changing, 107
BaseNamedObjects, 101
bcount, 5
BCrypt* functions, 136
BCryptAddContextFunctionProvider, 137138
BCryptCreateHash, 141
BCryptDeriveKey, 144
bcrypt.dll, 140
BCryptGetFipsAlgorithmMode, 143
<bcrypt.h>, 140
BCryptOpenAlgorithmProvider, 141
Bell-Lapadula integrity model, 40
benign privileges, 106107
Bennett, David, 167
Biba integrity model, 40
big-endian format, 6
binary patching, 8
BIOS options, and NX, 6162
BitLocker Drive Encryption, 176177
BITS (Background Intelligent Transfer Service), 75
blocking, 166. See also parental controls
bool, 9394
BOOL, 9394
Boolean arguments, 27, 9394
buffer overrun, 4, 8. See also buffer overrun defenses
warnings, 10
buffer overrun defenses, 4950
ASLR, 5154
DEP, 59, 62, 126129
/GS, 6467
heap defenses, 5559, 173
NX, 5964
pointer encoding, 172
safeSEH, 6772
stack randomization, 5455
buffers, 34, 7
functions and, 57
input, TPM, 180
output, TPM, 180181
source, 8
stack, 50
string, SAL annotation and, 38
bugs, 1
banned API removal and, 2
double-free, 57
privilege, 15
requestedPrivileges, Windows XP, 23
SAL annotation and, 2
static analysis, 911
Unknown SID type, Windows Vista, 34
Bugs or Defects? (Humphrey), 50
Button_SetElevationRequiredState macro, 27
Bypassing Windows Hardware-Enforced Data
Execution Prevention (Skape and
Skywing), 60
BYTE*, 6
bytes, counting, 8
C
C/C++
banned APIs, 2, 89
compiler and linkage options, 11
exception handling, 6768
heap defense functions, 57
pointers, 34, 172, 173175
SAL annotation, 2, 38
service writing in, 96
static analysis, 910
string buffers, 3
token access, 1516
ConvertStringSidToSid
TPM writing, 178180
unmanaged, 3, 11
C runtime (CRT) functions, 3
C runtime headers, 8
C runtime library, and ASLR, 52
C# programming
credential/consent prompts, 25
TPM queries, 177
C4966 warning, 8
C6387 warning, 7
calc.exe, 129
cacls tool, 101
callbacks, 117
callees (implementations), 3
callers (clients), 3
Cameron, Kim, 159
canonicalization functions, 130131
CAPICOM, 148, 157
CardSpace. See Windows CardSpace and
Information Cards
cbcount, 8
cchcount, 8
CDP (Certificate Revocation List Distribution
Point), 145
CertEnroll, 148
CertGetCertificateChain, 146147, 148
certificate common name (CN), 155
certificate events, viewing, 147
Certificate Revocation List (CRL) revocation checks,
145147
Certificate Revocation List
Distribution Point (CDP), 145
certificate verification, 145146
server, 153
SSL/TLS, 155
certificates, root, 148
certutil url tool, 147
ChangeServiceConfig2, 103
Channel9, 156
characters, counting, 8
_checkReturn, 7
checksum, 9
cipher suites, 144145
ciphers, Internet Explorer 7, 133
classes, sample code for, 6364
cl.exe, 5
clients (callers), 3
clients, authenticating, 117, 152.
See also authentication and authorization
clients, impersonating, 106
RPC/COM, 227
sockets, 117
CNG (Cryptography API: Next Generation), 135
add-ins, 137138
algorithms, new, 139140
185
crypto-agility, 137138
FIPS and, 142143
missing element, 144
use of, 140142
CoCreateInstance, 85, 91
code, third-party, 58
Code Analysis for C/C++ Warnings (MSDN), 10
code quality, 111
Code Red worm, 55
code security, 111
code signing, importance of, 44, 169
COM client/server configuration
elevation, 26
RPC, 117
COM components, COM Elevation Moniker,
starting, 2526
COM Elevation Moniker, 25
COM Elevation Moniker, The (MSDN), 26
COM interfaces
ATL and, 165
cURL and IUri interface, 131
deprecated crypto features, 148
RSS, 82
COM objects, 2526
credential providers, 159
deprecated, crypto features, 148
parental controls, 165
problems with, privilege reduction, 102103
Comer, Douglas E., 78
commands
icacls, 38
mklink, 45
Common Criteria requirements, 143
security events, 172
communication, with desktop. See desktop,
communication with compatibility, backward, 9
compilers, 45
/analyze, 5, 7, 9
and banned APIs, 8
/GS, 11. See also /GS
JIT, 127
warnings, 910
connections, port, and firewalls, 9192
connectivity, determining, 81
ConnectivityChanged, 81
ConnectNamedPipe, 113, 115
console 0, sharing of, 110
consolidated URL parser (cURL), 130131, 133
const, 5
constant source strings, 8
constants, string, 137
control handlers, and services, 9899
ControlKey, 114
controls, adding, 123
ConvertStringSidToSid, 36
186
cookies
cookies
/GS, 6467
safeSEH, 68
CreateFileMapping, 112
error (Global\\objectname), 43
CreateNamedPipe, 113
CreatePipeDacl, 102, 118
CreateProcessAsUser, 107
CreateRestrictedToken, 102103
CreateService, 98, 117118
CreateWellKnownSid, 160
Credential Manager, 44
Credential Prompt box, 18
Credential Provider model, 159
credentials, user, prompting for, 2425, 169171
credentials management, access to, 44
CredUIPromptForCredentials, deprecated, 169
CredUIPromptForWindowsCredentials,
169171
CRL (Certificate Revocation List) revocation checks,
145147
CRT (C runtime) functions, 3
CryptAcquireContext, 141
crypto-agility, 9, 136137
in CNG, 137139
cryptographic algorithms, 9, 136137
Cryptographic API 1.0 (CAPI 1.0), 135136
Cryptographic API 2.0 (CAPI 2.0), 135136
cryptographic enhancements, 135136
auditing, improved, 143
CNG algorithms, new, 139140
CNG crypto-agility, 137138
CNG and FIPS, 142143
CNG something missing, 144
CNG use, 140142
crypto-agility, 136137
deprecated features, 148
kernel mode and user mode support, 136
root certificates, 148
SSL/TLS, 144147
cryptographic interfaces, user-mode, supported,
135136
cryptography, banned, 3
list of, 9
removing from codebase, 9
Cryptography API: Next Generation (CNG). See
CNG (Cryptography API: Next Generation)
cURL (consolidated URL parser), 130131, 133
Cutler, David, 60
D
DACLs (discretionary access control lists), 32, 160
daemons, 97
dangling pointers, 55
data
encryption, 7
hashing, 141
MACing, 141
untrusted, 181
user, safe storage of, 30
Data Execution Prevention (DEP), 11, 59, 62.
See also NX (No-Execute up)
enabling, 127128
Internet Explorer 7 defenses, 126129
Davies, Joseph, 78
DCOM, 117
Debug privilege (SE_DEBUG_NAME), 16, 43
debugging
of application compatibility issues, 4244
of Internet Explorer 7 Protected Mode, 126
kernel mode, 163, 175
process, 105
of services, 101
declspec syntax, 3
DecodePointer, 174
defects, software, 4950
denial of service attacks
pipe server, 115116
sockets, 117
deny SIDs, 1920
DEP. See Data Execution Prevention (DEP)
desktop, communication with, 110112
named pipes, 112116
RPC/COM, 117
shared memory, 112
simple message boxes, 112
sockets, 116117
desktop, private, CardSpace, 158
device drivers, loading and unloading, 105
devices, permanent, enabling, 104105
digital signatures, 9, 136
Information Card, 151, 156
directories
allowing low-integrity process writing, 37
integrity level of, setting, 38
LDAP, synchronizing, 105
Program File, access to, 13, 28, 29
user-writable, 42
Windows, access to, 13
disabled warnings, 1011
DisconnectNamedPipe, 115
discretionary access control lists (DACLs),
32, 160
DLLs (dynamic link libraries), 72
ASLR and, 5154
firewall rules and, 9091
GINA, 159
NX bypassing and, 60
document signing, 9
Authenticode, 44
GetFirewallSettings
domain administration, enabling, 105
domain service accounts, 99
domains, adding computers to, 107
double-free conditions, 5556
downloads, blocking, 166. See also parental controls
_dref_out_bcount(n) macro, 6
drive-by downloads, 121
drop-offs, reducing, 152
DSA, and RNGs, 140
DWORDs, 64, 68, 71
dwServiceSidType, 100
dynamic link libraries (DLLs). See DLLs (dynamic
link libraries)
/DynamicBase linkage, 3, 11
ALSR, 72
browser defenses, 133
E
EBP register, and /GS flag, 64
echo servers, 9192
ecount, 5
eEye, 55
elevated code, starting, 27
elevated tokens, 20
elevation
to administrator, 1820
COM client/server configuration, 26
monikers, 2526
of processes, determining, 2022
prompts, disappearing, 2324
of rouge software, 26
shield icon, 27
encoded pointers, 55
encoding, pointer, 172175
encrypting credentials, 169171
encrypting data, 141
ERROR_ELEVATION_REQUIRED (740) error, 44
errors. See also warnings
450 error, 166
Blocked by Parental Controls, 166
CreateFileMapping (Global\\objectname), 43
ERROR_ELEVATION_REQUIRED (740), 44
OpenSCManager (,SC_MANAGER_ALL_
ACCESS), 43
Event Viewer
certification events, 147
virtualization events, 30
event log, security, 163, 171172
events, logging, 167
_except keyword, 67
exception handlers, 6772
exception handling pointers, 71
Execution Disable (XD), 11
ExitProcess, 61
Explorer, and virtualized processes, finding, 29
187
F
Federal Information Processing Standard (FIPS),
142143
file downloads, blocking, 166. See also parental
controls
file overwrite privilege, 105
file registry, access to, 13
file system, access to, 13
file warnings, 42
files, integrity level of, setting, 38
FillString function, 4, 7
filtered tokens, 20
filtering, disabling, 166167
_finally keyword, 6768
FIPS (Federal Information Processing Standard),
142143
firewalls
configurable properties, 8687
creating rules, 8792
global settings, 8587
groups and interface properties, setting, 9091
network access control, 107110
rule groups, working with, 9394
Teredo, 7880
fixed pointers, 55
Flash, Adobe, 121, 122
FlushFileBuffers, 116
full (full-privilege) tokens, 20, 34
full-cone NAT, 7980
function pointers, 54
randomization of, 58
functions. See also specific functions
annotation of, 3
banned, 8
BCrypt*, 136
C/C++ annotation, example, 45
canonicalization, 130131
cryptographic add-ins, 137138
hash, 9, 136
heap defense, 57
Information Card, browser support detection, 158
Internet Explorer 7 Protected Mode, 124125
NCrypt*, 136
SHA-1, 136
TBS, 180
URL parsing, deprecated, 131
WinMain, and heap defenses, 5859
fuzzing, 50
FXCop analysis tool, 9
G
gadgets, 181
GetConnectivity, 81
GetCurrentDirectory, 118
GetFirewallSettings, 86
188
get_IsRuleGroupCurrentlyEnabled
get_IsRuleGroupCurrentlyEnabled, 94
GetLastError, 102
get_LocalPolicy, 108
GetModuleBaseName, 118
GetModuleFileNameEx, 117118
GetNamedPipeClientComputerName, 115
GetNamedPipeClientProcessId, 115116
GetNamedPipeClientSessionId, 116
GetNetworkConnections, 81
GetNetworks, 81
GetPreferredHmacAlg, 141
get_Rules, 108, 109
GetTokenInformation function, 20
GetUserSettings, 165
GINA (Graphical Identification and Authorization),
159
global namespaces, 11011
global objects
creating, and local service accounts, 99
namespace, 106
warnings, 43
Graphical Identification and Authorization
(GINA), 159
/GS, 3, 11
browser defenses, 133
buffer overrun protection, 6467
flaws in, 6772
H
hardcoded algorithms, 137, 148
hash-based message authentication codes
(HMACs), 136
hash functions, 9, 136
hashing data, 141
headers, 8
heap
countermeasures, 58
defenses, 5559, 72
overrun, 57, 173
randomization, 51
heap spraying, 55
HeapTerminateOnCorrupt, 72
high-integrity objects
determining, 3940
labeling, 3739
high-integrity processes, 3234, 37, 4142
high-level privileges, 104106
highestAvailable manifest option, 23
HKCU registry, 43
HKLM registry, 13, 43
HMAC (hash-based message authentication
codes), 136
How Windows Defender Identifies Spyware, 168
Howard, Michael, 1, 49, 83, 130, 181
HTML, 121, 123
I
IBM system, and GINA, 159
icacls command/tool, 38, 41, 101
Ichiro persona, 17
ICredentialProvider interface, 159
IDA overflow advisory, 55
Identity Provider, 152, 153
Identity Selector, 156, 157
IEGetWritableFolderPath, 124
IEGetWriteableHKCU, 124
IEIsProtectedModeProcess, 124
IEIsProtectedModeURL, 125
IELaunchURL, 125
IEnumVARIANT, 109
iepmapi.lib, 124
IESaveFile, 124
IEShowSaveFileDialog, 124, 125
IEUser.exe, 125
IExplore.exe, 125
IFeedsManager, 82
IInternetSecurityManager, 131
IInternetSecurityManagerEx, 131
IInternetSecurityManagerEx2, 131
image files, 121
image randomization, 51
IMG tags, 132
ImpersonateNamedPipeClient, 17, 106, 112
implementations (callees), 3
_in macro, 5
inappropriate content, restricting. See parental
controls
#include sal.h, 7
Independent Software Vendors (ISVs), 159, 167
INetFwPolicy2, 108
INetFwRules, 109
INetFwServiceRegistration, 108
INetwork::GetCategory, 82
INetwork::GetDomainType, 81
INetworkListManager, 81
INetworkListManagerEvents, 81
Information Cards, 151153. See also Windows
CardSpace and Information Cards
browser support, detecting, 158
communicating parties, 152
creation of, 156
display Information Card use, 154
local namespaces
PIN protection, 154
programmatic access to, 156157
user-issued, 152153, 157
INI warnings, 43
IniFileMapping registry settings, 43
_in_opt macro, 5
_inout macro, 5
_inout_bcount_full(n) macro, 6
_inout_bcount_part(n,m) macro, 6
input buffers, TPM, 180
Integer Handling with the C++ SafeInt Class
(LeBlanc), 55
integer manipulation, computer capability, 55
integer overflow warnings, 910
integrity levels, 1314, 3237
custom, 34
integrity SIDs, 34
low-integrity processes, write locations, 37
object integrity levels, determining, 3940
object integrity levels, setting, 3739
rules for, 40
setting, 45
integrity masks, 40
integrity models, 40
defensive, 4142
Intel Execute Disable (XD-bit), 59
intellectual property rights, 151
Internet connections, determining, 81
Internet Explorer 7. See also Internet Explorer 7
defenses
changes to, 132133
integrity levels, 32
phishing detection, 154
Protected Mode, 40, 4142, 124126
third-party code, 58
Internet Explorer 7 defenses, 121122
ActiveX control, locking, 131132
ActiveX Opt-In, 122123
AES, 132133
clipboard access disabled, 132
cURL and IUri interface, 130131
Data Execution Prevention (DEP), 126129
Protected Mode, 124126
script URLs, 132
window origin, 133
Internet Protocol (IP), 76. See also IPv4; IPv6
Internetworking with TCP/IP Vol. I (Comer), 78
interprocess communication
protocol (IPC), and integrity levels, 41
I/O completion ports, 98, 113
IP helper, 76
IPsec, 77
IPv4, 7677
Teredo, 7880
IPv6, 7678
Teredo, 7880
J
Java applets, 121, 127
JavaScript, 121, 132
gadgets, 181
Information Card, 158
Jet Propulsion Laboratory research, 50
Johnson, Ken (aka Skywing), 60
just-in-time (JIT) compilation, 127
K
KDFs. See key derivation features (KDFs)
Kerberos tickets, 106
kernel mode, 136
debugging, 163, 175
key derivation features (KDFs), 136
password-based, and CNG, 144
key operations, 143
keys, cryptographic, 136
PIN protection, 154
keywords, safeSEH, 6768
kill-bitting, 123
kloc, 4950
Known list, Windows Defender, 169
L
laptops, undocking, 107
LDAP remote file access, 145
LeBlanc, David, 49, 55, 83, 130, 181
legacy applications, and virtualization, 28
LFH (low fragmentation heap),
enabling, 59
Limited User Account (LUA), 14
linked tokens, 20, 21
linkers. See also individual linkers
/DynamicBase, 3, 11
/NXCompat, 3, 11, 129
/SafeSEH, 3, 11
links, forward and backward, checking, 57
Linux
services, 97
symlink security bugs, 45
Lipner, Steve, 1
Litchfield, David, 67, 68, 71
little-endian format, 6
LoadLibrary, 51, 52
local namespaces, 110111
189
190
M
Mac OS X, and symlink security bugs, 45
MACing data, 141
macros, 4, 57
shield icon, 27
malloc(), 7
malloc.h, 7
malware
anti-malware, 163, 167168
CardSpace, 154
NX, 60
Web browser attacks, 121
manifest file, 22
requestedExecutionLevel options, 23
manifest tool (mt.exe), 22
Marcelais, Mike, 55
Margosis, Aaron, 43
masks, integrity, 40
MD4/MD5 hash algorithms, 9, 136
m_data pointer, 173
m_dest pointer, 173
medium-integrity objects
determining, 3940
labeling, 3739
medium-integrity processes, 3234
memory, shared, 112
memory quotas, adjusting, 107
merchants, online, and Information Card, 152
message boxes, simple, 112
metadata randomization, blocking, 57
Metasploit project, 60
Microsoft Application Compatibility Toolkit 5.0,
debugging
Protected Mode, 126
Microsoft Kernel Mode Cryptographic Module, 136
N
name squatting attacks, 110
named pipes, 112116
namespace
collisions, 110
Information Card, 157
local vs. global, 11011
warnings, 43
NAT (network address translation), 7677
Teredo, 7880
National Institute of Standards
and Technology (NIST), 136, 142
National Security Agency (NSA), 143
NCrypt* functions, 136
.NET Framework 1.1, and Protected Mode, 126
.NET Framework 2.0, and Protected Mode, 126
.NET Framework 3.0, and
Information Cards, 151, 156157
.NET Framework Cryptography, 135136
.NET language, and credential/consent prompts, 25
Netcraft Toolbar, phishing defenses and, 154
NetFwMgr, 108
NetFwPolicy2, 85, 93
NetFwRule, 88, 91
network access, controlling, 107110
network address translation (NAT). See NAT
(network address translation)
network connections, determining, 81
Network Diagnostics Framework, 75
Network List Manager (NLM), 75, 8182
network service accounts, 99, 100
networking defenses, 7576
IPv6, 7678
Network List Manager (NLM), 8182
RSS platform, 8283
Teredo, 7880
Windows firewall, advanced security, 8594
Winsock Secure Socket
Extensions, 8385
O
object ownership, assuming, 106
objects
global namespace, creating, 106
integrity levels, determining, 3940
integrity levels, setting, 3739
local vs. global namespaces, 11011
warnings, 43
Office 2003, and namespaces, 111
Office 2007
ASLR, 54
namespaces, global and local, 111
third-party code, 58
Office Trustworthy Computing, 55
online bank, phishing example, 154156
Online Certificate Status Protocol (OCSP), 145147
Open Specification Policy, Microsoft, 151, 156
OpenBSD, and NX, 60
OpenProcess, 16
OpenSCManager (,SC_ MANAGER_ALL_ACCESS)
error, 43
operating system, process acting as, 106
ordinary user accounts, 1314
Organizational Unit (OU), owner SIDs, 160161
_out macro, 5
191
_out_ecount(n), 4
Outlook 2007, 81
output buffers, TPM, 180181
Owner Rights ACE, 160
owner SIDs, authentication/authorization and,
159161
P
page files, creating, 106
Paget, Chris, 110
parental controls, 163, 164
block file downloads, enabling, 166
code for, 165
error 450, 166
filtering, turning off, 166167
logging events, 167
time limits, 165166
parsing, URL, 130131
password-based key derivation, 144, 154
password management, and local user accounts, 99
password security, and Information Card, 152
password theft, 153154
patents, 151
PCT (Private Communication Technology), 132133
peer-to-peer functionality, 75
phishing attacks, 152
CardSpace and, 153156
PID (process identity), 15
PIN protection, and Information Cards, 154
Pincus, Jon, 49
pipe server attacks, 115116
pipes
handling code, sample, 114115
integrity levels and, 41
named, 112116
pointers
C/C++, 34, 172, 173175
dangling, 55
encoded/encoding, 54, 163, 172175
exception handling, 71
function, 54, 58
long-lived, 163, 172
m_data, 173
m_dest, 173
null, 5657, 174175
policy setting, cipher suites, 144145
port connections, and firewalls, 9192
port-restricted cone NATS, 80
PostMessage, 24
PostQueuedCompletionStatus, 114
Power Users, and access, 1314
PowerShell, and credentials/
consent prompts, 25
PREfast analysis tool, 9
192
Q
quality gates, 23
QueryInterface, 108, 109
Quicktime, 121
quota, 161
R
RACF (Resource Access Control Facility), 159
RAM, locking, 107
random number generators (RNGs), 136, 140
random numbers, generating, 142
randomization
ASLR, 51
heap base, 58
metadata, blocking, 57
stack, 5455
ReadFile, 113
Really Simple Syndication (RSS) platform, 76, 8283
RealPlayer, 121
RegisterServiceCtrlHandlerEx, 98
registry warnings, 43
Relying party, Information Card, 152
Relying Party Certificate thumbprint not specified
warning, 157
remote procedure calls, 117
S
SACL (system audit access check), enabling, 105
/SafeSEH, 3, 11
browser defenses, 133
buffer overrun protection, 6772
SAL annotation, 3
buffers, 34
bug prevention, 2
C/C++ string buffers, 2, 38
example of, 45
in existing code, 78
macros, 57
syntaxes, 3
Salazar, Abby, persona, 17
Salazar, Toby, persona, 17
sal.h header file, 8
salts, and PIN protection, 154
Saltzer, Jerome H., 110
SAML (Security Assertion Markup Language), 156
SCdEnrl, 148
Schroeder, Michael D., 119
screen-scraper attacks, 158
script URLs, Internet Explorer 7, 132
scripting languages, 121
SDDL. See Security Descriptor Definition Language
(SDDL)
secure socket extensions, 8385
Secure Sockets Layer and Transport Layer Security
(SSL/TLS). See SSL/TLS (Secure Sockets Layer
and Transport Security Layer)
Security Assertion Markup
Language (SAML), 156
193
194
T
tags, IMG, 132
TakeOwnership, owner SIDs, 161
Task Manager, and virtualized files
determination, 29
TBS (TPM Base Services), 177
Tbsip_Submit_Command, 180
TCG Software Stack (TSS), 177178
Teredo, 76, 7880
terminate on corruption code, and heap defenses,
5859
third-party code, 58
thread tokens, 17
threat modeling, 1
time limits, setting, 165166
time of day, system, modifying, 106
time zone, system, setting, 45
TLS, 132133
token warnings, 43
TokenElevationTypeDefault, 21
TokenElevationTypeFull, 21
TokenElevationTypeLimited, 22
tokens. See user tokens
TokenUIAccess, 24
TPM (Trusted Platform Module), 163, 175177
low-level access to, 177181
TPM Base Services (TBS), 177
Trusted Computing Group, 175, 176
U
UAC (User Account Control). See User Account
Control (UAC)
UAC-friendly user data storage, 30
UAP (User Account Protection), 14
UDP packets, Teredo and, 7880
uiAccess manifest element, 23
UIPI (User Interface Privilege Isolation), 24
Understanding IPv6 (Davies), 78
Unicode, 6
Universal Plug and Play (UPnP), 86
Unix
access control, 160
root account, 99
services, 97
symlink security bugs, 45
Unknown SID type bug,
Windows Vista, 34
unprivileged users, 14
untrusted data, 181
UPnP (Universal Plug and Play), 86
URIs, 130131
URL filtering, disabling, 166167
URLExemptionList, 167
U.S. Government Protection Profile for Single-Level
Operating Systems in Environments Requiring
Medium Robustness v1.67, 143
User Account Control (UAC), 1314, 1415, 40
administrator application runs, requiring, 2224
administrator elevation, 18
administrator with approval mode, 1820
COM components with COM elevation moniker,
starting, 2526
credentials or consent, application prompt for,
2425
disabling, advising against, 45
elevated managed code applications, starting, 27
options, checking, 44
process elevation, determining, 2022
token format, updated, 20
User Interface Privilege Isolation (UIPI), 24
user tokens, 1517
User Account Protection (UAP), 14
user credentials, prompting for, 2425, 169171
user data, safe storage of, 30
user interface considerations, 2728
V
VARIANT_BOOL, 9394
VBScript, and TPM, 177
verification, certificate. See certificate verification
VeriSign, 152
video formats, 121
Viega, John, 181
virtualization, 2831, 42
disabling, 3132
as temporary fix, 45
viewing, Event Viewer, 30
virtualized processes, finding, 2930
VirtualProtect function, 60, 62
browser defenses, 129, 133
Vista International, 132
Visual Studio 7.0, /GS flag, 64
Visual Studio 2005
administrator-only applications, 22
ASLR, 52
code quality, 3, 4, 7, 8, 9, 11
exception handling, 68, 71
/NXCOMPAT, 129
Visual Studio 2005 Team System, 5
Vittorio Bertocci: WS-TrustUnder the Hood, 156
volume management, enabling, 105
VulnerableFunc function, 6567
W
/W4. See warnings, warning level 4
warnings, 4, 7. See also errors
6204, 10
/analyze, 10
Application Verifier, 11
buffer overrun, 9
C4996, 8
C6387, 7
compiler, 910
disabled, 1011
file, 42
FXCop, 11
195
196
peer-to-peer functionality, 75
pointer encoding, 172
RequestedPrivileges bug, 23
root certificates, 148
SIDs, 160
user accounts, 1314, 17, 28, 42
Windows Firewall, 87
WinHTTPExemptionList, 167
Winlogon, privileges and, 44
WinURLExemptionList, 167
WinVerifyTrust, 148
WlxLoggedOutSAS, 159
WMI interfaces, and TPM, 177, 181
WMI objects, and filtering, 166167
working set, process, increasing, 45
worm, Code Red, 55
WpcuSidStringForCurrentUser, 165
WpcuSidStringFromUserName, 165
write down, not write-up, 32
WriteData, 173
WriteFile, 113, 115
WriteProfileXXX, 43
Writing Secure Code (Howard and LeBlanc),
49, 83, 92, 117
WS-* protocols, 151
WSAImpersonateSocketPeer, 84
WSARevertImpersonation, 84
WSASetSocketPeerTargetName, 84
WSASetSocketSecurity, 8385
WTSEnumerateSessions, 112
WTSSendMessage, 112
X
X.509 certificates, 136
XEnroll, 148
XML documents, digitally signed, 151, 156
Michael Howard
Michael Howard is a senior security program manager in
the Security Engineering team at Microsoft, and an architect of the security-related process improvements at the
company. He is the co-author of many security books,
mostly with David LeBlanc including the award-winning
Writing Secure Code, 19 Deadly Sins of Software Security,
and the Security Development Lifecycle. He is an editor of
IEEE Security and Privacy, and the series editor of the
Microsoft Press Secure Software Development series.
David LeBlanc
David LeBlanc is a senior developer in the Microsoft
Office Division's Trustworthy Computing team. In
addition to writing code, he helps advise Office on
security issues and how to implement the SDL. He has
co-authored several security books, mostly with Michael
Howard. David has worked in many aspects of the
security industry, ranging from anti-fraud for telephony
companies, leading the development team for an awardwinning network security assessment tool, penetration
testing for Microsoft's network security group, and was a
founding member of the Trustworthy Computing
Initiative team. When not writing books, he'll be found
somewhere in the Cascades on his horse.