Intro – Secure Process Creation
I chose the Secure Process Creation pattern as the first pattern to kick of the series on security design patterns because process creation is everywhere in the software world today. Ensuring that the way processes are created is a secure, and an attacker cannot manipulate them, is the bedrock of security, after ensuring that an attacker cannot, directly or indirectly manipulate your process, then you’ve only got to worry about about the data that your process uses and consumes and ensure that, that is secure.
Insecure processes have led to operating systems becoming hostage to an attacker, data theft, ddos attacks, manipulation of the operating system to lead to other vulnerabilities. There are so many things that can go wrong when a process is fundamentally insecure.
What is a Process?
I think this is a tough question to answer, and one where you could ask a million developers and get a million different answers an what exactly a computing process is. I am certainly not about to help clear up the ambiguity I would argue that a process is any piece of software that a computer system runs either through a process itself (a process creating a process), scheduled, action, program action, windows or linux service, any time a piece of code written is executed, that meets the measure of what a process is, that execution of code, is a process, the process starts when the execution starts, and the process ends when the execution ends. So really I am not telling any new developer this, there are literally thousands of processes running on our devices at any given time, with our action or without, ensuring that these processes are created using a secure process creation mechanism is vital to ensuring the safety and security of our devices.
Secure Process Creation
Disclaimer: The secure process creation pattern is a pattern for helping the developer create a process in a secure mechanism, however like most security patterns, it still does come down to the decisions and choices the developer makes. If bad decisions are made with the secure process creation pattern, well then, it’s still going to be insecure
If you remember Viegra & McGraw’s list about what goes into a security pattern or even if you don’t, here’s the list again.
- Secure weakest link
- Practice defense in depth
- Fail securely
- **Principle of least privilege **
- Compartmentalize
- Keep it simple
- Promote privacy
- Hiding secrets is hard
- *** Be reluctant to trust ***
- Use community resources
This list might look slightly different then in my last post. The reason being, is I’ve highlighted the components that the secure process creation pattern attempts to fulfill. Again I mention attempts because as we’re going to see in a second one of the things it depends greatly on is the choices the developer/engineer chooses to make when writing their software.
The Pattern
I like this pattern because it’s pretty simple. It includes 3 main parts, I am sorry to you more visual folks, I’ll update with, a graphical representation over the weekend. However, the first part of the pattern is the Process Creator – The code making the process, The second portion of the pattern is Process itself and the third and most important is the permission set Essentially the pattern in it’s simplistic manner the process creator references a set of permissions, which could originate anywhere, file, registry, hard coded, database, web etc. Then creates a process based on those permissions. That’s it, pretty simple, you’d think well doesn’t everyone do that? – if that’s the question you’re asking yourself the answer is more complicated, yes some do, no some don’t, and maybe but some do it wrong. When I mentioned it’s still possible to create processes in an insecure manner using this pattern, it depends on the permissions you define, where & how they’re stored, whether they’re editable or not, and how they’re applied.
The Code
Lets consider how I chose to implement this pattern in the code.
My process creation code looks like this.
<br />
1. cap_user_header_t hdr;<br />
2. cap_user_data_t data;<br />
3. data->effective &= CAP_TO_MASK(CAP_NET_RAW);<br />
4. data->permitted &= CAP_TO_MASK(CAP_NET_RAW);<br />
5. data->inheritable = 0;<br />
6. pid_t pid = fork(); </p>
<p> 7. if (pid == 0)<br />
8. {<br />
9. std::auto_ptr<Process> pWorker(new WorkerProcess);<br />
10. childProc = pWorker->getProcId();<br />
11. hdr->pid =childProc;<br />
12. if (capset(hdr, data) < 0)<br />
13. {<br />
14. std::cout << "Error Setting capabilities" << std::endl;<br />
15. }<br />
16. pWorker->runProcess();<br />
17. }<br />
The very first thing that should be pointed out, is that I am using Linux capabilities, actually the first thing that should be pointed out is that auto_ptr is deprecated in C++11. I am in the process of upgrading. However Linux capabilities are not available to Windows developers/engineers, and you can read all about the process creation mechanism for windows
here is a good starting point.
Lines 3- 5 basically define, in a hard coded, manner the set of the new permissions my process, is going to get when it’s created. Granted in a generic production code these would be defined somewhere securely and if this was a production process creator, it would read the permission for each and every process, based on a configuration most probably. Lines 6 – 10 actually create my new process for me, just like in Windows in Linux every process has an id, therefore on lines 11-12 I am actually asking the process for the id, that it’s been created with, and then I am setting the capabilities of what this process is allowed to do as it runs. In this case the process is not allowed to do very much, except connect to some lower TCP/IP sockets the internet uses, which could still be vulnerable, but works for this example.
Lets now consider the guts of the worker process.
<br />
void WorkerProcess::runProcess()<br />
{<br />
1. if (setuid(3) < 0)<br />
2. {<br />
3. std::cout << "Failed to set uid in worker Proc" << std::endl;<br />
4. perror("Setting UID: ");<br />
5. }<br />
6. else<br />
7. {<br />
8. std::cout<<" Set the uid to: " << getuid() << std::endl;<br />
9. }<br />
10. std::string uInpt;<br />
11. std::cout << "Please Enter some input" << std::endl;<br />
12. std::getline(std::cin, uInpt);<br />
13. while (uInpt.compare("quit") != 0)<br />
14. {<br />
15. std::cout << "ECHO: " << uInpt << std::endl;<br />
16. std::getline(std::cin, uInpt);<br />
17. }<br />
18.}<br />
The worker process is actually pretty stupid, it just echo's the user input back to the user, which again is okay for this example, it could actually be doing a whole lot more. However what I'd like to draw your attention to is if you consider lines 1 - 9. The very first thing that this worker process tries to do is change the user, and therefore the user permissions that this code is running as. Typically a program could attempt to set the user id to be the root user, therefore gaining root privileges. It's also conceivable, that if the code the developer/engineer, writes directly doesn't attempt to do this, perhaps a library, third party or otherwise that the application links to, could attempt to change the program's privilege level and therefore gain access to things it's not allowed to do. There is also the fall back buffer over run to the program, if an attacker where to try to exploit that with this particular case, again the program would not allow the attacker to implement a buffer over run attack to change the privilege level of this application.
In this case I have combined the process creator with the rules, to create a secure process, as I've mentioned there are many other ways. The only code left to consider is what a process actually looks like.
<br />
class Process<br />
{<br />
public:<br />
Process();<br />
~Process(){;}<br />
pid_t getProcId();<br />
virtual void runProcess() = 0;<br />
};</p>
<p>Process::Process()<br />
{</p>
<p>}</p>
<p>pid_t Process::getProcId()<br />
{<br />
return getpid();<br />
}<br />
Given the Worker process, even if the worker process attempts to call set capabilities itself and change it's own capabilities, ie give itself the capability to set the user id it's running as the code would fail, unless its given that capability back by a higher functioning process, ie a root level process.
Linux capabilities are one way to implement this pattern, and the way that I chose, there are other ways to accomplish the same thing, however I felt this was the cleanest, easiest and most direct, example of secure process creation. I have also provided a complete code listing in windows zip format.