Using Architecture to Avoid Dumb Mistakes
The security report against SunnComm’s MediaMax that I document in my previous blog post seems so amazingly simple: world-writable file permissions. Replace a file and wait for it to be run by an administrative user, then you have total control. Attacks don’t get much more basic, or much more obvious, than that. SunnComm’s example, however, is one of many that illustrate the fact that access control is poorly-understood by developers. Major software firms (including, on occasion, Microsoft itself) have misconfigured access control so badly as to make their products practically give away elevated privileges. Something about this picture has got to change.
SunnComm’s mistake may seem obvious, and it certainly does demonstrate a lack of understanding of multi-user security, but their error is strikingly common:
C:\Program Files\SomeDeveloper\SomeApp>cacls someapp.exe
C:\Program Files\SomeDeveloper\SomeApp\SomeApp.exe Everyone:F
I’ve obviously edited this output, but I’ve done so to avoid naming a widely-deployed software application as having a Full Control permission for the Everyone group on all of its files. This blatant disregard for user security may seem pointless, and indeed it is wholly irresponsible. However, it is often justified by concerns such as allowing less-privileged users to install software updates. The problem is, most companies just don’t get that software update deployment is an administrative task for a reason. Namely, that it is simply not possible to trust a user to deploy an update that won’t damage the system.
There are several tasks that applications and system components simply should not trust limited, untrusted users to do. Hence the purpose of the term “untrusted user”. Access control is one of many areas where some actions are such huge, clear-cut mistakes that even attempting them should immediately subject an application to question. Placing ACLs that allow all users to modify objects (essentially unsecuring a secured object) is one of them. I’ve seen many applications try to allow Everyone, world, or equivalent roles modification rights to an object, and I’ve never seen one of them do it securely. Not even Microsoft could get that right, and had to release MS02-064 to advise users to plug a hole in Windows 2000 that was caused by an access control error of its own.
In spite of common knowledge that some security-related actions are potentially dangerous and almost never desired, it is still shockingly easy for an application developer to expose himself/herself to an attack. Windows Vista and Longhorn Server contain API “improvements”, presumably modeled after the simplicity of the access control APIs for Unix-like systems, that will make stripping your system’s security to the bone even easier.
So where are the countermeasures? Though there’s no way to make a system idiot-proof, shouldn’t systems developers work to make being an idiot a little less simple? OpenBSD is notorious for this approach of “secure by default” that ships the user a hardened system. Forcing users to open holes, rather than close them, encourages understanding of the risks involved as well.
A similar approach should be taken to application-level errors. It’s entirely sensible for an Operating System to block obviously dangerous activity. FormatGuard for Immunix is one example of this. That project was amazingly successful — blocking almost 100% of format string attacks with a near-zero false positive rate. Why? Because it blocked the exploitation of an obvious programming error.
This preemptive defense model has a lot of promise, and could just as easily be applied to other security cases. Suspicious behavior like setting world-writable ACLs on critical objects should raise immediate alarms, and systems developers could do quite a bit to facilitate this. Imagine being a developer of an application like MediaMax, when systems begin to trigger on the insecure ACL behavior and display warnings such as:
WARNING: This application is attempting a potentially-dangerous action. Allowing it to continue may expose your system to compromise by malicious users, viruses, or other threats. Are you sure you want to allow this application to continue?
Now, there will be folks that answer ‘Yes’, but the concern prompted by such a warning on the part of others would more than likely force a redesign on the part of SunnComm and vendors like them. In the ideal case, such a warning would expose the vulnerability before the software ever left the lab, rather than months later, when 20 million CDs with insecure code on them have been sold worldwide.
For something like this to be possible, the old notion of application and system as distinct components will have to be abandoned, in favor of a development concept that recognizes the reality that application and system code are dependent upon one another for functionality, including security. Applications should be architected to avoid potential system holes, and systems should be designed with the goal of making it more difficult to create holes in applications.
Unfortunately, both simplicity and complexity contribute, at least in excess, to a lack of security at other levels. Sometimes, a stop-gap measure is necessary to prevent slightly clueless folk from becoming a major risk.