There is a reason why almost any major Firewall that uses outbound filtering also pairs with a HIPS component. It is trivial to bypass an outbound firewall without it. Why, you ask? Because Windows does not separate programs that are all running under a single user account – if you’ve got Pidgin open, and you’ve got Firefox open, they may run in a separate address space but they are not isolated from each other.
You need the ability to call exactly two calls from kernel32 in order to bypass a plain, unassisted outbound firewall – CreateRemoteThread() and LoadLibrary().
CreateRemoteThread() is a lot like it sounds, you call it, you pass it a handle, security descriptor (lol if you’re pre-SP2 XP, this’ll get you!), stack size, blah blah blah, the address to load it, and the code you want it to execute. Yes, you read that right! You can have your code executed by another process with a single call. Process A calls CreateRemoteThread() and it can create that thread in Process B.
In this case we would be pointing to the LoadLibrary() function, and pass that function the path too our .dll file.
At this point the .dll file, which holds our code, is loaded into whichever process we like (as long as we share a UID) and we control that process.
This is why a HIPS component will be like “Hey, they’re trying to load a file into this other process” or “Hey, they’re trying to create a thread in this other process” and stop it. Without that specific check the outbound firewall is useless.
Now, of course, even with that check I’d be wary of an outbound firewall. And of course the user has to actually answer those popups correctly… but you see that at least with a HIPS component it’s not bypassable in 10 lines of code.